1 /***************************************************************************
2 * Copyright (C) 2011 by Jean-Baptiste Mardelle (jb@kdenlive.org) *
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. *
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. *
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 ***************************************************************************/
21 #include "archivewidget.h"
22 #include "titlewidget.h"
25 #include <KDiskFreeSpaceInfo>
26 #include <KUrlRequester>
27 #include <KFileDialog>
28 #include <KMessageBox>
30 #include <KIO/NetAccess>
33 #include <KApplication>
34 #include <kio/directorysizejob.h>
35 #if KDE_IS_VERSION(4,7,0)
36 #include <KMessageWidget>
39 #include <QTreeWidget>
40 #include <QtConcurrentRun>
41 #include "projectsettings.h"
44 ArchiveWidget::ArchiveWidget(QString projectName, QDomDocument doc, QList <DocClipBase*> list, QStringList luma_list, QWidget * parent) :
48 m_name(projectName.section('.', 0, -2)),
50 m_abortArchive(false),
52 m_extractArchive(NULL),
55 setAttribute(Qt::WA_DeleteOnClose);
57 setWindowTitle(i18n("Archive Project"));
58 archive_url->setUrl(KUrl(QDir::homePath()));
59 connect(archive_url, SIGNAL(textChanged (const QString &)), this, SLOT(slotCheckSpace()));
60 connect(this, SIGNAL(archivingFinished(bool)), this, SLOT(slotArchivingFinished(bool)));
61 connect(this, SIGNAL(archiveProgress(int)), this, SLOT(slotArchivingProgress(int)));
64 QTreeWidgetItem *videos = new QTreeWidgetItem(files_list, QStringList() << i18n("Video clips"));
65 videos->setIcon(0, KIcon("video-x-generic"));
66 videos->setData(0, Qt::UserRole, "videos");
67 videos->setExpanded(false);
68 QTreeWidgetItem *sounds = new QTreeWidgetItem(files_list, QStringList() << i18n("Audio clips"));
69 sounds->setIcon(0, KIcon("audio-x-generic"));
70 sounds->setData(0, Qt::UserRole, "sounds");
71 sounds->setExpanded(false);
72 QTreeWidgetItem *images = new QTreeWidgetItem(files_list, QStringList() << i18n("Image clips"));
73 images->setIcon(0, KIcon("image-x-generic"));
74 images->setData(0, Qt::UserRole, "images");
75 images->setExpanded(false);
76 QTreeWidgetItem *slideshows = new QTreeWidgetItem(files_list, QStringList() << i18n("Slideshow clips"));
77 slideshows->setIcon(0, KIcon("image-x-generic"));
78 slideshows->setData(0, Qt::UserRole, "slideshows");
79 slideshows->setExpanded(false);
80 QTreeWidgetItem *texts = new QTreeWidgetItem(files_list, QStringList() << i18n("Text clips"));
81 texts->setIcon(0, KIcon("text-plain"));
82 texts->setData(0, Qt::UserRole, "texts");
83 texts->setExpanded(false);
84 QTreeWidgetItem *playlists = new QTreeWidgetItem(files_list, QStringList() << i18n("Playlist clips"));
85 playlists->setIcon(0, KIcon("video-mlt-playlist"));
86 playlists->setData(0, Qt::UserRole, "playlist");
87 playlists->setExpanded(false);
88 QTreeWidgetItem *others = new QTreeWidgetItem(files_list, QStringList() << i18n("Other clips"));
89 others->setIcon(0, KIcon("unknown"));
90 others->setData(0, Qt::UserRole, "others");
91 others->setExpanded(false);
92 QTreeWidgetItem *lumas = new QTreeWidgetItem(files_list, QStringList() << i18n("Luma files"));
93 lumas->setIcon(0, KIcon("image-x-generic"));
94 lumas->setData(0, Qt::UserRole, "lumas");
95 lumas->setExpanded(false);
97 QTreeWidgetItem *proxies = new QTreeWidgetItem(files_list, QStringList() << i18n("Proxy clips"));
98 proxies->setIcon(0, KIcon("video-x-generic"));
99 proxies->setData(0, Qt::UserRole, "proxy");
100 proxies->setExpanded(false);
103 QStringList allFonts;
105 QStringList fileNames;
106 generateItems(lumas, luma_list);
108 QStringList slideUrls;
109 QStringList audioUrls;
110 QStringList videoUrls;
111 QStringList imageUrls;
112 QStringList otherUrls;
113 QStringList playlistUrls;
114 QStringList proxyUrls;
116 for (int i = 0; i < list.count(); i++) {
117 DocClipBase *clip = list.at(i);
118 CLIPTYPE t = clip->clipType();
119 if (t == SLIDESHOW) {
120 KUrl slideUrl = clip->fileURL();
121 //TODO: Slideshow files
122 slideUrls << slideUrl.path();
124 else if (t == IMAGE) imageUrls << clip->fileURL().path();
125 else if (t == TEXT) {
126 QStringList imagefiles = TitleWidget::extractImageList(clip->getProperty("xmldata"));
127 QStringList fonts = TitleWidget::extractFontList(clip->getProperty("xmldata"));
128 imageUrls << imagefiles;
130 } else if (t == PLAYLIST) {
131 playlistUrls << clip->fileURL().path();
132 QStringList files = ProjectSettings::extractPlaylistUrls(clip->fileURL().path());
135 else if (!clip->fileURL().isEmpty()) {
136 if (t == AUDIO) audioUrls << clip->fileURL().path();
138 videoUrls << clip->fileURL().path();
139 // Check if we have a proxy
140 QString proxy = clip->getProperty("proxy");
141 if (!proxy.isEmpty() && proxy != "-" && QFile::exists(proxy)) proxyUrls << proxy;
146 generateItems(sounds, audioUrls);
147 generateItems(videos, videoUrls);
148 generateItems(images, imageUrls);
149 generateItems(slideshows, slideUrls);
150 generateItems(playlists, playlistUrls);
151 generateItems(others, otherUrls);
152 generateItems(proxies, proxyUrls);
154 allFonts.removeDuplicates();
156 #if KDE_IS_VERSION(4,7,0)
157 m_infoMessage = new KMessageWidget(this);
158 QVBoxLayout *s = static_cast <QVBoxLayout*> (layout());
159 s->insertWidget(5, m_infoMessage);
160 m_infoMessage->setCloseButtonVisible(false);
161 m_infoMessage->setWordWrap(true);
162 m_infoMessage->hide();
165 // missing clips, warn user
166 if (m_missingClips > 0) {
167 QString infoText = i18np("You have %1 missing clip in your project.", "You have %1 missing clips in your project.", m_missingClips);
168 #if KDE_IS_VERSION(4,7,0)
169 m_infoMessage->setMessageType(KMessageWidget::Warning);
170 m_infoMessage->setText(infoText);
171 m_infoMessage->animatedShow();
173 KMessageBox::sorry(this, infoText);
179 // Hide unused categories, add item count
181 for (int i = 0; i < files_list->topLevelItemCount(); i++) {
182 QTreeWidgetItem *parentItem = files_list->topLevelItem(i);
183 int items = parentItem->childCount();
185 files_list->topLevelItem(i)->setHidden(true);
188 if (parentItem->data(0, Qt::UserRole).toString() == "slideshows")
190 // Special case: slideshows contain several files
191 for (int j = 0; j < items; j++) {
192 total += parentItem->child(j)->data(0, Qt::UserRole + 1).toStringList().count();
196 parentItem->setText(0, files_list->topLevelItem(i)->text(0) + " " + i18np("(%1 item)", "(%1 items)", items));
199 if (m_name.isEmpty()) m_name = i18n("Untitled");
200 compressed_archive->setText(compressed_archive->text() + " (" + m_name + ".tar.gz)");
201 project_files->setText(i18np("%1 file to archive, requires %2", "%1 files to archive, requires %2", total, KIO::convertSize(m_requestedSize)));
202 buttonBox->button(QDialogButtonBox::Apply)->setText(i18n("Archive"));
203 connect(buttonBox->button(QDialogButtonBox::Apply), SIGNAL(clicked()), this, SLOT(slotStartArchiving()));
204 buttonBox->button(QDialogButtonBox::Apply)->setEnabled(false);
209 // Constructor for extract widget
210 ArchiveWidget::ArchiveWidget(const KUrl &url, QWidget * parent):
215 //setAttribute(Qt::WA_DeleteOnClose);
218 connect(this, SIGNAL(extractingFinished()), this, SLOT(slotExtractingFinished()));
219 connect(this, SIGNAL(showMessage(const QString &, const QString &)), this, SLOT(slotDisplayMessage(const QString &, const QString &)));
221 compressed_archive->setHidden(true);
222 project_files->setHidden(true);
223 files_list->setHidden(true);
224 label->setText(i18n("Extract to"));
225 setWindowTitle(i18n("Open Archived Project"));
226 archive_url->setUrl(KUrl(QDir::homePath()));
227 buttonBox->button(QDialogButtonBox::Apply)->setText(i18n("Extract"));
228 connect(buttonBox->button(QDialogButtonBox::Apply), SIGNAL(clicked()), this, SLOT(slotStartExtracting()));
229 buttonBox->button(QDialogButtonBox::Apply)->setEnabled(true);
231 m_archiveThread = QtConcurrent::run(this, &ArchiveWidget::openArchiveForExtraction);
235 ArchiveWidget::~ArchiveWidget()
237 if (m_extractArchive) delete m_extractArchive;
240 void ArchiveWidget::slotDisplayMessage(const QString &icon, const QString &text)
242 icon_info->setPixmap(KIcon(icon).pixmap(16, 16));
243 text_info->setText(text);
246 void ArchiveWidget::slotJobResult(bool success, const QString &text)
248 #if KDE_IS_VERSION(4,7,0)
249 m_infoMessage->setMessageType(success ? KMessageWidget::Positive : KMessageWidget::Warning);
250 m_infoMessage->setText(text);
251 m_infoMessage->animatedShow();
253 if (success) icon_info->setPixmap(KIcon("dialog-ok").pixmap(16, 16));
254 else icon_info->setPixmap(KIcon("dialog-close").pixmap(16, 16));
255 text_info->setText(text);
259 void ArchiveWidget::openArchiveForExtraction()
261 emit showMessage("system-run", i18n("Opening archive..."));
262 m_extractArchive = new KTar(m_extractUrl.path());
263 if (!m_extractArchive->isOpen() && !m_extractArchive->open( QIODevice::ReadOnly )) {
264 emit showMessage("dialog-close", i18n("Cannot open archive file:\n %1", m_extractUrl.path()));
265 groupBox->setEnabled(false);
269 // Check that it is a kdenlive project archive
270 bool isProjectArchive = false;
271 QStringList files = m_extractArchive->directory()->entries();
272 for (int i = 0; i < files.count(); i++) {
273 if (files.at(i).endsWith(".kdenlive")) {
274 m_projectName = files.at(i);
275 isProjectArchive = true;
280 if (!isProjectArchive) {
281 emit showMessage("dialog-close", i18n("File %1\n is not an archived Kdenlive project", m_extractUrl.path()));
282 groupBox->setEnabled(false);
285 buttonBox->button(QDialogButtonBox::Apply)->setEnabled(true);
286 emit showMessage("dialog-ok", i18n("Ready"));
289 void ArchiveWidget::done ( int r )
291 if (closeAccepted()) QDialog::done(r);
294 void ArchiveWidget::closeEvent ( QCloseEvent * e )
297 if (closeAccepted()) e->accept();
302 bool ArchiveWidget::closeAccepted()
304 if (!m_extractMode && !archive_url->isEnabled()) {
305 // Archiving in progress, should we stop?
306 if (KMessageBox::warningContinueCancel(this, i18n("Archiving in progress, do you want to stop it?"), i18n("Stop Archiving"), KGuiItem(i18n("Stop Archiving"))) != KMessageBox::Continue) {
309 if (m_copyJob) m_copyJob->kill();
315 void ArchiveWidget::generateItems(QTreeWidgetItem *parentItem, QStringList items)
317 QStringList filesList;
320 bool isSlideshow = parentItem->data(0, Qt::UserRole).toString() == "slideshows";
321 foreach(const QString & file, items) {
322 QTreeWidgetItem *item = new QTreeWidgetItem(parentItem, QStringList() << file);
323 fileName = KUrl(file).fileName();
325 // we store each slideshow in a separate subdirectory
326 item->setData(0, Qt::UserRole, ix);
329 QDir dir(slideUrl.directory(KUrl::AppendTrailingSlash));
330 if (slideUrl.fileName().startsWith(".all.")) {
331 // mimetype slideshow (for example *.png)
334 // TODO: improve jpeg image detection with extension like jpeg, requires change in MLT image producers
335 filters << "*." + slideUrl.fileName().section('.', -1);
336 dir.setNameFilters(filters);
337 QFileInfoList resultList = dir.entryInfoList(QDir::Files);
338 QStringList slideImages;
339 for (int i = 0; i < resultList.count(); i++) {
340 m_requestedSize += resultList.at(i).size();
341 slideImages << resultList.at(i).absoluteFilePath();
343 item->setData(0, Qt::UserRole + 1, slideImages);
346 // pattern url (like clip%.3d.png)
347 QStringList result = dir.entryList(QDir::Files);
348 QString filter = slideUrl.fileName();
349 QString ext = filter.section('.', -1);
350 filter = filter.section('%', 0, -2);
351 QString regexp = "^" + filter + "\\d+\\." + ext + "$";
353 QStringList slideImages;
354 QString directory = dir.absolutePath();
355 if (!directory.endsWith('/')) directory.append('/');
356 foreach(const QString & path, result) {
357 if (rx.exactMatch(path)) {
358 m_requestedSize += QFileInfo(directory + path).size();
359 slideImages << directory + path;
362 item->setData(0, Qt::UserRole + 1, slideImages);
365 else if (filesList.contains(fileName)) {
366 // we have 2 files with same name
368 QString newFileName = fileName.section('.', 0, -2) + "_" + QString::number(ix) + "." + fileName.section('.', -1);
369 while (filesList.contains(newFileName)) {
371 newFileName = fileName.section('.', 0, -2) + "_" + QString::number(ix) + "." + fileName.section('.', -1);
373 fileName = newFileName;
374 item->setData(0, Qt::UserRole, fileName);
377 qint64 fileSize = QFileInfo(file).size();
379 item->setIcon(0, KIcon("edit-delete"));
382 else m_requestedSize += fileSize;
383 filesList << fileName;
388 void ArchiveWidget::slotCheckSpace()
390 KDiskFreeSpaceInfo inf = KDiskFreeSpaceInfo::freeSpaceInfo( archive_url->url().path());
391 KIO::filesize_t freeSize = inf.available();
392 if (freeSize > m_requestedSize) {
394 buttonBox->button(QDialogButtonBox::Apply)->setEnabled(true);
395 slotDisplayMessage("dialog-ok", i18n("Available space on drive: %1", KIO::convertSize(freeSize)));
398 buttonBox->button(QDialogButtonBox::Apply)->setEnabled(false);
399 slotDisplayMessage("dialog-close", i18n("Not enough space on drive, free space: %1", KIO::convertSize(freeSize)));
403 bool ArchiveWidget::slotStartArchiving(bool firstPass)
405 if (firstPass && (m_copyJob || m_archiveThread.isRunning())) {
406 // archiving in progress, abort
407 if (m_copyJob) m_copyJob->kill(KJob::EmitResult);
408 m_abortArchive = true;
411 bool isArchive = compressed_archive->isChecked();
412 if (!firstPass) m_copyJob = NULL;
415 m_abortArchive = false;
416 m_duplicateFiles.clear();
417 m_replacementList.clear();
418 m_foldersList.clear();
420 slotDisplayMessage("system-run", i18n("Archiving..."));
422 archive_url->setEnabled(false);
423 compressed_archive->setEnabled(false);
428 QTreeWidgetItem *parentItem;
429 bool isSlideshow = false;
430 for (int i = 0; i < files_list->topLevelItemCount(); i++) {
431 parentItem = files_list->topLevelItem(i);
432 if (parentItem->childCount() > 0 && !parentItem->isDisabled()) {
433 if (parentItem->data(0, Qt::UserRole).toString() == "slideshows") {
434 KUrl slideFolder(archive_url->url().path(KUrl::AddTrailingSlash) + "slideshows");
435 if (isArchive) m_foldersList.append("slideshows");
436 else KIO::NetAccess::mkdir(slideFolder, this);
439 files_list->setCurrentItem(parentItem);
440 if (!isSlideshow) parentItem->setDisabled(true);
441 destPath = parentItem->data(0, Qt::UserRole).toString() + "/";
442 destUrl = KUrl(archive_url->url().path(KUrl::AddTrailingSlash) + destPath);
443 QTreeWidgetItem *item;
444 for (int j = 0; j < parentItem->childCount(); j++) {
445 item = parentItem->child(j);
446 // Special case: slideshows
448 if (item->isDisabled()) {
451 destPath.append(item->data(0, Qt::UserRole).toString() + "/");
452 destUrl = KUrl(archive_url->url().path(KUrl::AddTrailingSlash) + destPath);
453 QStringList srcFiles = item->data(0, Qt::UserRole + 1).toStringList();
454 for (int k = 0; k < srcFiles.count(); k++) {
455 files << KUrl(srcFiles.at(k));
457 item->setDisabled(true);
458 if (parentItem->indexOfChild(item) == parentItem->childCount() - 1) {
459 // We have processed all slideshows
460 parentItem->setDisabled(true);
464 else if (item->data(0, Qt::UserRole).isNull()) {
465 files << KUrl(item->text(0));
468 // We must rename the destination file, since another file with same name exists
469 //TODO: monitor progress
471 m_filesList.insert(item->text(0), destPath + item->data(0, Qt::UserRole).toString());
473 else m_duplicateFiles.insert(KUrl(item->text(0)), KUrl(destUrl.path(KUrl::AddTrailingSlash) + item->data(0, Qt::UserRole).toString()));
480 if (destPath.isEmpty()) {
481 if (m_duplicateFiles.isEmpty()) return false;
482 QMapIterator<KUrl, KUrl> i(m_duplicateFiles);
485 KUrl startJobSrc = i.key();
486 KUrl startJobDst = i.value();
487 m_duplicateFiles.remove(startJobSrc);
488 KIO::CopyJob *job = KIO::copyAs(startJobSrc, startJobDst, KIO::HideProgressInfo);
489 connect(job, SIGNAL(result(KJob *)), this, SLOT(slotArchivingFinished(KJob *)));
490 connect(job, SIGNAL(processedSize(KJob *, qulonglong)), this, SLOT(slotArchivingProgress(KJob *, qulonglong)));
496 m_foldersList.append(destPath);
497 for (int i = 0; i < files.count(); i++) {
498 m_filesList.insert(files.at(i).path(), destPath + files.at(i).fileName());
500 slotArchivingFinished();
502 else if (files.isEmpty()) {
503 slotStartArchiving(false);
506 KIO::NetAccess::mkdir(destUrl, this);
507 m_copyJob = KIO::copy (files, destUrl, KIO::HideProgressInfo);
508 connect(m_copyJob, SIGNAL(result(KJob *)), this, SLOT(slotArchivingFinished(KJob *)));
509 connect(m_copyJob, SIGNAL(processedSize(KJob *, qulonglong)), this, SLOT(slotArchivingProgress(KJob *, qulonglong)));
512 progressBar->setValue(0);
513 buttonBox->button(QDialogButtonBox::Apply)->setText(i18n("Abort"));
518 void ArchiveWidget::slotArchivingFinished(KJob *job)
520 if (job == NULL || job->error() == 0) {
521 if (slotStartArchiving(false)) {
522 // We still have files to archive
525 else if (!compressed_archive->isChecked()) {
526 // Archiving finished
527 progressBar->setValue(100);
528 if (processProjectFile()) {
529 slotJobResult(true, i18n("Project was successfully archived."));
532 slotJobResult(false, i18n("There was an error processing project file"));
534 } else processProjectFile();
538 slotJobResult(false, i18n("There was an error while copying the files: %1", job->errorString()));
540 if (!compressed_archive->isChecked()) {
541 buttonBox->button(QDialogButtonBox::Apply)->setText(i18n("Archive"));
542 archive_url->setEnabled(true);
543 compressed_archive->setEnabled(true);
544 for (int i = 0; i < files_list->topLevelItemCount(); i++) {
545 files_list->topLevelItem(i)->setDisabled(false);
546 for (int j = 0; j < files_list->topLevelItem(i)->childCount(); j++)
547 files_list->topLevelItem(i)->child(j)->setDisabled(false);
552 void ArchiveWidget::slotArchivingProgress(KJob *, qulonglong size)
554 progressBar->setValue((int) 100 * size / m_requestedSize);
558 bool ArchiveWidget::processProjectFile()
561 QTreeWidgetItem *item;
562 bool isArchive = compressed_archive->isChecked();
564 for (int i = 0; i < files_list->topLevelItemCount(); i++) {
565 QTreeWidgetItem *parentItem = files_list->topLevelItem(i);
566 if (parentItem->childCount() > 0) {
567 destUrl = KUrl(archive_url->url().path(KUrl::AddTrailingSlash) + parentItem->data(0, Qt::UserRole).toString());
568 bool isSlideshow = parentItem->data(0, Qt::UserRole).toString() == "slideshows";
569 for (int j = 0; j < parentItem->childCount(); j++) {
570 item = parentItem->child(j);
571 KUrl src(item->text(0));
574 dest = KUrl(destUrl.path(KUrl::AddTrailingSlash) + item->data(0, Qt::UserRole).toString() + "/" + src.fileName());
576 else if (item->data(0, Qt::UserRole).isNull()) {
577 dest.addPath(src.fileName());
580 dest.addPath(item->data(0, Qt::UserRole).toString());
582 m_replacementList.insert(src, dest);
587 QDomElement mlt = m_doc.documentElement();
588 QString root = mlt.attribute("root") + "/";
590 // Adjust global settings
592 if (isArchive) basePath = "$CURRENTPATH";
593 else basePath = archive_url->url().path(KUrl::RemoveTrailingSlash);
594 mlt.setAttribute("root", basePath);
595 QDomElement project = mlt.firstChildElement("kdenlivedoc");
596 project.setAttribute("projectfolder", basePath);
598 // process kdenlive producers
599 QDomNodeList prods = mlt.elementsByTagName("kdenlive_producer");
600 for (int i = 0; i < prods.count(); i++) {
601 QDomElement e = prods.item(i).toElement();
602 if (e.isNull()) continue;
603 if (e.hasAttribute("resource")) {
604 KUrl src(e.attribute("resource"));
605 KUrl dest = m_replacementList.value(src);
606 if (!dest.isEmpty()) e.setAttribute("resource", dest.path());
608 if (e.hasAttribute("proxy") && e.attribute("proxy") != "-") {
609 KUrl src(e.attribute("proxy"));
610 KUrl dest = m_replacementList.value(src);
611 if (!dest.isEmpty()) e.setAttribute("proxy", dest.path());
615 // process mlt producers
616 prods = mlt.elementsByTagName("producer");
617 for (int i = 0; i < prods.count(); i++) {
618 QDomElement e = prods.item(i).toElement();
619 if (e.isNull()) continue;
620 QString src = EffectsList::property(e, "resource");
621 if (!src.isEmpty()) {
622 if (!src.startsWith('/')) src.prepend(root);
624 KUrl dest = m_replacementList.value(src);
625 if (!dest.isEmpty()) EffectsList::setProperty(e, "resource", dest.path());
629 // process mlt transitions (for luma files)
630 prods = mlt.elementsByTagName("transition");
632 for (int i = 0; i < prods.count(); i++) {
633 QDomElement e = prods.item(i).toElement();
634 if (e.isNull()) continue;
635 attribute = "resource";
636 QString src = EffectsList::property(e, attribute);
637 if (src.isEmpty()) attribute = "luma";
638 src = EffectsList::property(e, attribute);
639 if (!src.isEmpty()) {
640 if (!src.startsWith('/')) src.prepend(root);
642 KUrl dest = m_replacementList.value(src);
643 if (!dest.isEmpty()) EffectsList::setProperty(e, attribute, dest.path());
647 QString playList = m_doc.toString();
649 QString startString("\"");
650 startString.append(archive_url->url().path(KUrl::RemoveTrailingSlash));
651 QString endString("\"");
652 endString.append(basePath);
653 playList.replace(startString, endString);
654 startString = ">" + archive_url->url().path(KUrl::RemoveTrailingSlash);
655 endString = ">" + basePath;
656 playList.replace(startString, endString);
660 m_temp = new KTemporaryFile;
661 if (!m_temp->open()) KMessageBox::error(this, i18n("Cannot create temporary file"));
662 m_temp->write(playList.toUtf8());
664 m_archiveThread = QtConcurrent::run(this, &ArchiveWidget::createArchive);
668 QString path = archive_url->url().path(KUrl::AddTrailingSlash) + m_name + ".kdenlive";
670 if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
671 kWarning() << "////// ERROR writing to file: " << path;
672 KMessageBox::error(this, i18n("Cannot write to file %1", path));
676 file.write(m_doc.toString().toUtf8());
677 if (file.error() != QFile::NoError) {
678 KMessageBox::error(this, i18n("Cannot write to file %1", path));
686 void ArchiveWidget::createArchive()
688 QFileInfo dirInfo(archive_url->url().path());
689 QString user = dirInfo.owner();
690 QString group = dirInfo.group();
691 KTar archive(archive_url->url().path(KUrl::AddTrailingSlash) + m_name + ".tar.gz", "application/x-gzip");
692 archive.open( QIODevice::WriteOnly );
695 foreach(const QString &path, m_foldersList) {
696 archive.writeDir(path, user, group);
701 QMapIterator<QString, QString> i(m_filesList);
702 while (i.hasNext()) {
704 archive.addLocalFile(i.key(), i.value());
705 emit archiveProgress((int) 100 * ix / m_filesList.count());
710 archive.addLocalFile(m_temp->fileName(), m_name + ".kdenlive");
711 bool result = archive.close();
713 emit archivingFinished(result);
716 void ArchiveWidget::slotArchivingFinished(bool result)
719 slotJobResult(true, i18n("Project was successfully archived."));
720 buttonBox->button(QDialogButtonBox::Apply)->setEnabled(false);
723 slotJobResult(false, i18n("There was an error processing project file"));
725 progressBar->setValue(100);
726 buttonBox->button(QDialogButtonBox::Apply)->setText(i18n("Archive"));
727 archive_url->setEnabled(true);
728 compressed_archive->setEnabled(true);
729 for (int i = 0; i < files_list->topLevelItemCount(); i++) {
730 files_list->topLevelItem(i)->setDisabled(false);
731 for (int j = 0; j < files_list->topLevelItem(i)->childCount(); j++)
732 files_list->topLevelItem(i)->child(j)->setDisabled(false);
736 void ArchiveWidget::slotArchivingProgress(int p)
738 progressBar->setValue(p);
741 void ArchiveWidget::slotStartExtracting()
743 if (m_archiveThread.isRunning()) {
744 //TODO: abort extracting
747 QFileInfo f(m_extractUrl.path());
748 m_requestedSize = f.size();
749 KIO::NetAccess::mkdir(archive_url->url().path(KUrl::RemoveTrailingSlash), this);
750 slotDisplayMessage("system-run", i18n("Extracting..."));
751 buttonBox->button(QDialogButtonBox::Apply)->setText(i18n("Abort"));
752 m_progressTimer = new QTimer;
753 m_progressTimer->setInterval(800);
754 m_progressTimer->setSingleShot(false);
755 connect(m_progressTimer, SIGNAL(timeout()), this, SLOT(slotExtractProgress()));
756 m_archiveThread = QtConcurrent::run(this, &ArchiveWidget::doExtracting);
757 m_progressTimer->start();
760 void ArchiveWidget::slotExtractProgress()
762 KIO::DirectorySizeJob *job = KIO::directorySize(archive_url->url());
763 connect(job, SIGNAL(result(KJob*)), this, SLOT(slotGotProgress(KJob*)));
766 void ArchiveWidget::slotGotProgress(KJob* job)
769 KIO::DirectorySizeJob *j = static_cast <KIO::DirectorySizeJob *>(job);
770 progressBar->setValue((int) 100 * j->totalSize() / m_requestedSize);
775 void ArchiveWidget::doExtracting()
777 m_extractArchive->directory()->copyTo(archive_url->url().path(KUrl::AddTrailingSlash));
778 m_extractArchive->close();
779 emit extractingFinished();
782 QString ArchiveWidget::extractedProjectFile()
784 return archive_url->url().path(KUrl::AddTrailingSlash) + m_projectName;
787 void ArchiveWidget::slotExtractingFinished()
789 m_progressTimer->stop();
790 delete m_progressTimer;
791 // Process project file
792 QFile file(extractedProjectFile());
794 if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
798 QString playList = file.readAll();
800 if (playList.isEmpty()) {
804 playList.replace("$CURRENTPATH", archive_url->url().path(KUrl::RemoveTrailingSlash));
805 if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
806 kWarning() << "////// ERROR writing to file: ";
810 file.write(playList.toUtf8());
811 if (file.error() != QFile::NoError) {
819 KMessageBox::sorry(kapp->activeWindow(), i18n("Cannot open project file %1", extractedProjectFile()), i18n("Cannot open file"));