]> git.sesse.net Git - kdenlive/blobdiff - src/projectsettings.cpp
Integrate with the required MLT hooks for getting Movit to work.
[kdenlive] / src / projectsettings.cpp
index bed29b1e9fe4817ffffe9f3593a4144f71ddb9ee..848a0718c87c92f1d7fa6b12b89404e4f2d2fb0b 100644 (file)
@@ -21,7 +21,7 @@
 #include "kdenlivesettings.h"
 #include "profilesdialog.h"
 #include "docclipbase.h"
-#include "titlewidget.h"
+#include "widgets/titlewidget.h"
 #include "effectslist.h"
 
 #include <KStandardDirs>
 #include <KDebug>
 #include <kio/directorysizejob.h>
 #include <KIO/NetAccess>
+#include <KTemporaryFile>
+#include <KFileDialog>
 
 #include <QDir>
 #include <kmessagebox.h>
 
-ProjectSettings::ProjectSettings(ProjectList *projectlist, QStringList lumas, int videotracks, int audiotracks, const QString projectPath, bool readOnlyTracks, bool savedProject, QWidget * parent) :
+ProjectSettings::ProjectSettings(ProjectList *projectlist, QMap <QString, QString> metadata, const QStringList &lumas, int videotracks, int audiotracks, const QString &projectPath, bool readOnlyTracks, bool savedProject, QWidget * parent) :
     QDialog(parent), m_savedProject(savedProject), m_projectList(projectlist), m_lumas(lumas)
 {
     setupUi(this);
 
-    list_search->setListWidget(files_list);
+    list_search->setTreeWidget(files_list);
 
     QMap <QString, QString> profilesInfo = ProfilesDialog::getProfilesInfo();
     QMapIterator<QString, QString> i(profilesInfo);
@@ -50,7 +52,7 @@ ProjectSettings::ProjectSettings(ProjectList *projectlist, QStringList lumas, in
     project_folder->setUrl(KUrl(projectPath));
     QString currentProf = KdenliveSettings::current_profile();
 
-    for (int i = 0; i < profiles_list->count(); i++) {
+    for (int i = 0; i < profiles_list->count(); ++i) {
         if (profiles_list->itemData(i).toString() == currentProf) {
             profiles_list->setCurrentIndex(i);
             break;
@@ -63,35 +65,155 @@ ProjectSettings::ProjectSettings(ProjectList *projectlist, QStringList lumas, in
     video_thumbs->setChecked(KdenliveSettings::videothumbnails());
     audio_tracks->setValue(audiotracks);
     video_tracks->setValue(videotracks);
+    connect(generate_proxy, SIGNAL(toggled(bool)), proxy_minsize, SLOT(setEnabled(bool)));
+    connect(generate_imageproxy, SIGNAL(toggled(bool)), proxy_imageminsize, SLOT(setEnabled(bool)));
+    QString proxyparameters;
+    QString proxyextension;
+    if (projectlist) {
+        enable_proxy->setChecked(projectlist->getDocumentProperty("enableproxy").toInt());
+        generate_proxy->setChecked(projectlist->getDocumentProperty("generateproxy").toInt());
+        proxy_minsize->setValue(projectlist->getDocumentProperty("proxyminsize").toInt());
+        proxyparameters = projectlist->getDocumentProperty("proxyparams");
+        generate_imageproxy->setChecked(projectlist->getDocumentProperty("generateimageproxy").toInt());
+        proxy_imageminsize->setValue(projectlist->getDocumentProperty("proxyimageminsize").toInt());
+        proxyextension = projectlist->getDocumentProperty("proxyextension");
+    }
+    else {
+        enable_proxy->setChecked(KdenliveSettings::enableproxy());
+        generate_proxy->setChecked(KdenliveSettings::generateproxy());
+        proxy_minsize->setValue(KdenliveSettings::proxyminsize());
+        proxyparameters = KdenliveSettings::proxyparams();
+        generate_imageproxy->setChecked(KdenliveSettings::generateimageproxy());
+        proxy_imageminsize->setValue(KdenliveSettings::proxyimageminsize());
+        proxyextension = KdenliveSettings::proxyextension();
+      
+    }
 
+    proxy_minsize->setEnabled(generate_proxy->isChecked());
+    proxy_imageminsize->setEnabled(generate_imageproxy->isChecked());
+
+
+    // load proxy profiles
+    KConfig conf("encodingprofiles.rc", KConfig::CascadeConfig, "appdata");
+    KConfigGroup group(&conf, "proxy");
+    QMap <QString, QString> values = group.entryMap();
+    QMapIterator<QString, QString> k(values);
+    int ix = -1;
+    while (k.hasNext()) {
+        k.next();
+        if (!k.key().isEmpty()) {
+            QString params = k.value().section(';', 0, 0);
+            QString extension = k.value().section(';', 1, 1);
+            if (ix == -1 && ((params == proxyparameters && extension == proxyextension) || (proxyparameters.isEmpty() || proxyextension.isEmpty()))) {
+                // this is the current profile
+                ix = proxy_profile->count();
+            }
+            proxy_profile->addItem(k.key(), k.value());
+        }
+    }
+    if (ix == -1) {
+        // Current project proxy settings not found
+        ix = proxy_profile->count();
+        proxy_profile->addItem(i18n("Current Settings"), QString(proxyparameters + ';' + proxyextension));
+    }
+    proxy_profile->setCurrentIndex(ix);
+    slotUpdateProxyParams();
+
+    // Proxy GUI stuff
+    proxy_showprofileinfo->setIcon(KIcon("help-about"));
+    connect(proxy_profile, SIGNAL(currentIndexChanged(int)), this, SLOT(slotUpdateProxyParams()));
+    proxyparams->setVisible(false);
+    proxyparams->setMaximumHeight(QFontMetrics(font()).lineSpacing() * 5);
+    connect(proxy_showprofileinfo, SIGNAL(clicked(bool)), proxyparams, SLOT(setVisible(bool)));
+    
     if (readOnlyTracks) {
         video_tracks->setEnabled(false);
         audio_tracks->setEnabled(false);
     }
+    
+    
+    // Metadata list
+    QTreeWidgetItem *item = new QTreeWidgetItem(metadata_list, QStringList() << i18n("Title"));
+    item->setData(0, Qt::UserRole, QString("meta.attr.title.markup"));
+    if (metadata.contains("meta.attr.title.markup")) {
+       item->setText(1, metadata.value("meta.attr.title.markup"));
+       metadata.remove("meta.attr.title.markup");
+    }
+    item->setFlags(Qt::ItemIsEditable | Qt::ItemIsSelectable | Qt::ItemIsEnabled);
+    item = new QTreeWidgetItem(metadata_list, QStringList() << i18n("Author"));
+    item->setData(0, Qt::UserRole, QString("meta.attr.author.markup"));
+    if (metadata.contains("meta.attr.author.markup")) {
+       item->setText(1, metadata.value("meta.attr.author.markup"));
+       metadata.remove("meta.attr.author.markup");
+    }
+    else if (metadata.contains("meta.attr.artist.markup")) {
+       item->setText(0, i18n("Artist"));
+       item->setData(0, Qt::UserRole, QString("meta.attr.artist.markup"));
+       item->setText(1, metadata.value("meta.attr.artist.markup"));
+       metadata.remove("meta.attr.artist.markup");
+    }
+    item->setFlags(Qt::ItemIsEditable | Qt::ItemIsSelectable | Qt::ItemIsEnabled);
+    item = new QTreeWidgetItem(metadata_list, QStringList() << i18n("Copyright"));
+    item->setData(0, Qt::UserRole, QString("meta.attr.copyright.markup"));
+    if (metadata.contains("meta.attr.copyright.markup")) {
+       item->setText(1, metadata.value("meta.attr.copyright.markup"));
+       metadata.remove("meta.attr.copyright.markup");
+    }
+    item->setFlags(Qt::ItemIsEditable | Qt::ItemIsSelectable | Qt::ItemIsEnabled);
+    item = new QTreeWidgetItem(metadata_list, QStringList() << i18n("Year"));
+    item->setData(0, Qt::UserRole, QString("meta.attr.year.markup"));
+    if (metadata.contains("meta.attr.year.markup")) {
+       item->setText(1, metadata.value("meta.attr.year.markup"));
+       metadata.remove("meta.attr.year.markup");
+    }
+    else if (metadata.contains("meta.attr.date.markup")) {
+       item->setText(0, i18n("Date"));
+       item->setData(0, Qt::UserRole, QString("meta.attr.date.markup"));
+       item->setText(1, metadata.value("meta.attr.date.markup"));
+       metadata.remove("meta.attr.date.markup");
+    }
+    item->setFlags(Qt::ItemIsEditable | Qt::ItemIsSelectable | Qt::ItemIsEnabled);
+    
+    QMap<QString, QString>::const_iterator meta = metadata.constBegin();
+    while (meta != metadata.constEnd()) {
+       item = new QTreeWidgetItem(metadata_list, QStringList() << meta.key().section('.', 2,2));
+       item->setData(0, Qt::UserRole, meta.key());
+       item->setText(1, meta.value());
+       item->setFlags(Qt::ItemIsEditable | Qt::ItemIsSelectable | Qt::ItemIsEnabled);
+       ++meta;
+    }
+    
+    connect(add_metadata, SIGNAL(clicked()), this, SLOT(slotAddMetadataField()));
+    connect(delete_metadata, SIGNAL(clicked()), this, SLOT(slotDeleteMetadataField()));
+    add_metadata->setIcon(KIcon("list-add"));
+    delete_metadata->setIcon(KIcon("list-remove"));
+    
     slotUpdateDisplay();
     if (m_projectList != NULL) {
         slotUpdateFiles();
         connect(clear_cache, SIGNAL(clicked()), this, SLOT(slotClearCache()));
         connect(delete_unused, SIGNAL(clicked()), this, SLOT(slotDeleteUnused()));
+        connect(delete_proxies, SIGNAL(clicked()), this, SLOT(slotDeleteProxies()));
     } else tabWidget->widget(1)->setEnabled(false);
     connect(profiles_list, SIGNAL(currentIndexChanged(int)), this, SLOT(slotUpdateDisplay()));
-    connect(project_folder, SIGNAL(textChanged(const QString &)), this, SLOT(slotUpdateButton(const QString &)));
+    connect(project_folder, SIGNAL(textChanged(QString)), this, SLOT(slotUpdateButton(QString)));
+    connect(button_export, SIGNAL(clicked()), this, SLOT(slotExportToText()));
 }
 
 void ProjectSettings::slotDeleteUnused()
 {
     QStringList toDelete;
     QList <DocClipBase*> list = m_projectList->documentClipList();
-    for (int i = 0; i < list.count(); i++) {
+    for (int i = 0; i < list.count(); ++i) {
         DocClipBase *clip = list.at(i);
-        if (clip->numReferences() == 0 && clip->clipType() != SLIDESHOW) {
+        if (clip->numReferences() == 0 && clip->clipType() != SlideShow) {
             KUrl url = clip->fileURL();
             if (!url.isEmpty() && !toDelete.contains(url.path())) toDelete << url.path();
         }
     }
 
     // make sure our urls are not used in another clip
-    for (int i = 0; i < list.count(); i++) {
+    for (int i = 0; i < list.count(); ++i) {
         DocClipBase *clip = list.at(i);
         if (clip->numReferences() > 0) {
             KUrl url = clip->fileURL();
@@ -120,12 +242,28 @@ void ProjectSettings::slotClearCache()
     slotUpdateFiles(true);
 }
 
+void ProjectSettings::slotDeleteProxies()
+{
+    if (KMessageBox::warningContinueCancel(this, i18n("Deleting proxy clips will disable proxies for this project.")) != KMessageBox::Continue) return;
+    buttonBox->setEnabled(false);
+    enable_proxy->setChecked(false);
+    emit disableProxies();
+    KIO::NetAccess::del(KUrl(project_folder->url().path(KUrl::AddTrailingSlash) + "proxy/"), this);
+    KStandardDirs::makeDir(project_folder->url().path(KUrl::AddTrailingSlash) + "proxy/");
+    buttonBox->setEnabled(true);
+    slotUpdateFiles(true);
+}
+
 void ProjectSettings::slotUpdateFiles(bool cacheOnly)
 {
-    KIO::DirectorySizeJob * job = KIO::directorySize(project_folder->url().path(KUrl::AddTrailingSlash) + "thumbs/");
+    KIO::DirectorySizeJob *job = KIO::directorySize(project_folder->url().path(KUrl::AddTrailingSlash) + "thumbs/");
     job->exec();
     thumbs_count->setText(QString::number(job->totalFiles()));
     thumbs_size->setText(KIO::convertSize(job->totalSize()));
+    job = KIO::directorySize(project_folder->url().path(KUrl::AddTrailingSlash) + "proxy/");
+    job->exec();
+    proxy_count->setText(QString::number(job->totalFiles()));
+    proxy_size->setText(KIO::convertSize(job->totalSize()));
     delete job;
     if (cacheOnly) return;
     int unused = 0;
@@ -137,26 +275,83 @@ void ProjectSettings::slotUpdateFiles(bool cacheOnly)
 
     // List all files that are used in the project. That also means:
     // images included in slideshow and titles, files in playlist clips
-    // TODO: images used in luma transitions, files used for LADSPA effects?
-
-    QStringList allFiles;
+    // TODO: images used in luma transitions?
+
+    // Setup categories
+    QTreeWidgetItem *videos = new QTreeWidgetItem(files_list, QStringList() << i18n("Video clips"));
+    videos->setIcon(0, KIcon("video-x-generic"));
+    videos->setExpanded(true);
+    QTreeWidgetItem *sounds = new QTreeWidgetItem(files_list, QStringList() << i18n("Audio clips"));
+    sounds->setIcon(0, KIcon("audio-x-generic"));
+    sounds->setExpanded(true);
+    QTreeWidgetItem *images = new QTreeWidgetItem(files_list, QStringList() << i18n("Image clips"));
+    images->setIcon(0, KIcon("image-x-generic"));
+    images->setExpanded(true);
+    QTreeWidgetItem *slideshows = new QTreeWidgetItem(files_list, QStringList() << i18n("Slideshow clips"));
+    slideshows->setIcon(0, KIcon("image-x-generic"));
+    slideshows->setExpanded(true);
+    QTreeWidgetItem *texts = new QTreeWidgetItem(files_list, QStringList() << i18n("Text clips"));
+    texts->setIcon(0, KIcon("text-plain"));
+    texts->setExpanded(true);
+    QTreeWidgetItem *playlists = new QTreeWidgetItem(files_list, QStringList() << i18n("Playlist clips"));
+    playlists->setIcon(0, KIcon("video-mlt-playlist"));
+    playlists->setExpanded(true);
+    QTreeWidgetItem *others = new QTreeWidgetItem(files_list, QStringList() << i18n("Other clips"));
+    others->setIcon(0, KIcon("unknown"));
+    others->setExpanded(true);
+    int count = 0;
     QStringList allFonts;
-    allFiles << m_lumas;
-    for (int i = 0; i < list.count(); i++) {
+    foreach(const QString & file, m_lumas) {
+        count++;
+        new QTreeWidgetItem(images, QStringList() << file);
+    }
+
+    for (int i = 0; i < list.count(); ++i) {
         DocClipBase *clip = list.at(i);
-        if (clip->clipType() == SLIDESHOW) {
-            // special case, list all images
-            QStringList files = extractSlideshowUrls(clip->fileURL());
-            allFiles << files;
-        } else if (!clip->fileURL().isEmpty()) allFiles.append(clip->fileURL().path());
-        if (clip->clipType() == TEXT) {
-            QStringList images = TitleWidget::extractImageList(clip->getProperty("xmldata"));
+        if (clip->clipType() == SlideShow) {
+            QStringList subfiles = extractSlideshowUrls(clip->fileURL());
+            foreach(const QString & file, subfiles) {
+                count++;
+                new QTreeWidgetItem(slideshows, QStringList() << file);
+            }
+        } else if (!clip->fileURL().isEmpty()) {
+            //allFiles.append(clip->fileURL().path());
+            switch (clip->clipType()) {
+            case Text:
+                new QTreeWidgetItem(texts, QStringList() << clip->fileURL().path());
+                break;
+            case Audio:
+                new QTreeWidgetItem(sounds, QStringList() << clip->fileURL().path());
+                break;
+            case Image:
+                new QTreeWidgetItem(images, QStringList() << clip->fileURL().path());
+                break;
+            case Playlist:
+                new QTreeWidgetItem(playlists, QStringList() << clip->fileURL().path());
+                break;
+            case Unknown:
+                new QTreeWidgetItem(others, QStringList() << clip->fileURL().path());
+                break;
+            default:
+                new QTreeWidgetItem(videos, QStringList() << clip->fileURL().path());
+                break;
+            }
+            count++;
+        }
+        if (clip->clipType() == Text) {
+            QStringList imagefiles = TitleWidget::extractImageList(clip->getProperty("xmldata"));
             QStringList fonts = TitleWidget::extractFontList(clip->getProperty("xmldata"));
-            allFiles << images;
+            foreach(const QString & file, imagefiles) {
+                count++;
+                new QTreeWidgetItem(images, QStringList() << file);
+            }
             allFonts << fonts;
-        } else if (clip->clipType() == PLAYLIST) {
+        } else if (clip->clipType() == Playlist) {
             QStringList files = extractPlaylistUrls(clip->fileURL().path());
-            allFiles << files;
+            foreach(const QString & file, files) {
+                count++;
+                new QTreeWidgetItem(others, QStringList() << file);
+            }
         }
 
         if (clip->numReferences() == 0) {
@@ -167,13 +362,19 @@ void ProjectSettings::slotUpdateFiles(bool cacheOnly)
             usedSize += clip->fileSize();
         }
     }
-#if QT_VERSION >= 0x040500
-    allFiles.removeDuplicates();
     allFonts.removeDuplicates();
-#endif
-    files_count->setText(QString::number(allFiles.count()));
-    files_list->addItems(allFiles);
+    // Hide unused categories
+    for (int i = 0; i < files_list->topLevelItemCount(); ++i) {
+        if (files_list->topLevelItem(i)->childCount() == 0) {
+            files_list->topLevelItem(i)->setHidden(true);
+        }
+    }
+    files_count->setText(QString::number(count));
     fonts_list->addItems(allFonts);
+    if (allFonts.isEmpty()) {
+        fonts_list->setHidden(true);
+        label_fonts->setHidden(true);
+    }
     used_count->setText(QString::number(used));
     used_size->setText(KIO::convertSize(usedSize));
     unused_count->setText(QString::number(unused));
@@ -190,6 +391,7 @@ void ProjectSettings::accept()
 
 void ProjectSettings::slotUpdateDisplay()
 {
+    QLocale locale;
     QString currentProfile = profiles_list->itemData(profiles_list->currentIndex()).toString();
     QMap< QString, QString > values = ProfilesDialog::getSettingsFromFile(currentProfile);
     p_size->setText(values.value("width") + 'x' + values.value("height"));
@@ -198,7 +400,7 @@ void ProjectSettings::slotUpdateDisplay()
     p_display->setText(values.value("display_aspect_num") + '/' + values.value("display_aspect_den"));
     if (values.value("progressive").toInt() == 0) {
         p_progressive->setText(i18n("Interlaced (%1 fields per second)",
-                                    QString::number((double)2 * values.value("frame_rate_num").toInt() / values.value("frame_rate_den").toInt(), 'f', 2)));
+                                    locale.toString((double)2 * values.value("frame_rate_num").toInt() / values.value("frame_rate_den").toInt(), 'f', 2)));
     } else {
         p_progressive->setText(i18n("Progressive"));
     }
@@ -224,7 +426,7 @@ KUrl ProjectSettings::selectedFolder() const
     return project_folder->url();
 }
 
-QPoint ProjectSettings::tracks()
+QPoint ProjectSettings::tracks() const
 {
     QPoint p;
     p.setX(video_tracks->value());
@@ -242,9 +444,45 @@ bool ProjectSettings::enableAudioThumbs() const
     return audio_thumbs->isChecked();
 }
 
+bool ProjectSettings::useProxy() const
+{
+    return enable_proxy->isChecked();
+}
+
+bool ProjectSettings::generateProxy() const
+{
+    return generate_proxy->isChecked();
+}
+
+bool ProjectSettings::generateImageProxy() const
+{
+    return generate_imageproxy->isChecked();
+}
+
+int ProjectSettings::proxyMinSize() const
+{
+    return proxy_minsize->value();
+}
+
+int ProjectSettings::proxyImageMinSize() const
+{
+    return proxy_imageminsize->value();
+}
+
+QString ProjectSettings::proxyParams() const
+{
+    QString params = proxy_profile->itemData(proxy_profile->currentIndex()).toString();
+    return params.section(';', 0, 0);
+}
+
+QString ProjectSettings::proxyExtension() const
+{
+    QString params = proxy_profile->itemData(proxy_profile->currentIndex()).toString();
+    return params.section(';', 1, 1);
+}
 
 //static
-QStringList ProjectSettings::extractPlaylistUrls(QString path)
+QStringList ProjectSettings::extractPlaylistUrls(const QString &path)
 {
     QStringList urls;
     QDomDocument doc;
@@ -259,11 +497,14 @@ QStringList ProjectSettings::extractPlaylistUrls(QString path)
     QString root = doc.documentElement().attribute("root");
     if (!root.isEmpty() && !root.endsWith('/')) root.append('/');
     QDomNodeList files = doc.elementsByTagName("producer");
-    for (int i = 0; i < files.count(); i++) {
+    for (int i = 0; i < files.count(); ++i) {
         QDomElement e = files.at(i).toElement();
         QString type = EffectsList::property(e, "mlt_service");
         if (type != "colour") {
             QString url = EffectsList::property(e, "resource");
+            if (type == "framebuffer") {
+                url = url.section('?', 0, 0);
+            }
             if (!url.isEmpty()) {
                 if (!url.startsWith('/')) url.prepend(root);
                 if (url.section('.', 0, -2).endsWith("/.all")) {
@@ -280,7 +521,7 @@ QStringList ProjectSettings::extractPlaylistUrls(QString path)
 
     // luma files for transitions
     files = doc.elementsByTagName("transition");
-    for (int i = 0; i < files.count(); i++) {
+    for (int i = 0; i < files.count(); ++i) {
         QDomElement e = files.at(i).toElement();
         QString url = EffectsList::property(e, "luma");
         if (!url.isEmpty()) {
@@ -294,22 +535,104 @@ QStringList ProjectSettings::extractPlaylistUrls(QString path)
 
 
 //static
-QStringList ProjectSettings::extractSlideshowUrls(KUrl url)
+QStringList ProjectSettings::extractSlideshowUrls(const KUrl &url)
 {
     QStringList urls;
     QString path = url.directory(KUrl::AppendTrailingSlash);
     QString ext = url.path().section('.', -1);
     QDir dir(path);
-    QStringList filters;
-    filters << "*." + ext;
-    dir.setNameFilters(filters);
-    QStringList result = dir.entryList(QDir::Files);
-    for (int j = 0; j < result.count(); j++) {
-        urls.append(path + result.at(j));
+    if (url.path().contains(".all.")) {
+        // this is a mime slideshow, like *.jpeg
+        QStringList filters;
+        filters << "*." + ext;
+        dir.setNameFilters(filters);
+        QStringList result = dir.entryList(QDir::Files);
+        urls.append(path + filters.at(0) + " (" + i18np("1 image found", "%1 images found", result.count()) + ')');
+    } else {
+        // this is a pattern slideshow, like sequence%4d.jpg
+        QString filter = url.fileName();
+        QString ext = filter.section('.', -1);
+        filter = filter.section('%', 0, -2);
+        QString regexp = '^' + filter + "\\d+\\." + ext + '$';
+        QRegExp rx(regexp);
+        int count = 0;
+        QStringList result = dir.entryList(QDir::Files);
+        foreach(const QString & path, result) {
+            if (rx.exactMatch(path)) count++;
+        }
+        urls.append(url.path() + " (" + i18np("1 image found", "%1 images found", count) + ')');
     }
     return urls;
 }
 
+void ProjectSettings::slotExportToText()
+{
+    QString savePath = KFileDialog::getSaveFileName(project_folder->url(), "text/plain", this);
+    if (savePath.isEmpty()) return;
+    QString data;
+    data.append(i18n("Project folder: %1",  project_folder->url().path()) + '\n');
+    data.append(i18n("Project profile: %1",  profiles_list->currentText()) + '\n');
+    data.append(i18n("Total clips: %1 (%2 used in timeline).", files_count->text(), used_count->text()) + "\n\n");
+    for (int i = 0; i < files_list->topLevelItemCount(); ++i) {
+        if (files_list->topLevelItem(i)->childCount() > 0) {
+            data.append('\n' + files_list->topLevelItem(i)->text(0) + ":\n\n");
+            for (int j = 0; j < files_list->topLevelItem(i)->childCount(); j++) {
+                data.append(files_list->topLevelItem(i)->child(j)->text(0) + '\n');
+            }
+        }
+    }
+    KTemporaryFile tmpfile;
+    if (!tmpfile.open()) {
+        kWarning() << "/////  CANNOT CREATE TMP FILE in: " << tmpfile.fileName();
+        return;
+    }
+    QFile xmlf(tmpfile.fileName());
+    if (!xmlf.open(QIODevice::WriteOnly))
+        return;
+    xmlf.write(data.toUtf8());
+    if (xmlf.error() != QFile::NoError) {
+        xmlf.close();
+        return;
+    }
+    xmlf.close();
+    KIO::NetAccess::upload(tmpfile.fileName(), savePath, 0);
+}
+
+void ProjectSettings::slotUpdateProxyParams()
+{
+    QString params = proxy_profile->itemData(proxy_profile->currentIndex()).toString();
+    proxyparams->setPlainText(params.section(';', 0, 0));
+}
+
+const QMap <QString, QString> ProjectSettings::metadata() const
+{
+    QMap <QString, QString> metadata;
+    for (int i = 0; i < metadata_list->topLevelItemCount(); ++i)
+    {
+        QTreeWidgetItem *item = metadata_list->topLevelItem(i);
+        if (!item->text(1).simplified().isEmpty()) {
+            // Insert metadata entry
+            QString key = item->data(0, Qt::UserRole).toString();
+           if (key.isEmpty()) key = "meta.attr." + item->text(0).simplified() + ".markup";
+            QString value = item->text(1);
+            metadata.insert(key, value);
+        }
+    }
+    return metadata;
+}
+
+void ProjectSettings::slotAddMetadataField()
+{
+    QTreeWidgetItem *item = new QTreeWidgetItem(metadata_list, QStringList() << i18n("field_name"));
+    item->setFlags(Qt::ItemIsEditable | Qt::ItemIsSelectable | Qt::ItemIsEnabled);
+}
+
+void ProjectSettings::slotDeleteMetadataField()
+{
+    QTreeWidgetItem *item = metadata_list->currentItem();
+    if (item) delete item;
+}
+
 #include "projectsettings.moc"