]> git.sesse.net Git - kdenlive/blob - src/archivewidget.cpp
Show progress when archiving compressed project
[kdenlive] / src / archivewidget.cpp
1 /***************************************************************************
2  *   Copyright (C) 2011 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
21 #include "archivewidget.h"
22 #include "titlewidget.h"
23
24 #include <KLocale>
25 #include <KDiskFreeSpaceInfo>
26 #include <KUrlRequester>
27 #include <KFileDialog>
28 #include <KMessageBox>
29 #include <KGuiItem>
30 #include <KIO/NetAccess>
31 #include <KTar>
32
33 #include <KDebug>
34 #include <QTreeWidget>
35 #include <QtConcurrentRun>
36 #include "projectsettings.h"
37
38
39 ArchiveWidget::ArchiveWidget(QString projectName, QDomDocument doc, QList <DocClipBase*> list, QStringList luma_list, QWidget * parent) :
40         QDialog(parent),
41         m_requestedSize(0),
42         m_copyJob(NULL),
43         m_name(projectName.section('.', 0, -2)),
44         m_doc(doc),
45         m_abortArchive(false)
46 {
47     setAttribute(Qt::WA_DeleteOnClose);
48     setupUi(this);
49     setWindowTitle(i18n("Archive Project"));
50     archive_url->setUrl(KUrl(QDir::homePath()));
51     connect(archive_url, SIGNAL(textChanged (const QString &)), this, SLOT(slotCheckSpace()));
52     connect(this, SIGNAL(archivingFinished(bool)), this, SLOT(slotArchivingFinished(bool)));
53     connect(this, SIGNAL(archiveProgress(int)), this, SLOT(slotArchivingProgress(int)));
54
55     // Setup categories
56     QTreeWidgetItem *videos = new QTreeWidgetItem(files_list, QStringList() << i18n("Video clips"));
57     videos->setIcon(0, KIcon("video-x-generic"));
58     videos->setData(0, Qt::UserRole, "videos");
59     videos->setExpanded(false);
60     QTreeWidgetItem *sounds = new QTreeWidgetItem(files_list, QStringList() << i18n("Audio clips"));
61     sounds->setIcon(0, KIcon("audio-x-generic"));
62     sounds->setData(0, Qt::UserRole, "sounds");
63     sounds->setExpanded(false);
64     QTreeWidgetItem *images = new QTreeWidgetItem(files_list, QStringList() << i18n("Image clips"));
65     images->setIcon(0, KIcon("image-x-generic"));
66     images->setData(0, Qt::UserRole, "images");
67     images->setExpanded(false);
68     QTreeWidgetItem *slideshows = new QTreeWidgetItem(files_list, QStringList() << i18n("Slideshow clips"));
69     slideshows->setIcon(0, KIcon("image-x-generic"));
70     slideshows->setData(0, Qt::UserRole, "slideshows");
71     slideshows->setExpanded(false);
72     QTreeWidgetItem *texts = new QTreeWidgetItem(files_list, QStringList() << i18n("Text clips"));
73     texts->setIcon(0, KIcon("text-plain"));
74     texts->setData(0, Qt::UserRole, "texts");
75     texts->setExpanded(false);
76     QTreeWidgetItem *others = new QTreeWidgetItem(files_list, QStringList() << i18n("Other clips"));
77     others->setIcon(0, KIcon("unknown"));
78     others->setData(0, Qt::UserRole, "others");
79     others->setExpanded(false);
80     QTreeWidgetItem *lumas = new QTreeWidgetItem(files_list, QStringList() << i18n("Luma files"));
81     lumas->setIcon(0, KIcon("image-x-generic"));
82     lumas->setData(0, Qt::UserRole, "lumas");
83     lumas->setExpanded(false);
84     
85     QTreeWidgetItem *proxies = new QTreeWidgetItem(files_list, QStringList() << i18n("Proxy clips"));
86     proxies->setIcon(0, KIcon("video-x-generic"));
87     proxies->setData(0, Qt::UserRole, "proxy");
88     proxies->setExpanded(false);
89     
90     // process all files
91     QStringList allFonts;
92     KUrl::List fileUrls;
93     QStringList fileNames;
94     generateItems(lumas, luma_list);
95
96     QStringList slideUrls;
97     QStringList audioUrls;
98     QStringList videoUrls;
99     QStringList imageUrls;
100     QStringList otherUrls;
101     QStringList proxyUrls;
102
103     for (int i = 0; i < list.count(); i++) {
104         DocClipBase *clip = list.at(i);
105         CLIPTYPE t = clip->clipType();
106         if (t == SLIDESHOW) {
107             KUrl slideUrl = clip->fileURL();
108             //TODO: Slideshow files
109             slideUrls << slideUrl.path();
110         }
111         else if (t == IMAGE) imageUrls << clip->fileURL().path();
112         else if (t == TEXT) {
113             QStringList imagefiles = TitleWidget::extractImageList(clip->getProperty("xmldata"));
114             QStringList fonts = TitleWidget::extractFontList(clip->getProperty("xmldata"));
115             imageUrls << imagefiles;
116             allFonts << fonts;
117         } else if (t == PLAYLIST) {
118             QStringList files = ProjectSettings::extractPlaylistUrls(clip->fileURL().path());
119             otherUrls << files;
120         }
121         else if (!clip->fileURL().isEmpty()) {
122             if (t == AUDIO) audioUrls << clip->fileURL().path();
123             else {
124                 videoUrls << clip->fileURL().path();
125                 // Check if we have a proxy
126                 QString proxy = clip->getProperty("proxy");
127                 if (!proxy.isEmpty() && proxy != "-" && QFile::exists(proxy)) proxyUrls << proxy;
128             }
129         }
130     }
131
132     generateItems(sounds, audioUrls);
133     generateItems(videos, videoUrls);
134     generateItems(images, imageUrls);
135     generateItems(slideshows, slideUrls);
136     generateItems(others, otherUrls);
137     generateItems(proxies, proxyUrls);
138     
139 #if QT_VERSION >= 0x040500
140     allFonts.removeDuplicates();
141 #endif
142
143     //TODO: fonts
144
145     // Hide unused categories, add item count
146     int total = 0;
147     for (int i = 0; i < files_list->topLevelItemCount(); i++) {
148         QTreeWidgetItem *parentItem = files_list->topLevelItem(i);
149         int items = parentItem->childCount();
150         if (items == 0) {
151             files_list->topLevelItem(i)->setHidden(true);
152         }
153         else {
154             if (parentItem->data(0, Qt::UserRole).toString() == "slideshows")
155             {
156                 // Special case: slideshows contain several files
157                 for (int j = 0; j < items; j++) {
158                     total += parentItem->child(j)->data(0, Qt::UserRole + 1).toStringList().count();
159                 }
160             }
161             else total += items;
162             parentItem->setText(0, files_list->topLevelItem(i)->text(0) + " " + i18np("(%1 item)", "(%1 items)", items));
163         }
164     }
165
166     compressed_archive->setText(compressed_archive->text() + " (" + m_name + ".tar.gz)");
167     project_files->setText(i18np("%1 file to archive, requires %2", "%1 files to archive, requires %2", total, KIO::convertSize(m_requestedSize)));
168     buttonBox->button(QDialogButtonBox::Apply)->setText(i18n("Archive"));
169     connect(buttonBox->button(QDialogButtonBox::Apply), SIGNAL(clicked()), this, SLOT(slotStartArchiving()));
170     buttonBox->button(QDialogButtonBox::Apply)->setEnabled(false);
171     
172     slotCheckSpace();
173 }
174
175 ArchiveWidget::~ArchiveWidget()
176 {
177 }
178
179 void ArchiveWidget::done ( int r )
180 {
181     if (closeAccepted()) QDialog::done(r);
182 }
183
184 void ArchiveWidget::closeEvent ( QCloseEvent * e )
185 {
186
187     if (closeAccepted()) e->accept();
188     else e->ignore();
189 }
190
191
192 bool ArchiveWidget::closeAccepted()
193 {
194     if (!archive_url->isEnabled()) {
195         // Archiving in progress, should we stop?
196         if (KMessageBox::warningContinueCancel(this, i18n("Archiving in progress, do you want to stop it?"), i18n("Stop Archiving"), KGuiItem(i18n("Stop Archiving"))) != KMessageBox::Continue) {
197             return false;
198         }
199         if (m_copyJob) m_copyJob->kill();
200     }
201     return true;
202 }
203
204
205 void ArchiveWidget::generateItems(QTreeWidgetItem *parentItem, QStringList items)
206 {
207     QStringList filesList;
208     QString fileName;
209     int ix = 0;
210     bool isSlideshow = parentItem->data(0, Qt::UserRole).toString() == "slideshows";
211     foreach(const QString & file, items) {
212         QTreeWidgetItem *item = new QTreeWidgetItem(parentItem, QStringList() << file);
213         fileName = KUrl(file).fileName();
214         if (isSlideshow) {
215             // we store each slideshow in a separate subdirectory
216             item->setData(0, Qt::UserRole, ix);
217             ix++;
218             KUrl slideUrl(file);
219             QDir dir(slideUrl.directory(KUrl::AppendTrailingSlash));
220             if (slideUrl.fileName().startsWith(".all.")) {
221                 // mimetype slideshow (for example *.png)
222                     QStringList filters;
223                     QString extension;
224                     // TODO: improve jpeg image detection with extension like jpeg, requires change in MLT image producers
225                     filters << "*." + slideUrl.fileName().section('.', -1);
226                     dir.setNameFilters(filters);
227                     QFileInfoList resultList = dir.entryInfoList(QDir::Files);
228                     QStringList slideImages;
229                     for (int i = 0; i < resultList.count(); i++) {
230                         m_requestedSize += resultList.at(i).size();
231                         slideImages << resultList.at(i).absoluteFilePath();
232                     }
233                     item->setData(0, Qt::UserRole + 1, slideImages);
234             }
235             else {
236                 // pattern url (like clip%.3d.png)
237                 QStringList result = dir.entryList(QDir::Files);
238                 QString filter = slideUrl.fileName();
239                 QString ext = filter.section('.', -1);
240                 filter = filter.section('%', 0, -2);
241                 QString regexp = "^" + filter + "\\d+\\." + ext + "$";
242                 QRegExp rx(regexp);
243                 QStringList slideImages;
244                 QString directory = dir.absolutePath();
245                 if (!directory.endsWith('/')) directory.append('/');
246                 foreach(const QString & path, result) {
247                     if (rx.exactMatch(path)) {
248                         m_requestedSize += QFileInfo(directory + path).size();
249                         slideImages <<  directory + path;
250                     }
251                 }
252                 item->setData(0, Qt::UserRole + 1, slideImages);
253             }                    
254         }
255         else if (filesList.contains(fileName)) {
256             // we have 2 files with same name
257             int ix = 0;
258             QString newFileName = fileName.section('.', 0, -2) + "_" + QString::number(ix) + "." + fileName.section('.', -1);
259             while (filesList.contains(newFileName)) {
260                 ix ++;
261                 newFileName = fileName.section('.', 0, -2) + "_" + QString::number(ix) + "." + fileName.section('.', -1);
262             }
263             fileName = newFileName;
264             item->setData(0, Qt::UserRole, fileName);
265         }
266         if (!isSlideshow) {
267             m_requestedSize += QFileInfo(file).size();
268             filesList << fileName;
269         }
270     }
271 }
272
273 void ArchiveWidget::slotCheckSpace()
274 {
275     KDiskFreeSpaceInfo inf = KDiskFreeSpaceInfo::freeSpaceInfo( archive_url->url().path());
276     KIO::filesize_t freeSize = inf.available();;
277     if (freeSize > m_requestedSize) {
278         // everything is ok
279         buttonBox->button(QDialogButtonBox::Apply)->setEnabled(true);
280         icon_info->setPixmap(KIcon("dialog-ok").pixmap(16, 16));
281         text_info->setText(i18n("Available space on drive: %1", KIO::convertSize(freeSize)));
282     }
283     else {
284         buttonBox->button(QDialogButtonBox::Apply)->setEnabled(false);
285         icon_info->setPixmap(KIcon("dialog-close").pixmap(16, 16));
286         text_info->setText(i18n("Not enough space on drive, free space: %1", KIO::convertSize(freeSize)));
287     }
288 }
289
290 bool ArchiveWidget::slotStartArchiving(bool firstPass)
291 {
292     if (firstPass && (m_copyJob || m_archiveThread.isRunning())) {
293         // archiving in progress, abort
294         if (m_copyJob) m_copyJob->kill(KJob::EmitResult);
295         m_abortArchive = true;
296         return true;
297     }
298     bool isArchive = compressed_archive->isChecked();
299     if (!firstPass) m_copyJob = NULL;
300     else {
301         //starting archiving
302         m_abortArchive = false;
303         m_duplicateFiles.clear();
304         m_replacementList.clear();
305         m_foldersList.clear();
306         m_filesList.clear();
307         icon_info->setPixmap(KIcon("system-run").pixmap(16, 16));
308         text_info->setText(i18n("Archiving..."));
309         repaint();
310         archive_url->setEnabled(false);
311         compressed_archive->setEnabled(false);
312     }
313     KUrl::List files;
314     KUrl destUrl;
315     QString destPath;
316     QTreeWidgetItem *parentItem;
317     bool isSlideshow = false;
318     for (int i = 0; i < files_list->topLevelItemCount(); i++) {
319         parentItem = files_list->topLevelItem(i);
320         if (parentItem->childCount() > 0 && !parentItem->isDisabled()) {
321             if (parentItem->data(0, Qt::UserRole).toString() == "slideshows") {
322                 KUrl slideFolder(archive_url->url().path(KUrl::AddTrailingSlash) + "slideshows");
323                 if (isArchive) m_foldersList.append("slideshows");
324                 else KIO::NetAccess::mkdir(slideFolder, this);
325                 isSlideshow = true;
326             }
327             files_list->setCurrentItem(parentItem);
328             if (!isSlideshow) parentItem->setDisabled(true);
329             destPath = parentItem->data(0, Qt::UserRole).toString() + "/";
330             destUrl = KUrl(archive_url->url().path(KUrl::AddTrailingSlash) + destPath);
331             QTreeWidgetItem *item;
332             for (int j = 0; j < parentItem->childCount(); j++) {
333                 item = parentItem->child(j);
334                 // Special case: slideshows
335                 if (isSlideshow) {
336                     if (item->isDisabled()) {
337                         continue;
338                     }
339                     destPath.append(item->data(0, Qt::UserRole).toString() + "/");
340                     destUrl = KUrl(archive_url->url().path(KUrl::AddTrailingSlash) + destPath);
341                     QStringList srcFiles = item->data(0, Qt::UserRole + 1).toStringList();
342                     for (int k = 0; k < srcFiles.count(); k++) {
343                         files << KUrl(srcFiles.at(k));
344                     }
345                     item->setDisabled(true);
346                     if (parentItem->indexOfChild(item) == parentItem->childCount() - 1) {
347                         // We have processed all slideshows
348                         parentItem->setDisabled(true);
349                     }
350                     break;
351                 }
352                 else if (item->data(0, Qt::UserRole).isNull()) {
353                     files << KUrl(item->text(0));
354                 }
355                 else {
356                     // We must rename the destination file, since another file with same name exists
357                     //TODO: monitor progress
358                     if (isArchive) {
359                         m_filesList.insert(item->text(0), destPath + item->data(0, Qt::UserRole).toString());
360                     }
361                     else m_duplicateFiles.insert(KUrl(item->text(0)), KUrl(destUrl.path(KUrl::AddTrailingSlash) + item->data(0, Qt::UserRole).toString()));
362                 }
363             }
364             break;
365         }
366     }
367
368     if (destPath.isEmpty()) {
369         if (m_duplicateFiles.isEmpty()) return false;        
370         QMapIterator<KUrl, KUrl> i(m_duplicateFiles);
371         if (i.hasNext()) {
372             i.next();
373             KUrl startJobSrc = i.key();
374             KUrl startJobDst = i.value();
375             m_duplicateFiles.remove(startJobSrc);
376             KIO::CopyJob *job = KIO::copyAs(startJobSrc, startJobDst, KIO::HideProgressInfo);
377             connect(job, SIGNAL(result(KJob *)), this, SLOT(slotArchivingFinished(KJob *)));
378             connect(job, SIGNAL(processedSize(KJob *, qulonglong)), this, SLOT(slotArchivingProgress(KJob *, qulonglong)));
379         }
380         return true;
381     }
382
383     if (isArchive) {
384         m_foldersList.append(destPath);
385         for (int i = 0; i < files.count(); i++) {
386             m_filesList.insert(files.at(i).path(), destPath + files.at(i).fileName());
387         }
388         slotArchivingFinished();
389     }
390     else if (files.isEmpty()) {
391         slotStartArchiving(false);
392     }
393     else {
394         KIO::NetAccess::mkdir(destUrl, this);
395         m_copyJob = KIO::copy (files, destUrl, KIO::HideProgressInfo);
396         connect(m_copyJob, SIGNAL(result(KJob *)), this, SLOT(slotArchivingFinished(KJob *)));
397         connect(m_copyJob, SIGNAL(processedSize(KJob *, qulonglong)), this, SLOT(slotArchivingProgress(KJob *, qulonglong)));
398     }
399     if (firstPass) {
400         progressBar->setValue(0);
401         buttonBox->button(QDialogButtonBox::Apply)->setText(i18n("Abort"));
402     }
403     return true;
404 }
405
406 void ArchiveWidget::slotArchivingFinished(KJob *job)
407 {
408     if (job == NULL || job->error() == 0) {
409         if (slotStartArchiving(false)) {
410             // We still have files to archive
411             return;
412         }
413         else if (!compressed_archive->isChecked()) {
414             // Archiving finished
415             progressBar->setValue(100);
416             if (processProjectFile()) {
417                 icon_info->setPixmap(KIcon("dialog-ok").pixmap(16, 16));
418                 text_info->setText(i18n("Project was successfully archived."));
419             }
420             else {
421                 icon_info->setPixmap(KIcon("dialog-close").pixmap(16, 16));
422                 text_info->setText(i18n("There was an error processing project file"));
423             }
424         } else processProjectFile();
425     }
426     else {
427         m_copyJob = NULL;
428         icon_info->setPixmap(KIcon("dialog-close").pixmap(16, 16));
429         text_info->setText(i18n("There was an error while copying the files: %1", job->errorString()));
430     }
431     if (!compressed_archive->isChecked()) {
432         buttonBox->button(QDialogButtonBox::Apply)->setText(i18n("Archive"));
433         archive_url->setEnabled(true);
434         compressed_archive->setEnabled(true);
435         for (int i = 0; i < files_list->topLevelItemCount(); i++) {
436             files_list->topLevelItem(i)->setDisabled(false);
437             for (int j = 0; j < files_list->topLevelItem(i)->childCount(); j++)
438                 files_list->topLevelItem(i)->child(j)->setDisabled(false);        
439         }
440     }
441 }
442
443 void ArchiveWidget::slotArchivingProgress(KJob *, qulonglong size)
444 {
445     progressBar->setValue((int) 100 * size / m_requestedSize);
446 }
447
448
449 bool ArchiveWidget::processProjectFile()
450 {
451     KUrl destUrl;
452     QTreeWidgetItem *item;
453     for (int i = 0; i < files_list->topLevelItemCount(); i++) {
454         QTreeWidgetItem *parentItem = files_list->topLevelItem(i);
455         if (parentItem->childCount() > 0) {
456             destUrl = KUrl(archive_url->url().path(KUrl::AddTrailingSlash) + parentItem->data(0, Qt::UserRole).toString());
457             bool isSlideshow = parentItem->data(0, Qt::UserRole).toString() == "slideshows";
458             for (int j = 0; j < parentItem->childCount(); j++) {
459                 item = parentItem->child(j);
460                 KUrl src(item->text(0));
461                 KUrl dest = destUrl;
462                 if (isSlideshow) {
463                     dest = KUrl(destUrl.path(KUrl::AddTrailingSlash) + item->data(0, Qt::UserRole).toString() + "/" + src.fileName());
464                 }
465                 else if (item->data(0, Qt::UserRole).isNull()) {
466                     dest.addPath(src.fileName());
467                 }
468                 else {
469                     dest.addPath(item->data(0, Qt::UserRole).toString());
470                 }
471                 m_replacementList.insert(src, dest);
472             }
473         }
474     }
475     
476     // process kdenlive producers           
477     QDomElement mlt = m_doc.documentElement();
478     QString root = mlt.attribute("root") + "/";
479     mlt.setAttribute("root", archive_url->url().path(KUrl::RemoveTrailingSlash));
480     QDomNodeList prods = mlt.elementsByTagName("kdenlive_producer");
481     for (int i = 0; i < prods.count(); i++) {
482         QDomElement e = prods.item(i).toElement();
483         if (e.isNull()) continue;
484         if (e.hasAttribute("resource")) {
485             KUrl src(e.attribute("resource"));
486             KUrl dest = m_replacementList.value(src);
487             if (!dest.isEmpty()) e.setAttribute("resource", dest.path());
488         }
489         if (e.hasAttribute("proxy") && e.attribute("proxy") != "-") {
490             KUrl src(e.attribute("proxy"));
491             KUrl dest = m_replacementList.value(src);
492             if (!dest.isEmpty()) e.setAttribute("proxy", dest.path());
493         }
494     }
495
496     // process mlt producers
497     prods = mlt.elementsByTagName("producer");
498     for (int i = 0; i < prods.count(); i++) {
499         QDomElement e = prods.item(i).toElement();
500         if (e.isNull()) continue;
501         QString src = EffectsList::property(e, "resource");
502         if (!src.isEmpty()) {
503             if (!src.startsWith('/')) src.prepend(root);
504             KUrl srcUrl(src);
505             KUrl dest = m_replacementList.value(src);
506             if (!dest.isEmpty()) EffectsList::setProperty(e, "resource", dest.path());
507         }
508     }
509
510     // process mlt transitions (for luma files)
511     prods = mlt.elementsByTagName("transition");
512     QString attribute;
513     for (int i = 0; i < prods.count(); i++) {
514         QDomElement e = prods.item(i).toElement();
515         if (e.isNull()) continue;
516         attribute = "resource";
517         QString src = EffectsList::property(e, attribute);
518         if (src.isEmpty()) attribute = "luma";
519         src = EffectsList::property(e, attribute);
520         if (!src.isEmpty()) {
521             if (!src.startsWith('/')) src.prepend(root);
522             KUrl srcUrl(src);
523             KUrl dest = m_replacementList.value(src);
524             if (!dest.isEmpty()) EffectsList::setProperty(e, attribute, dest.path());
525         }
526     }
527
528     bool isArchive = compressed_archive->isChecked();
529     if (isArchive) {
530         m_temp = new KTemporaryFile;
531         if (!m_temp->open()) KMessageBox::error(this, i18n("Cannot create temporary file"));
532         m_temp->write(m_doc.toString().toUtf8());
533         m_temp->close();
534         m_archiveThread = QtConcurrent::run(this, &ArchiveWidget::createArchive);
535         return true;
536     }
537     
538     QString path = archive_url->url().path(KUrl::AddTrailingSlash) + m_name + ".kdenlive";
539     QFile file(path);
540     if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
541         kWarning() << "//////  ERROR writing to file: " << path;
542         KMessageBox::error(this, i18n("Cannot write to file %1", path));
543         return false;
544     }
545
546     file.write(m_doc.toString().toUtf8());
547     if (file.error() != QFile::NoError) {
548         KMessageBox::error(this, i18n("Cannot write to file %1", path));
549         file.close();
550         return false;
551     }
552     file.close();
553     return true;
554 }
555
556 void ArchiveWidget::createArchive()
557 {
558     QFileInfo dirInfo(archive_url->url().path());
559     QString user = dirInfo.owner();
560     QString group = dirInfo.group();
561     KTar archive(archive_url->url().path(KUrl::AddTrailingSlash) + m_name + ".tar.gz", "application/x-gzip");
562     archive.open( QIODevice::WriteOnly );
563
564     // Create folders
565     foreach(const QString &path, m_foldersList) {
566         archive.writeDir(path, user, group);
567     }
568
569     // Add files
570     int ix = 0;
571     QMapIterator<QString, QString> i(m_filesList);
572     while (i.hasNext()) {
573         i.next();
574         archive.addLocalFile(i.key(), i.value());
575         emit archiveProgress((int) 100 * ix / m_filesList.count());
576         ix++;
577     }
578
579     // Add project file
580     archive.addLocalFile(m_temp->fileName(), m_name + ".kdenlive");
581     bool result = archive.close();
582     delete m_temp;
583     emit archivingFinished(result);
584 }
585
586 void ArchiveWidget::slotArchivingFinished(bool result)
587 {
588     if (result) {
589         icon_info->setPixmap(KIcon("dialog-ok").pixmap(16, 16));
590         text_info->setText(i18n("Project was successfully archived."));
591     }
592     else {
593         icon_info->setPixmap(KIcon("dialog-close").pixmap(16, 16));
594         text_info->setText(i18n("There was an error processing project file"));
595     }
596     progressBar->setValue(100);
597     buttonBox->button(QDialogButtonBox::Apply)->setText(i18n("Archive"));
598     archive_url->setEnabled(true);
599     compressed_archive->setEnabled(true);
600     for (int i = 0; i < files_list->topLevelItemCount(); i++) {
601         files_list->topLevelItem(i)->setDisabled(false);
602         for (int j = 0; j < files_list->topLevelItem(i)->childCount(); j++)
603             files_list->topLevelItem(i)->child(j)->setDisabled(false);
604     }
605 }
606
607 void ArchiveWidget::slotArchivingProgress(int p)
608 {
609     progressBar->setValue(p);
610 }