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>
29 #include <KApplication>
30 #include <KIO/NetAccess>
33 #include <QTreeWidget>
34 #include "projectsettings.h"
37 ArchiveWidget::ArchiveWidget(QDomDocument doc, QList <DocClipBase*> list, QStringList luma_list, QWidget * parent) :
44 setWindowTitle(i18n("Archive Project"));
45 archive_url->setUrl(KUrl(QDir::homePath()));
46 connect(archive_url, SIGNAL(textChanged (const QString &)), this, SLOT(slotCheckSpace()));
50 QTreeWidgetItem *videos = new QTreeWidgetItem(files_list, QStringList() << i18n("Video clips"));
51 videos->setIcon(0, KIcon("video-x-generic"));
52 videos->setData(0, Qt::UserRole, i18n("videos"));
53 videos->setExpanded(false);
54 QTreeWidgetItem *sounds = new QTreeWidgetItem(files_list, QStringList() << i18n("Audio clips"));
55 sounds->setIcon(0, KIcon("audio-x-generic"));
56 sounds->setData(0, Qt::UserRole, i18n("sounds"));
57 sounds->setExpanded(false);
58 QTreeWidgetItem *images = new QTreeWidgetItem(files_list, QStringList() << i18n("Image clips"));
59 images->setIcon(0, KIcon("image-x-generic"));
60 images->setData(0, Qt::UserRole, i18n("images"));
61 images->setExpanded(false);
62 QTreeWidgetItem *slideshows = new QTreeWidgetItem(files_list, QStringList() << i18n("Slideshow clips"));
63 slideshows->setIcon(0, KIcon("image-x-generic"));
64 slideshows->setData(0, Qt::UserRole, i18n("slideshows"));
65 slideshows->setExpanded(false);
66 QTreeWidgetItem *texts = new QTreeWidgetItem(files_list, QStringList() << i18n("Text clips"));
67 texts->setIcon(0, KIcon("text-plain"));
68 texts->setData(0, Qt::UserRole, i18n("texts"));
69 texts->setExpanded(false);
70 QTreeWidgetItem *others = new QTreeWidgetItem(files_list, QStringList() << i18n("Other clips"));
71 others->setIcon(0, KIcon("unknown"));
72 others->setData(0, Qt::UserRole, i18n("others"));
73 others->setExpanded(false);
74 QTreeWidgetItem *lumas = new QTreeWidgetItem(files_list, QStringList() << i18n("Luma files"));
75 lumas->setIcon(0, KIcon("image-x-generic"));
76 lumas->setData(0, Qt::UserRole, i18n("lumas"));
77 lumas->setExpanded(false);
82 QStringList fileNames;
83 generateItems(lumas, luma_list);
85 QStringList slideUrls;
86 QStringList audioUrls;
87 QStringList videoUrls;
88 QStringList imageUrls;
89 QStringList otherUrls;
91 for (int i = 0; i < list.count(); i++) {
92 DocClipBase *clip = list.at(i);
93 if (clip->clipType() == SLIDESHOW) {
94 QStringList subfiles = ProjectSettings::extractSlideshowUrls(clip->fileURL());
95 foreach(const QString & file, subfiles) {
96 kDebug()<<"SLIDE: "<<file;
97 new QTreeWidgetItem(slideshows, QStringList() << file);
98 m_requestedSize += QFileInfo(file).size();
100 } else if (!clip->fileURL().isEmpty()) {
101 if (clip->clipType() == AUDIO) audioUrls << clip->fileURL().path();
102 else videoUrls << clip->fileURL().path();
104 if (clip->clipType() == TEXT) {
105 QStringList imagefiles = TitleWidget::extractImageList(clip->getProperty("xmldata"));
106 QStringList fonts = TitleWidget::extractFontList(clip->getProperty("xmldata"));
107 imageUrls << imagefiles;
109 } else if (clip->clipType() == PLAYLIST) {
110 QStringList files = ProjectSettings::extractPlaylistUrls(clip->fileURL().path());
115 generateItems(sounds, audioUrls);
116 generateItems(videos, videoUrls);
117 generateItems(images, imageUrls);
118 //generateItems(slideshows, slideUrls);
119 generateItems(others, otherUrls);
121 #if QT_VERSION >= 0x040500
122 allFonts.removeDuplicates();
127 // Hide unused categories, add item count
129 for (int i = 0; i < files_list->topLevelItemCount(); i++) {
130 int items = files_list->topLevelItem(i)->childCount();
132 files_list->topLevelItem(i)->setHidden(true);
136 files_list->topLevelItem(i)->setText(0, files_list->topLevelItem(i)->text(0) + " " + i18n("(%1 items)", items));
140 project_files->setText(i18n("%1 files to archive, requires %2", total, KIO::convertSize(m_requestedSize)));
141 buttonBox->button(QDialogButtonBox::Apply)->setText(i18n("Archive"));
142 connect(buttonBox->button(QDialogButtonBox::Apply), SIGNAL(clicked()), this, SLOT(slotStartArchiving()));
143 buttonBox->button(QDialogButtonBox::Apply)->setEnabled(false);
148 ArchiveWidget::~ArchiveWidget()
152 void ArchiveWidget::generateItems(QTreeWidgetItem *parentItem, QStringList items)
154 QStringList filesList;
156 foreach(const QString & file, items) {
157 QTreeWidgetItem *item = new QTreeWidgetItem(parentItem, QStringList() << file);
158 fileName = KUrl(file).fileName();
159 if (filesList.contains(fileName)) {
160 // we have 2 files with same name
162 QString newFileName = fileName.section('.', 0, -2) + "_" + QString::number(ix) + "." + fileName.section('.', -1);
163 while (filesList.contains(newFileName)) {
165 newFileName = fileName.section('.', 0, -2) + "_" + QString::number(ix) + "." + fileName.section('.', -1);
167 fileName = newFileName;
168 item->setData(0, Qt::UserRole, fileName);
170 filesList << fileName;
171 m_requestedSize += QFileInfo(file).size();
175 void ArchiveWidget::slotCheckSpace()
177 KDiskFreeSpaceInfo inf = KDiskFreeSpaceInfo::freeSpaceInfo( archive_url->url().path());
178 KIO::filesize_t freeSize = inf.available();;
179 if (freeSize > m_requestedSize) {
181 buttonBox->button(QDialogButtonBox::Apply)->setEnabled(true);
182 icon_info->setPixmap(KIcon("dialog-ok").pixmap(16, 16));
183 text_info->setText(i18n("Available space on drive: %1", KIO::convertSize(freeSize)));
186 buttonBox->button(QDialogButtonBox::Apply)->setEnabled(false);
187 icon_info->setPixmap(KIcon("dialog-close").pixmap(16, 16));
188 text_info->setText(i18n("Not enough space on drive, free space: %1", KIO::convertSize(freeSize)));
192 bool ArchiveWidget::slotStartArchiving(bool firstPass)
194 if (firstPass && m_copyJob) {
195 // archiving in progress, abort
196 m_copyJob->kill(KJob::EmitResult);
199 if (!firstPass) m_copyJob = NULL;
202 m_duplicateFiles.clear();
203 m_replacementList.clear();
207 for (int i = 0; i < files_list->topLevelItemCount(); i++) {
208 if (files_list->topLevelItem(i)->childCount() > 0 && !files_list->topLevelItem(i)->isDisabled()) {
209 files_list->setCurrentItem(files_list->topLevelItem(i));
210 files_list->topLevelItem(i)->setDisabled(true);
211 destUrl = KUrl(archive_url->url().path(KUrl::AddTrailingSlash) + files_list->topLevelItem(i)->data(0, Qt::UserRole).toString());
212 KIO::NetAccess::mkdir(destUrl, this);
213 QTreeWidgetItem *item;
214 for (int j = 0; j < files_list->topLevelItem(i)->childCount(); j++) {
215 item = files_list->topLevelItem(i)->child(j);
216 if (item->data(0, Qt::UserRole).isNull()) {
217 files << KUrl(item->text(0));
220 // We must rename the destination file, since another file with same name exists
221 //TODO: monitor progress
222 m_duplicateFiles.insert(KUrl(item->text(0)), KUrl(destUrl.path(KUrl::AddTrailingSlash) + item->data(0, Qt::UserRole).toString()));
229 if (destUrl.isEmpty()) {
230 if (m_duplicateFiles.isEmpty()) return false;
231 QMapIterator<KUrl, KUrl> i(m_duplicateFiles);
236 KIO::CopyJob *job = KIO::copyAs(startJob, i.value(), KIO::HideProgressInfo);
237 connect(job, SIGNAL(result(KJob *)), this, SLOT(slotArchivingFinished(KJob *)));
238 connect(job, SIGNAL(processedSize(KJob *, qulonglong)), this, SLOT(slotArchivingProgress(KJob *, qulonglong)));
239 m_duplicateFiles.remove(startJob);
244 m_copyJob = KIO::copy (files, destUrl, KIO::HideProgressInfo);
245 connect(m_copyJob, SIGNAL(result(KJob *)), this, SLOT(slotArchivingFinished(KJob *)));
246 connect(m_copyJob, SIGNAL(processedSize(KJob *, qulonglong)), this, SLOT(slotArchivingProgress(KJob *, qulonglong)));
249 progressBar->setValue(0);
250 buttonBox->button(QDialogButtonBox::Apply)->setText(i18n("Abort"));
255 void ArchiveWidget::slotArchivingFinished(KJob *job)
257 if (job->error() == 0) {
258 if (slotStartArchiving(false)) {
259 // We still have files to archive
263 // Archiving finished
264 progressBar->setValue(100);
265 if (processProjectFile()) {
266 icon_info->setPixmap(KIcon("dialog-ok").pixmap(16, 16));
267 text_info->setText(i18n("Project was successfully archived."));
270 icon_info->setPixmap(KIcon("dialog-close").pixmap(16, 16));
271 text_info->setText(i18n("There was an error processing project file"));
277 icon_info->setPixmap(KIcon("dialog-close").pixmap(16, 16));
278 text_info->setText(i18n("There was an error while copying the files: %1", job->errorString()));
280 buttonBox->button(QDialogButtonBox::Apply)->setText(i18n("Archive"));
281 for (int i = 0; i < files_list->topLevelItemCount(); i++) {
282 files_list->topLevelItem(i)->setDisabled(false);
286 void ArchiveWidget::slotArchivingProgress(KJob *, qulonglong size)
288 progressBar->setValue((int) 100 * size / m_requestedSize);
292 bool ArchiveWidget::processProjectFile()
295 QTreeWidgetItem *item;
296 for (int i = 0; i < files_list->topLevelItemCount(); i++) {
297 if (files_list->topLevelItem(i)->childCount() > 0) {
298 destUrl = KUrl(archive_url->url().path(KUrl::AddTrailingSlash) + files_list->topLevelItem(i)->data(0, Qt::UserRole).toString());
299 for (int j = 0; j < files_list->topLevelItem(i)->childCount(); j++) {
300 item = files_list->topLevelItem(i)->child(j);
301 KUrl src(item->text(0));
303 if (item->data(0, Qt::UserRole).isNull()) {
304 dest.addPath(src.fileName());
307 dest.addPath(item->data(0, Qt::UserRole).toString());
309 m_replacementList.insert(src, dest);
313 // process kdenlive producers
315 QDomElement mlt = m_doc.documentElement();
316 QDomNodeList prods = mlt.elementsByTagName("kdenlive_producer");
317 for (int i = 0; i < prods.count(); i++) {
318 QDomElement e = prods.item(i).toElement();
319 if (e.isNull()) continue;
320 if (e.hasAttribute("resource")) {
321 KUrl src(e.attribute("resource"));
322 KUrl dest = m_replacementList.value(src);
323 if (!dest.isEmpty()) e.setAttribute("resource", dest.path());
327 QString path = archive_url->url().path(KUrl::AddTrailingSlash) + "project.kdenlive";
329 if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
330 kWarning() << "////// ERROR writing to file: " << path;
331 KMessageBox::error(kapp->activeWindow(), i18n("Cannot write to file %1", path));
335 file.write(m_doc.toString().toUtf8());
336 if (file.error() != QFile::NoError) {
337 KMessageBox::error(kapp->activeWindow(), i18n("Cannot write to file %1", path));