X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=src%2Farchivewidget.cpp;h=569d336ac7dd6a02dc33d1e93ef89c45db291252;hb=0da95d724966048ade94f10a865260b5216ad2e4;hp=db2b9e8db2c6d033c1db1f17a42db70beb343ad0;hpb=a9eee37bf60bfae1ce974d2bb3f4954c23e05664;p=kdenlive diff --git a/src/archivewidget.cpp b/src/archivewidget.cpp index db2b9e8d..569d336a 100644 --- a/src/archivewidget.cpp +++ b/src/archivewidget.cpp @@ -32,6 +32,9 @@ #include #include #include +#if KDE_IS_VERSION(4,7,0) +#include +#endif #include #include @@ -46,7 +49,8 @@ ArchiveWidget::ArchiveWidget(QString projectName, QDomDocument doc, QList setIcon(0, KIcon("text-plain")); texts->setData(0, Qt::UserRole, "texts"); texts->setExpanded(false); + QTreeWidgetItem *playlists = new QTreeWidgetItem(files_list, QStringList() << i18n("Playlist clips")); + playlists->setIcon(0, KIcon("video-mlt-playlist")); + playlists->setData(0, Qt::UserRole, "playlist"); + playlists->setExpanded(false); QTreeWidgetItem *others = new QTreeWidgetItem(files_list, QStringList() << i18n("Other clips")); others->setIcon(0, KIcon("unknown")); others->setData(0, Qt::UserRole, "others"); @@ -95,53 +104,80 @@ ArchiveWidget::ArchiveWidget(QString projectName, QDomDocument doc, QList slideUrls; + QMap audioUrls; + QMap videoUrls; + QMap imageUrls; + QMap playlistUrls; + QMap proxyUrls; for (int i = 0; i < list.count(); i++) { DocClipBase *clip = list.at(i); CLIPTYPE t = clip->clipType(); + QString id = clip->getId(); if (t == SLIDESHOW) { KUrl slideUrl = clip->fileURL(); //TODO: Slideshow files - slideUrls << slideUrl.path(); + slideUrls.insert(id, slideUrl.path()); } - else if (t == IMAGE) imageUrls << clip->fileURL().path(); + else if (t == IMAGE) imageUrls.insert(id, clip->fileURL().path()); else if (t == TEXT) { QStringList imagefiles = TitleWidget::extractImageList(clip->getProperty("xmldata")); QStringList fonts = TitleWidget::extractFontList(clip->getProperty("xmldata")); - imageUrls << imagefiles; + extraImageUrls << imagefiles; allFonts << fonts; } else if (t == PLAYLIST) { + playlistUrls.insert(id, clip->fileURL().path()); QStringList files = ProjectSettings::extractPlaylistUrls(clip->fileURL().path()); otherUrls << files; } else if (!clip->fileURL().isEmpty()) { - if (t == AUDIO) audioUrls << clip->fileURL().path(); + if (t == AUDIO) audioUrls.insert(id, clip->fileURL().path()); else { - videoUrls << clip->fileURL().path(); + videoUrls.insert(id, clip->fileURL().path()); // Check if we have a proxy QString proxy = clip->getProperty("proxy"); - if (!proxy.isEmpty() && proxy != "-" && QFile::exists(proxy)) proxyUrls << proxy; + if (!proxy.isEmpty() && proxy != "-" && QFile::exists(proxy)) proxyUrls.insert(id, proxy); } } } + generateItems(images, extraImageUrls); generateItems(sounds, audioUrls); generateItems(videos, videoUrls); generateItems(images, imageUrls); generateItems(slideshows, slideUrls); + generateItems(playlists, playlistUrls); generateItems(others, otherUrls); generateItems(proxies, proxyUrls); allFonts.removeDuplicates(); +#if KDE_IS_VERSION(4,7,0) + m_infoMessage = new KMessageWidget(this); + QVBoxLayout *s = static_cast (layout()); + s->insertWidget(5, m_infoMessage); + m_infoMessage->setCloseButtonVisible(false); + m_infoMessage->setWordWrap(true); + m_infoMessage->hide(); +#endif + + // missing clips, warn user + if (m_missingClips > 0) { + QString infoText = i18np("You have %1 missing clip in your project.", "You have %1 missing clips in your project.", m_missingClips); +#if KDE_IS_VERSION(4,7,0) + m_infoMessage->setMessageType(KMessageWidget::Warning); + m_infoMessage->setText(infoText); + m_infoMessage->animatedShow(); +#else + KMessageBox::sorry(this, infoText); +#endif + } + //TODO: fonts // Hide unused categories, add item count @@ -161,7 +197,7 @@ ArchiveWidget::ArchiveWidget(QString projectName, QDomDocument doc, QList setText(0, files_list->topLevelItem(i)->text(0) + " " + i18np("(%1 item)", "(%1 items)", items)); + parentItem->setText(0, files_list->topLevelItem(i)->text(0) + ' ' + i18np("(%1 item)", "(%1 items)", items)); } } if (m_name.isEmpty()) m_name = i18n("Untitled"); @@ -170,7 +206,7 @@ ArchiveWidget::ArchiveWidget(QString projectName, QDomDocument doc, QList button(QDialogButtonBox::Apply)->setText(i18n("Archive")); connect(buttonBox->button(QDialogButtonBox::Apply), SIGNAL(clicked()), this, SLOT(slotStartArchiving())); buttonBox->button(QDialogButtonBox::Apply)->setEnabled(false); - + slotCheckSpace(); } @@ -187,6 +223,7 @@ ArchiveWidget::ArchiveWidget(const KUrl &url, QWidget * parent): connect(this, SIGNAL(showMessage(const QString &, const QString &)), this, SLOT(slotDisplayMessage(const QString &, const QString &))); compressed_archive->setHidden(true); + proxy_only->setHidden(true); project_files->setHidden(true); files_list->setHidden(true); label->setText(i18n("Extract to")); @@ -194,7 +231,7 @@ ArchiveWidget::ArchiveWidget(const KUrl &url, QWidget * parent): archive_url->setUrl(KUrl(QDir::homePath())); buttonBox->button(QDialogButtonBox::Apply)->setText(i18n("Extract")); connect(buttonBox->button(QDialogButtonBox::Apply), SIGNAL(clicked()), this, SLOT(slotStartExtracting())); - buttonBox->button(QDialogButtonBox::Apply)->setEnabled(false); + buttonBox->button(QDialogButtonBox::Apply)->setEnabled(true); adjustSize(); m_archiveThread = QtConcurrent::run(this, &ArchiveWidget::openArchiveForExtraction); } @@ -206,11 +243,24 @@ ArchiveWidget::~ArchiveWidget() } void ArchiveWidget::slotDisplayMessage(const QString &icon, const QString &text) -{ +{ icon_info->setPixmap(KIcon(icon).pixmap(16, 16)); text_info->setText(text); } +void ArchiveWidget::slotJobResult(bool success, const QString &text) +{ +#if KDE_IS_VERSION(4,7,0) + m_infoMessage->setMessageType(success ? KMessageWidget::Positive : KMessageWidget::Warning); + m_infoMessage->setText(text); + m_infoMessage->animatedShow(); +#else + if (success) icon_info->setPixmap(KIcon("dialog-ok").pixmap(16, 16)); + else icon_info->setPixmap(KIcon("dialog-close").pixmap(16, 16)); + text_info->setText(text); +#endif +} + void ArchiveWidget::openArchiveForExtraction() { emit showMessage("system-run", i18n("Opening archive...")); @@ -235,6 +285,7 @@ void ArchiveWidget::openArchiveForExtraction() if (!isProjectArchive) { emit showMessage("dialog-close", i18n("File %1\n is not an archived Kdenlive project", m_extractUrl.path())); groupBox->setEnabled(false); + buttonBox->button(QDialogButtonBox::Apply)->setEnabled(false); return; } buttonBox->button(QDialogButtonBox::Apply)->setEnabled(true); @@ -291,11 +342,14 @@ void ArchiveWidget::generateItems(QTreeWidgetItem *parentItem, QStringList items dir.setNameFilters(filters); QFileInfoList resultList = dir.entryInfoList(QDir::Files); QStringList slideImages; + qint64 totalSize = 0; for (int i = 0; i < resultList.count(); i++) { - m_requestedSize += resultList.at(i).size(); + totalSize += resultList.at(i).size(); slideImages << resultList.at(i).absoluteFilePath(); } item->setData(0, Qt::UserRole + 1, slideImages); + item->setData(0, Qt::UserRole + 3, totalSize); + m_requestedSize += totalSize; } else { // pattern url (like clip%.3d.png) @@ -303,24 +357,113 @@ void ArchiveWidget::generateItems(QTreeWidgetItem *parentItem, QStringList items QString filter = slideUrl.fileName(); QString ext = filter.section('.', -1); filter = filter.section('%', 0, -2); - QString regexp = "^" + filter + "\\d+\\." + ext + "$"; + QString regexp = '^' + filter + "\\d+\\." + ext + '$'; QRegExp rx(regexp); QStringList slideImages; QString directory = dir.absolutePath(); if (!directory.endsWith('/')) directory.append('/'); + qint64 totalSize = 0; foreach(const QString & path, result) { if (rx.exactMatch(path)) { - m_requestedSize += QFileInfo(directory + path).size(); + totalSize += QFileInfo(directory + path).size(); slideImages << directory + path; } } item->setData(0, Qt::UserRole + 1, slideImages); + item->setData(0, Qt::UserRole + 3, totalSize); + m_requestedSize += totalSize; } } else if (filesList.contains(fileName)) { // we have 2 files with same name int ix = 0; - QString newFileName = fileName.section('.', 0, -2) + "_" + QString::number(ix) + "." + fileName.section('.', -1); + QString newFileName = fileName.section('.', 0, -2) + '_' + QString::number(ix) + '.' + fileName.section('.', -1); + while (filesList.contains(newFileName)) { + ix ++; + newFileName = fileName.section('.', 0, -2) + '_' + QString::number(ix) + '.' + fileName.section('.', -1); + } + fileName = newFileName; + item->setData(0, Qt::UserRole, fileName); + } + if (!isSlideshow) { + qint64 fileSize = QFileInfo(file).size(); + if (fileSize <= 0) { + item->setIcon(0, KIcon("edit-delete")); + m_missingClips++; + } + else { + m_requestedSize += fileSize; + item->setData(0, Qt::UserRole + 3, fileSize); + } + filesList << fileName; + } + } +} + +void ArchiveWidget::generateItems(QTreeWidgetItem *parentItem, QMap items) +{ + QStringList filesList; + QString fileName; + int ix = 0; + bool isSlideshow = parentItem->data(0, Qt::UserRole).toString() == "slideshows"; + QMap::const_iterator it = items.constBegin(); + while (it != items.constEnd()) { + QString file = it.value(); + QTreeWidgetItem *item = new QTreeWidgetItem(parentItem, QStringList() << file); + // Store the clip's id + item->setData(0, Qt::UserRole + 2, it.key()); + fileName = KUrl(file).fileName(); + if (isSlideshow) { + // we store each slideshow in a separate subdirectory + item->setData(0, Qt::UserRole, ix); + ix++; + KUrl slideUrl(file); + QDir dir(slideUrl.directory(KUrl::AppendTrailingSlash)); + if (slideUrl.fileName().startsWith(".all.")) { + // mimetype slideshow (for example *.png) + QStringList filters; + QString extension; + // TODO: improve jpeg image detection with extension like jpeg, requires change in MLT image producers + filters << "*." + slideUrl.fileName().section('.', -1); + dir.setNameFilters(filters); + QFileInfoList resultList = dir.entryInfoList(QDir::Files); + QStringList slideImages; + qint64 totalSize = 0; + for (int i = 0; i < resultList.count(); i++) { + totalSize += resultList.at(i).size(); + slideImages << resultList.at(i).absoluteFilePath(); + } + item->setData(0, Qt::UserRole + 1, slideImages); + item->setData(0, Qt::UserRole + 3, totalSize); + m_requestedSize += totalSize; + } + else { + // pattern url (like clip%.3d.png) + QStringList result = dir.entryList(QDir::Files); + QString filter = slideUrl.fileName(); + QString ext = filter.section('.', -1); + filter = filter.section('%', 0, -2); + QString regexp = '^' + filter + "\\d+\\." + ext + '$'; + QRegExp rx(regexp); + QStringList slideImages; + qint64 totalSize = 0; + QString directory = dir.absolutePath(); + if (!directory.endsWith('/')) directory.append('/'); + foreach(const QString & path, result) { + if (rx.exactMatch(path)) { + totalSize += QFileInfo(directory + path).size(); + slideImages << directory + path; + } + } + item->setData(0, Qt::UserRole + 1, slideImages); + item->setData(0, Qt::UserRole + 3, totalSize); + m_requestedSize += totalSize; + } + } + else if (filesList.contains(fileName)) { + // we have 2 files with same name + int ix = 0; + QString newFileName = fileName.section('.', 0, -2) + '_' + QString::number(ix) + '.' + fileName.section('.', -1); while (filesList.contains(newFileName)) { ix ++; newFileName = fileName.section('.', 0, -2) + "_" + QString::number(ix) + "." + fileName.section('.', -1); @@ -329,16 +472,25 @@ void ArchiveWidget::generateItems(QTreeWidgetItem *parentItem, QStringList items item->setData(0, Qt::UserRole, fileName); } if (!isSlideshow) { - m_requestedSize += QFileInfo(file).size(); + qint64 fileSize = QFileInfo(file).size(); + if (fileSize <= 0) { + item->setIcon(0, KIcon("edit-delete")); + m_missingClips++; + } + else { + m_requestedSize += fileSize; + item->setData(0, Qt::UserRole + 3, fileSize); + } filesList << fileName; } + ++it; } } void ArchiveWidget::slotCheckSpace() { KDiskFreeSpaceInfo inf = KDiskFreeSpaceInfo::freeSpaceInfo( archive_url->url().path()); - KIO::filesize_t freeSize = inf.available();; + KIO::filesize_t freeSize = inf.available(); if (freeSize > m_requestedSize) { // everything is ok buttonBox->button(QDialogButtonBox::Apply)->setEnabled(true); @@ -370,6 +522,7 @@ bool ArchiveWidget::slotStartArchiving(bool firstPass) slotDisplayMessage("system-run", i18n("Archiving...")); repaint(); archive_url->setEnabled(false); + proxy_only->setEnabled(false); compressed_archive->setEnabled(false); } KUrl::List files; @@ -377,28 +530,35 @@ bool ArchiveWidget::slotStartArchiving(bool firstPass) QString destPath; QTreeWidgetItem *parentItem; bool isSlideshow = false; + int items = 0; + + // We parse all files going into one folder, then start the copy job for (int i = 0; i < files_list->topLevelItemCount(); i++) { parentItem = files_list->topLevelItem(i); - if (parentItem->childCount() > 0 && !parentItem->isDisabled()) { + if (parentItem->isDisabled()) { + parentItem->setExpanded(false); + continue; + } + if (parentItem->childCount() > 0) { if (parentItem->data(0, Qt::UserRole).toString() == "slideshows") { KUrl slideFolder(archive_url->url().path(KUrl::AddTrailingSlash) + "slideshows"); if (isArchive) m_foldersList.append("slideshows"); else KIO::NetAccess::mkdir(slideFolder, this); isSlideshow = true; } + else isSlideshow = false; files_list->setCurrentItem(parentItem); - if (!isSlideshow) parentItem->setDisabled(true); - destPath = parentItem->data(0, Qt::UserRole).toString() + "/"; + parentItem->setExpanded(true); + destPath = parentItem->data(0, Qt::UserRole).toString() + '/'; destUrl = KUrl(archive_url->url().path(KUrl::AddTrailingSlash) + destPath); QTreeWidgetItem *item; for (int j = 0; j < parentItem->childCount(); j++) { item = parentItem->child(j); + if (item->isDisabled()) continue; // Special case: slideshows + items++; if (isSlideshow) { - if (item->isDisabled()) { - continue; - } - destPath.append(item->data(0, Qt::UserRole).toString() + "/"); + destPath += item->data(0, Qt::UserRole).toString() + '/'; destUrl = KUrl(archive_url->url().path(KUrl::AddTrailingSlash) + destPath); QStringList srcFiles = item->data(0, Qt::UserRole + 1).toStringList(); for (int k = 0; k < srcFiles.count(); k++) { @@ -423,10 +583,17 @@ bool ArchiveWidget::slotStartArchiving(bool firstPass) else m_duplicateFiles.insert(KUrl(item->text(0)), KUrl(destUrl.path(KUrl::AddTrailingSlash) + item->data(0, Qt::UserRole).toString())); } } + if (!isSlideshow) parentItem->setDisabled(true); break; } } + if (items == 0) { + // No clips to archive + slotArchivingFinished(NULL, true); + return true; + } + if (destPath.isEmpty()) { if (m_duplicateFiles.isEmpty()) return false; QMapIterator i(m_duplicateFiles); @@ -465,10 +632,10 @@ bool ArchiveWidget::slotStartArchiving(bool firstPass) return true; } -void ArchiveWidget::slotArchivingFinished(KJob *job) +void ArchiveWidget::slotArchivingFinished(KJob *job, bool finished) { if (job == NULL || job->error() == 0) { - if (slotStartArchiving(false)) { + if (!finished && slotStartArchiving(false)) { // We still have files to archive return; } @@ -476,20 +643,21 @@ void ArchiveWidget::slotArchivingFinished(KJob *job) // Archiving finished progressBar->setValue(100); if (processProjectFile()) { - slotDisplayMessage("dialog-ok", i18n("Project was successfully archived.")); + slotJobResult(true, i18n("Project was successfully archived.")); } else { - slotDisplayMessage("dialog-close", i18n("There was an error processing project file")); + slotJobResult(false, i18n("There was an error processing project file")); } } else processProjectFile(); } else { m_copyJob = NULL; - slotDisplayMessage("dialog-close", i18n("There was an error while copying the files: %1", job->errorString())); + slotJobResult(false, i18n("There was an error while copying the files: %1", job->errorString())); } if (!compressed_archive->isChecked()) { buttonBox->button(QDialogButtonBox::Apply)->setText(i18n("Archive")); archive_url->setEnabled(true); + proxy_only->setEnabled(true); compressed_archive->setEnabled(true); for (int i = 0; i < files_list->topLevelItemCount(); i++) { files_list->topLevelItem(i)->setDisabled(false); @@ -521,7 +689,7 @@ bool ArchiveWidget::processProjectFile() KUrl src(item->text(0)); KUrl dest = destUrl; if (isSlideshow) { - dest = KUrl(destUrl.path(KUrl::AddTrailingSlash) + item->data(0, Qt::UserRole).toString() + "/" + src.fileName()); + dest = KUrl(destUrl.path(KUrl::AddTrailingSlash) + item->data(0, Qt::UserRole).toString() + '/' + src.fileName()); } else if (item->data(0, Qt::UserRole).isNull()) { dest.addPath(src.fileName()); @@ -535,7 +703,7 @@ bool ArchiveWidget::processProjectFile() } QDomElement mlt = m_doc.documentElement(); - QString root = mlt.attribute("root") + "/"; + QString root = mlt.attribute("root") + '/'; // Adjust global settings QString basePath; @@ -601,8 +769,8 @@ bool ArchiveWidget::processProjectFile() QString endString("\""); endString.append(basePath); playList.replace(startString, endString); - startString = ">" + archive_url->url().path(KUrl::RemoveTrailingSlash); - endString = ">" + basePath; + startString = '>' + archive_url->url().path(KUrl::RemoveTrailingSlash); + endString = '>' + basePath; playList.replace(startString, endString); } @@ -666,14 +834,16 @@ void ArchiveWidget::createArchive() void ArchiveWidget::slotArchivingFinished(bool result) { if (result) { - slotDisplayMessage("dialog-ok", i18n("Project was successfully archived.")); + slotJobResult(true, i18n("Project was successfully archived.")); + buttonBox->button(QDialogButtonBox::Apply)->setEnabled(false); } else { - slotDisplayMessage("dialog-close", i18n("There was an error processing project file")); + slotJobResult(false, i18n("There was an error processing project file")); } progressBar->setValue(100); buttonBox->button(QDialogButtonBox::Apply)->setText(i18n("Archive")); archive_url->setEnabled(true); + proxy_only->setEnabled(true); compressed_archive->setEnabled(true); for (int i = 0; i < files_list->topLevelItemCount(); i++) { files_list->topLevelItem(i)->setDisabled(false); @@ -744,7 +914,7 @@ void ArchiveWidget::slotExtractingFinished() error = true; } else { - QString playList = file.readAll(); + QString playList = QString::fromUtf8(file.readAll()); file.close(); if (playList.isEmpty()) { error = true; @@ -770,3 +940,74 @@ void ArchiveWidget::slotExtractingFinished() } else accept(); } + +void ArchiveWidget::slotProxyOnly(int onlyProxy) +{ + m_requestedSize = 0; + if (onlyProxy == Qt::Checked) { + // Archive proxy clips + QStringList proxyIdList; + QTreeWidgetItem *parentItem = NULL; + + // Build list of existing proxy ids + for (int i = 0; i < files_list->topLevelItemCount(); i++) { + parentItem = files_list->topLevelItem(i); + if (parentItem->data(0, Qt::UserRole).toString() == "proxy") break; + } + if (!parentItem) return; + int items = parentItem->childCount(); + for (int j = 0; j < items; j++) { + proxyIdList << parentItem->child(j)->data(0, Qt::UserRole + 2).toString(); + } + + // Parse all items to disable original clips for existing proxies + for (int i = 0; i < proxyIdList.count(); i++) { + QString id = proxyIdList.at(i); + if (id.isEmpty()) continue; + for (int j = 0; j < files_list->topLevelItemCount(); j++) { + parentItem = files_list->topLevelItem(j); + if (parentItem->data(0, Qt::UserRole).toString() == "proxy") continue; + items = parentItem->childCount(); + for (int k = 0; k < items; k++) { + if (parentItem->child(k)->data(0, Qt::UserRole + 2).toString() == id) { + // This item has a proxy, do not archive it + parentItem->child(k)->setFlags(Qt::ItemIsSelectable); + break; + } + } + } + } + } + else { + // Archive all clips + for (int i = 0; i < files_list->topLevelItemCount(); i++) { + QTreeWidgetItem *parentItem = files_list->topLevelItem(i); + int items = parentItem->childCount(); + for (int j = 0; j < items; j++) { + parentItem->child(j)->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); + } + } + } + + // Calculate requested size + int total = 0; + for (int i = 0; i < files_list->topLevelItemCount(); i++) { + QTreeWidgetItem *parentItem = files_list->topLevelItem(i); + int items = parentItem->childCount(); + int itemsCount = 0; + bool isSlideshow = parentItem->data(0, Qt::UserRole).toString() == "slideshows"; + + for (int j = 0; j < items; j++) { + if (!parentItem->child(j)->isDisabled()) { + m_requestedSize += parentItem->child(j)->data(0, Qt::UserRole + 3).toInt(); + if (isSlideshow) total += parentItem->child(j)->data(0, Qt::UserRole + 1).toStringList().count(); + else total ++; + itemsCount ++; + } + } + parentItem->setText(0, parentItem->text(0).section('(', 0, 0) + i18np("(%1 item)", "(%1 items)", itemsCount)); + } + project_files->setText(i18np("%1 file to archive, requires %2", "%1 files to archive, requires %2", total, KIO::convertSize(m_requestedSize))); + slotCheckSpace(); +} +