]> git.sesse.net Git - kdenlive/blob - src/projectsettings.cpp
small cleanup
[kdenlive] / src / projectsettings.cpp
1 /***************************************************************************
2  *   Copyright (C) 2008 by Jean-Baptiste Mardelle (jb@kdenlive.org)        *
3  *                                                                         *
4  *   This program is free software; you can redistribute it and/or modify  *
5  *   it under the terms of the GNU General Public License as published by  *
6  *   the Free Software Foundation; either version 2 of the License, or     *
7  *   (at your option) any later version.                                   *
8  *                                                                         *
9  *   This program is distributed in the hope that it will be useful,       *
10  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
11  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
12  *   GNU General Public License for more details.                          *
13  *                                                                         *
14  *   You should have received a copy of the GNU General Public License     *
15  *   along with this program; if not, write to the                         *
16  *   Free Software Foundation, Inc.,                                       *
17  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA          *
18  ***************************************************************************/
19
20 #include "projectsettings.h"
21 #include "kdenlivesettings.h"
22 #include "profilesdialog.h"
23 #include "docclipbase.h"
24 #include "titlewidget.h"
25 #include "effectslist.h"
26
27 #include <KStandardDirs>
28 #include <KMessageBox>
29 #include <KDebug>
30 #include <kio/directorysizejob.h>
31 #include <KIO/NetAccess>
32 #include <KTemporaryFile>
33 #include <KFileDialog>
34
35 #include <QDir>
36 #include <kmessagebox.h>
37
38 ProjectSettings::ProjectSettings(ProjectList *projectlist, QMap <QString, QString> metadata, QStringList lumas, int videotracks, int audiotracks, const QString projectPath, bool readOnlyTracks, bool savedProject, QWidget * parent) :
39     QDialog(parent), m_savedProject(savedProject), m_projectList(projectlist), m_lumas(lumas)
40 {
41     setupUi(this);
42
43     list_search->setTreeWidget(files_list);
44
45     QMap <QString, QString> profilesInfo = ProfilesDialog::getProfilesInfo();
46     QMapIterator<QString, QString> i(profilesInfo);
47     while (i.hasNext()) {
48         i.next();
49         profiles_list->addItem(i.key(), i.value());
50     }
51     project_folder->setMode(KFile::Directory);
52     project_folder->setUrl(KUrl(projectPath));
53     QString currentProf = KdenliveSettings::current_profile();
54
55     for (int i = 0; i < profiles_list->count(); i++) {
56         if (profiles_list->itemData(i).toString() == currentProf) {
57             profiles_list->setCurrentIndex(i);
58             break;
59         }
60     }
61
62     m_buttonOk = buttonBox->button(QDialogButtonBox::Ok);
63     //buttonOk->setEnabled(false);
64     audio_thumbs->setChecked(KdenliveSettings::audiothumbnails());
65     video_thumbs->setChecked(KdenliveSettings::videothumbnails());
66     audio_tracks->setValue(audiotracks);
67     video_tracks->setValue(videotracks);
68     connect(generate_proxy, SIGNAL(toggled(bool)), proxy_minsize, SLOT(setEnabled(bool)));
69     connect(generate_imageproxy, SIGNAL(toggled(bool)), proxy_imageminsize, SLOT(setEnabled(bool)));
70     QString proxyparameters;
71     QString proxyextension;
72     if (projectlist) {
73         enable_proxy->setChecked(projectlist->getDocumentProperty("enableproxy").toInt());
74         generate_proxy->setChecked(projectlist->getDocumentProperty("generateproxy").toInt());
75         proxy_minsize->setValue(projectlist->getDocumentProperty("proxyminsize").toInt());
76         proxyparameters = projectlist->getDocumentProperty("proxyparams");
77         generate_imageproxy->setChecked(projectlist->getDocumentProperty("generateimageproxy").toInt());
78         proxy_imageminsize->setValue(projectlist->getDocumentProperty("proxyimageminsize").toInt());
79         proxyextension = projectlist->getDocumentProperty("proxyextension");
80     }
81     else {
82         enable_proxy->setChecked(KdenliveSettings::enableproxy());
83         generate_proxy->setChecked(KdenliveSettings::generateproxy());
84         proxy_minsize->setValue(KdenliveSettings::proxyminsize());
85         proxyparameters = KdenliveSettings::proxyparams();
86         generate_imageproxy->setChecked(KdenliveSettings::generateimageproxy());
87         proxy_imageminsize->setValue(KdenliveSettings::proxyimageminsize());
88         proxyextension = KdenliveSettings::proxyextension();
89       
90     }
91
92     proxy_minsize->setEnabled(generate_proxy->isChecked());
93     proxy_imageminsize->setEnabled(generate_imageproxy->isChecked());
94
95
96     // load proxy profiles
97     KConfig conf("encodingprofiles.rc", KConfig::CascadeConfig, "appdata");
98     KConfigGroup group(&conf, "proxy");
99     QMap <QString, QString> values = group.entryMap();
100     QMapIterator<QString, QString> k(values);
101     int ix = -1;
102     while (k.hasNext()) {
103         k.next();
104         if (!k.key().isEmpty()) {
105             QString params = k.value().section(';', 0, 0);
106             QString extension = k.value().section(';', 1, 1);
107             if (ix == -1 && ((params == proxyparameters && extension == proxyextension) || (proxyparameters.isEmpty() || proxyextension.isEmpty()))) {
108                 // this is the current profile
109                 ix = proxy_profile->count();
110             }
111             proxy_profile->addItem(k.key(), k.value());
112         }
113     }
114     if (ix == -1) {
115         // Current project proxy settings not found
116         ix = proxy_profile->count();
117         proxy_profile->addItem(i18n("Current Settings"), QString(proxyparameters + ';' + proxyextension));
118     }
119     proxy_profile->setCurrentIndex(ix);
120     slotUpdateProxyParams();
121
122     // Proxy GUI stuff
123     proxy_showprofileinfo->setIcon(KIcon("help-about"));
124     connect(proxy_profile, SIGNAL(currentIndexChanged(int)), this, SLOT(slotUpdateProxyParams()));
125     proxyparams->setVisible(false);
126     proxyparams->setMaximumHeight(QFontMetrics(font()).lineSpacing() * 5);
127     connect(proxy_showprofileinfo, SIGNAL(clicked(bool)), proxyparams, SLOT(setVisible(bool)));
128     
129     if (readOnlyTracks) {
130         video_tracks->setEnabled(false);
131         audio_tracks->setEnabled(false);
132     }
133     
134     
135     // Metadata list
136     QTreeWidgetItem *item = new QTreeWidgetItem(metadata_list, QStringList() << i18n("Title"));
137     item->setData(0, Qt::UserRole, QString("meta.attr.title.markup"));
138     if (metadata.contains("meta.attr.title.markup")) item->setText(1, metadata.value("meta.attr.title.markup"));
139     item->setFlags(Qt::ItemIsEditable | Qt::ItemIsSelectable | Qt::ItemIsEnabled);
140     item = new QTreeWidgetItem(metadata_list, QStringList() << i18n("Author"));
141     item->setData(0, Qt::UserRole, QString("meta.attr.author.markup"));
142     if (metadata.contains("meta.attr.author.markup")) item->setText(1, metadata.value("meta.attr.author.markup"));
143     item->setFlags(Qt::ItemIsEditable | Qt::ItemIsSelectable | Qt::ItemIsEnabled);
144     item = new QTreeWidgetItem(metadata_list, QStringList() << i18n("Copyright"));
145     item->setData(0, Qt::UserRole, QString("meta.attr.copyright.markup"));
146     if (metadata.contains("meta.attr.copyright.markup")) item->setText(1, metadata.value("meta.attr.copyright.markup"));
147     item->setFlags(Qt::ItemIsEditable | Qt::ItemIsSelectable | Qt::ItemIsEnabled);
148     item = new QTreeWidgetItem(metadata_list, QStringList() << i18n("Year"));
149     item->setData(0, Qt::UserRole, QString("meta.attr.year.markup"));
150     if (metadata.contains("meta.attr.year.markup")) item->setText(1, metadata.value("meta.attr.year.markup"));
151     item->setFlags(Qt::ItemIsEditable | Qt::ItemIsSelectable | Qt::ItemIsEnabled);
152     
153     slotUpdateDisplay();
154     if (m_projectList != NULL) {
155         slotUpdateFiles();
156         connect(clear_cache, SIGNAL(clicked()), this, SLOT(slotClearCache()));
157         connect(delete_unused, SIGNAL(clicked()), this, SLOT(slotDeleteUnused()));
158         connect(delete_proxies, SIGNAL(clicked()), this, SLOT(slotDeleteProxies()));
159     } else tabWidget->widget(1)->setEnabled(false);
160     connect(profiles_list, SIGNAL(currentIndexChanged(int)), this, SLOT(slotUpdateDisplay()));
161     connect(project_folder, SIGNAL(textChanged(const QString &)), this, SLOT(slotUpdateButton(const QString &)));
162     connect(button_export, SIGNAL(clicked()), this, SLOT(slotExportToText()));
163 }
164
165 void ProjectSettings::slotDeleteUnused()
166 {
167     QStringList toDelete;
168     QList <DocClipBase*> list = m_projectList->documentClipList();
169     for (int i = 0; i < list.count(); i++) {
170         DocClipBase *clip = list.at(i);
171         if (clip->numReferences() == 0 && clip->clipType() != SLIDESHOW) {
172             KUrl url = clip->fileURL();
173             if (!url.isEmpty() && !toDelete.contains(url.path())) toDelete << url.path();
174         }
175     }
176
177     // make sure our urls are not used in another clip
178     for (int i = 0; i < list.count(); i++) {
179         DocClipBase *clip = list.at(i);
180         if (clip->numReferences() > 0) {
181             KUrl url = clip->fileURL();
182             if (!url.isEmpty() && toDelete.contains(url.path())) toDelete.removeAll(url.path());
183         }
184     }
185
186     if (toDelete.count() == 0) {
187         // No physical url to delete, we only remove unused clips from project (color clips for example have no physical url)
188         if (KMessageBox::warningContinueCancel(this, i18n("This will remove all unused clips from your project."), i18n("Clean up project")) == KMessageBox::Cancel) return;
189         m_projectList->cleanup();
190         slotUpdateFiles();
191         return;
192     }
193     if (KMessageBox::warningYesNoList(this, i18n("This will remove the following files from your hard drive.\nThis action cannot be undone, only use if you know what you are doing.\nAre you sure you want to continue?"), toDelete, i18n("Delete unused clips")) != KMessageBox::Yes) return;
194     m_projectList->trashUnusedClips();
195     slotUpdateFiles();
196 }
197
198 void ProjectSettings::slotClearCache()
199 {
200     buttonBox->setEnabled(false);
201     KIO::NetAccess::del(KUrl(project_folder->url().path(KUrl::AddTrailingSlash) + "thumbs/"), this);
202     KStandardDirs::makeDir(project_folder->url().path(KUrl::AddTrailingSlash) + "thumbs/");
203     buttonBox->setEnabled(true);
204     slotUpdateFiles(true);
205 }
206
207 void ProjectSettings::slotDeleteProxies()
208 {
209     if (KMessageBox::warningContinueCancel(this, i18n("Deleting proxy clips will disable proxies for this project.")) != KMessageBox::Continue) return;
210     buttonBox->setEnabled(false);
211     enable_proxy->setChecked(false);
212     emit disableProxies();
213     KIO::NetAccess::del(KUrl(project_folder->url().path(KUrl::AddTrailingSlash) + "proxy/"), this);
214     KStandardDirs::makeDir(project_folder->url().path(KUrl::AddTrailingSlash) + "proxy/");
215     buttonBox->setEnabled(true);
216     slotUpdateFiles(true);
217 }
218
219 void ProjectSettings::slotUpdateFiles(bool cacheOnly)
220 {
221     KIO::DirectorySizeJob *job = KIO::directorySize(project_folder->url().path(KUrl::AddTrailingSlash) + "thumbs/");
222     job->exec();
223     thumbs_count->setText(QString::number(job->totalFiles()));
224     thumbs_size->setText(KIO::convertSize(job->totalSize()));
225     job = KIO::directorySize(project_folder->url().path(KUrl::AddTrailingSlash) + "proxy/");
226     job->exec();
227     proxy_count->setText(QString::number(job->totalFiles()));
228     proxy_size->setText(KIO::convertSize(job->totalSize()));
229     delete job;
230     if (cacheOnly) return;
231     int unused = 0;
232     int used = 0;
233     KIO::filesize_t usedSize = 0;
234     KIO::filesize_t unUsedSize = 0;
235     QList <DocClipBase*> list = m_projectList->documentClipList();
236     files_list->clear();
237
238     // List all files that are used in the project. That also means:
239     // images included in slideshow and titles, files in playlist clips
240     // TODO: images used in luma transitions?
241
242     // Setup categories
243     QTreeWidgetItem *videos = new QTreeWidgetItem(files_list, QStringList() << i18n("Video clips"));
244     videos->setIcon(0, KIcon("video-x-generic"));
245     videos->setExpanded(true);
246     QTreeWidgetItem *sounds = new QTreeWidgetItem(files_list, QStringList() << i18n("Audio clips"));
247     sounds->setIcon(0, KIcon("audio-x-generic"));
248     sounds->setExpanded(true);
249     QTreeWidgetItem *images = new QTreeWidgetItem(files_list, QStringList() << i18n("Image clips"));
250     images->setIcon(0, KIcon("image-x-generic"));
251     images->setExpanded(true);
252     QTreeWidgetItem *slideshows = new QTreeWidgetItem(files_list, QStringList() << i18n("Slideshow clips"));
253     slideshows->setIcon(0, KIcon("image-x-generic"));
254     slideshows->setExpanded(true);
255     QTreeWidgetItem *texts = new QTreeWidgetItem(files_list, QStringList() << i18n("Text clips"));
256     texts->setIcon(0, KIcon("text-plain"));
257     texts->setExpanded(true);
258     QTreeWidgetItem *playlists = new QTreeWidgetItem(files_list, QStringList() << i18n("Playlist clips"));
259     playlists->setIcon(0, KIcon("video-mlt-playlist"));
260     playlists->setExpanded(true);
261     QTreeWidgetItem *others = new QTreeWidgetItem(files_list, QStringList() << i18n("Other clips"));
262     others->setIcon(0, KIcon("unknown"));
263     others->setExpanded(true);
264     int count = 0;
265     QStringList allFonts;
266     foreach(const QString & file, m_lumas) {
267         count++;
268         new QTreeWidgetItem(images, QStringList() << file);
269     }
270
271     for (int i = 0; i < list.count(); i++) {
272         DocClipBase *clip = list.at(i);
273         if (clip->clipType() == SLIDESHOW) {
274             QStringList subfiles = extractSlideshowUrls(clip->fileURL());
275             foreach(const QString & file, subfiles) {
276                 count++;
277                 new QTreeWidgetItem(slideshows, QStringList() << file);
278             }
279         } else if (!clip->fileURL().isEmpty()) {
280             //allFiles.append(clip->fileURL().path());
281             switch (clip->clipType()) {
282             case TEXT:
283                 new QTreeWidgetItem(texts, QStringList() << clip->fileURL().path());
284                 break;
285             case AUDIO:
286                 new QTreeWidgetItem(sounds, QStringList() << clip->fileURL().path());
287                 break;
288             case IMAGE:
289                 new QTreeWidgetItem(images, QStringList() << clip->fileURL().path());
290                 break;
291             case PLAYLIST:
292                 new QTreeWidgetItem(playlists, QStringList() << clip->fileURL().path());
293                 break;
294             case UNKNOWN:
295                 new QTreeWidgetItem(others, QStringList() << clip->fileURL().path());
296                 break;
297             default:
298                 new QTreeWidgetItem(videos, QStringList() << clip->fileURL().path());
299                 break;
300             }
301             count++;
302         }
303         if (clip->clipType() == TEXT) {
304             QStringList imagefiles = TitleWidget::extractImageList(clip->getProperty("xmldata"));
305             QStringList fonts = TitleWidget::extractFontList(clip->getProperty("xmldata"));
306             foreach(const QString & file, imagefiles) {
307                 count++;
308                 new QTreeWidgetItem(images, QStringList() << file);
309             }
310             allFonts << fonts;
311         } else if (clip->clipType() == PLAYLIST) {
312             QStringList files = extractPlaylistUrls(clip->fileURL().path());
313             foreach(const QString & file, files) {
314                 count++;
315                 new QTreeWidgetItem(others, QStringList() << file);
316             }
317         }
318
319         if (clip->numReferences() == 0) {
320             unused++;
321             unUsedSize += clip->fileSize();
322         } else {
323             used++;
324             usedSize += clip->fileSize();
325         }
326     }
327     allFonts.removeDuplicates();
328     // Hide unused categories
329     for (int i = 0; i < files_list->topLevelItemCount(); i++) {
330         if (files_list->topLevelItem(i)->childCount() == 0) {
331             files_list->topLevelItem(i)->setHidden(true);
332         }
333     }
334     files_count->setText(QString::number(count));
335     fonts_list->addItems(allFonts);
336     if (allFonts.isEmpty()) {
337         fonts_list->setHidden(true);
338         label_fonts->setHidden(true);
339     }
340     used_count->setText(QString::number(used));
341     used_size->setText(KIO::convertSize(usedSize));
342     unused_count->setText(QString::number(unused));
343     unused_size->setText(KIO::convertSize(unUsedSize));
344     delete_unused->setEnabled(unused > 0);
345 }
346
347 void ProjectSettings::accept()
348 {
349     if (!m_savedProject && selectedProfile() != KdenliveSettings::current_profile())
350         if (KMessageBox::warningContinueCancel(this, i18n("Changing the profile of your project cannot be undone.\nIt is recommended to save your project before attempting this operation that might cause some corruption in transitions.\n Are you sure you want to proceed?"), i18n("Confirm profile change")) == KMessageBox::Cancel) return;
351     QDialog::accept();
352 }
353
354 void ProjectSettings::slotUpdateDisplay()
355 {
356     QLocale locale;
357     QString currentProfile = profiles_list->itemData(profiles_list->currentIndex()).toString();
358     QMap< QString, QString > values = ProfilesDialog::getSettingsFromFile(currentProfile);
359     p_size->setText(values.value("width") + 'x' + values.value("height"));
360     p_fps->setText(values.value("frame_rate_num") + '/' + values.value("frame_rate_den"));
361     p_aspect->setText(values.value("sample_aspect_num") + '/' + values.value("sample_aspect_den"));
362     p_display->setText(values.value("display_aspect_num") + '/' + values.value("display_aspect_den"));
363     if (values.value("progressive").toInt() == 0) {
364         p_progressive->setText(i18n("Interlaced (%1 fields per second)",
365                                     locale.toString((double)2 * values.value("frame_rate_num").toInt() / values.value("frame_rate_den").toInt(), 'f', 2)));
366     } else {
367         p_progressive->setText(i18n("Progressive"));
368     }
369     p_colorspace->setText(ProfilesDialog::getColorspaceDescription(values.value("colorspace").toInt()));
370 }
371
372 void ProjectSettings::slotUpdateButton(const QString &path)
373 {
374     if (path.isEmpty()) m_buttonOk->setEnabled(false);
375     else {
376         m_buttonOk->setEnabled(true);
377         slotUpdateFiles(true);
378     }
379 }
380
381 QString ProjectSettings::selectedProfile() const
382 {
383     return profiles_list->itemData(profiles_list->currentIndex()).toString();
384 }
385
386 KUrl ProjectSettings::selectedFolder() const
387 {
388     return project_folder->url();
389 }
390
391 QPoint ProjectSettings::tracks()
392 {
393     QPoint p;
394     p.setX(video_tracks->value());
395     p.setY(audio_tracks->value());
396     return p;
397 }
398
399 bool ProjectSettings::enableVideoThumbs() const
400 {
401     return video_thumbs->isChecked();
402 }
403
404 bool ProjectSettings::enableAudioThumbs() const
405 {
406     return audio_thumbs->isChecked();
407 }
408
409 bool ProjectSettings::useProxy() const
410 {
411     return enable_proxy->isChecked();
412 }
413
414 bool ProjectSettings::generateProxy() const
415 {
416     return generate_proxy->isChecked();
417 }
418
419 bool ProjectSettings::generateImageProxy() const
420 {
421     return generate_imageproxy->isChecked();
422 }
423
424 int ProjectSettings::proxyMinSize() const
425 {
426     return proxy_minsize->value();
427 }
428
429 int ProjectSettings::proxyImageMinSize() const
430 {
431     return proxy_imageminsize->value();
432 }
433
434 QString ProjectSettings::proxyParams() const
435 {
436     QString params = proxy_profile->itemData(proxy_profile->currentIndex()).toString();
437     return params.section(';', 0, 0);
438 }
439
440 QString ProjectSettings::proxyExtension() const
441 {
442     QString params = proxy_profile->itemData(proxy_profile->currentIndex()).toString();
443     return params.section(';', 1, 1);
444 }
445
446 //static
447 QStringList ProjectSettings::extractPlaylistUrls(QString path)
448 {
449     QStringList urls;
450     QDomDocument doc;
451     QFile file(path);
452     if (!file.open(QIODevice::ReadOnly))
453         return urls;
454     if (!doc.setContent(&file)) {
455         file.close();
456         return urls;
457     }
458     file.close();
459     QString root = doc.documentElement().attribute("root");
460     if (!root.isEmpty() && !root.endsWith('/')) root.append('/');
461     QDomNodeList files = doc.elementsByTagName("producer");
462     for (int i = 0; i < files.count(); i++) {
463         QDomElement e = files.at(i).toElement();
464         QString type = EffectsList::property(e, "mlt_service");
465         if (type != "colour") {
466             QString url = EffectsList::property(e, "resource");
467             if (type == "framebuffer") {
468                 url = url.section('?', 0, 0);
469             }
470             if (!url.isEmpty()) {
471                 if (!url.startsWith('/')) url.prepend(root);
472                 if (url.section('.', 0, -2).endsWith("/.all")) {
473                     // slideshow clip, extract image urls
474                     urls << extractSlideshowUrls(KUrl(url));
475                 } else urls << url;
476                 if (url.endsWith(".mlt") || url.endsWith(".kdenlive")) {
477                     //TODO: Do something to avoid infinite loops if 2 files reference themselves...
478                     urls << extractPlaylistUrls(url);
479                 }
480             }
481         }
482     }
483
484     // luma files for transitions
485     files = doc.elementsByTagName("transition");
486     for (int i = 0; i < files.count(); i++) {
487         QDomElement e = files.at(i).toElement();
488         QString url = EffectsList::property(e, "luma");
489         if (!url.isEmpty()) {
490             if (!url.startsWith('/')) url.prepend(root);
491             urls << url;
492         }
493     }
494
495     return urls;
496 }
497
498
499 //static
500 QStringList ProjectSettings::extractSlideshowUrls(KUrl url)
501 {
502     QStringList urls;
503     QString path = url.directory(KUrl::AppendTrailingSlash);
504     QString ext = url.path().section('.', -1);
505     QDir dir(path);
506     if (url.path().contains(".all.")) {
507         // this is a mime slideshow, like *.jpeg
508         QStringList filters;
509         filters << "*." + ext;
510         dir.setNameFilters(filters);
511         QStringList result = dir.entryList(QDir::Files);
512         urls.append(path + filters.at(0) + " (" + i18np("1 image found", "%1 images found", result.count()) + ')');
513     } else {
514         // this is a pattern slideshow, like sequence%4d.jpg
515         QString filter = url.fileName();
516         QString ext = filter.section('.', -1);
517         filter = filter.section('%', 0, -2);
518         QString regexp = '^' + filter + "\\d+\\." + ext + '$';
519         QRegExp rx(regexp);
520         int count = 0;
521         QStringList result = dir.entryList(QDir::Files);
522         foreach(const QString & path, result) {
523             if (rx.exactMatch(path)) count++;
524         }
525         urls.append(url.path() + " (" + i18np("1 image found", "%1 images found", count) + ')');
526     }
527     return urls;
528 }
529
530 void ProjectSettings::slotExportToText()
531 {
532     QString savePath = KFileDialog::getSaveFileName(project_folder->url(), "text/plain", this);
533     if (savePath.isEmpty()) return;
534     QString data;
535     data.append(i18n("Project folder: %1",  project_folder->url().path()) + '\n');
536     data.append(i18n("Project profile: %1",  profiles_list->currentText()) + '\n');
537     data.append(i18n("Total clips: %1 (%2 used in timeline).", files_count->text(), used_count->text()) + "\n\n");
538     for (int i = 0; i < files_list->topLevelItemCount(); i++) {
539         if (files_list->topLevelItem(i)->childCount() > 0) {
540             data.append('\n' + files_list->topLevelItem(i)->text(0) + ":\n\n");
541             for (int j = 0; j < files_list->topLevelItem(i)->childCount(); j++) {
542                 data.append(files_list->topLevelItem(i)->child(j)->text(0) + '\n');
543             }
544         }
545     }
546     KTemporaryFile tmpfile;
547     if (!tmpfile.open()) {
548         kWarning() << "/////  CANNOT CREATE TMP FILE in: " << tmpfile.fileName();
549         return;
550     }
551     QFile xmlf(tmpfile.fileName());
552     if (!xmlf.open(QIODevice::WriteOnly))
553         return;
554     xmlf.write(data.toUtf8());
555     if (xmlf.error() != QFile::NoError) {
556         xmlf.close();
557         return;
558     }
559     xmlf.close();
560     KIO::NetAccess::upload(tmpfile.fileName(), savePath, 0);
561 }
562
563 void ProjectSettings::slotUpdateProxyParams()
564 {
565     QString params = proxy_profile->itemData(proxy_profile->currentIndex()).toString();
566     proxyparams->setPlainText(params.section(';', 0, 0));
567 }
568
569 const QMap <QString, QString> ProjectSettings::metadata() const
570 {
571     QMap <QString, QString> metadata;
572     for (int i = 0; i < metadata_list->topLevelItemCount(); i++)
573     {
574         QTreeWidgetItem *item = metadata_list->topLevelItem(i);
575         if (!item->text(1).simplified().isEmpty()) {
576             // Insert metadata entry
577             QString key = item->data(0, Qt::UserRole).toString();
578             QString value = item->text(1);
579             metadata.insert(key, value);
580         }
581     }
582     return metadata;
583 }
584
585 #include "projectsettings.moc"
586
587