]> git.sesse.net Git - kdenlive/commitdiff
Some progress on archiving feature
authorJean-Baptiste Mardelle <jb@kdenlive.org>
Mon, 9 May 2011 08:14:13 +0000 (08:14 +0000)
committerJean-Baptiste Mardelle <jb@kdenlive.org>
Mon, 9 May 2011 08:14:13 +0000 (08:14 +0000)
svn path=/trunk/kdenlive/; revision=5572

src/archivewidget.cpp
src/archivewidget.h
src/kdenlivedoc.cpp
src/kdenlivedoc.h
src/mainwindow.cpp
src/widgets/archivewidget_ui.ui

index d74a8e8a83eafa2693c8307ae4d24e7864615c71..78692d354cd0356a76e38eb6410250447ff32170 100644 (file)
 #include <KDiskFreeSpaceInfo>
 #include <KUrlRequester>
 #include <KFileDialog>
+#include <KMessageBox>
+#include <KApplication>
+#include <KIO/NetAccess>
 
 #include <KDebug>
-#include <QListWidget>
+#include <QTreeWidget>
 #include "projectsettings.h"
 
 
-ArchiveWidget::ArchiveWidget(QList <DocClipBase*> list, QStringList lumas, QWidget * parent) :
+ArchiveWidget::ArchiveWidget(QDomDocument doc, QList <DocClipBase*> list, QStringList luma_list, QWidget * parent) :
         QDialog(parent),
         m_requestedSize(0),
-        m_copyJob(NULL)
+        m_copyJob(NULL),
+        m_doc(doc)
 {
     setupUi(this);
     setWindowTitle(i18n("Archive Project"));
     archive_url->setUrl(KUrl(QDir::homePath()));
     connect(archive_url, SIGNAL(textChanged (const QString &)), this, SLOT(slotCheckSpace()));
 
+
+    // Setup categories
+    QTreeWidgetItem *videos = new QTreeWidgetItem(files_list, QStringList() << i18n("Video clips"));
+    videos->setIcon(0, KIcon("video-x-generic"));
+    videos->setData(0, Qt::UserRole, i18n("videos"));
+    videos->setExpanded(false);
+    QTreeWidgetItem *sounds = new QTreeWidgetItem(files_list, QStringList() << i18n("Audio clips"));
+    sounds->setIcon(0, KIcon("audio-x-generic"));
+    sounds->setData(0, Qt::UserRole, i18n("sounds"));
+    sounds->setExpanded(false);
+    QTreeWidgetItem *images = new QTreeWidgetItem(files_list, QStringList() << i18n("Image clips"));
+    images->setIcon(0, KIcon("image-x-generic"));
+    images->setData(0, Qt::UserRole, i18n("images"));
+    images->setExpanded(false);
+    QTreeWidgetItem *slideshows = new QTreeWidgetItem(files_list, QStringList() << i18n("Slideshow clips"));
+    slideshows->setIcon(0, KIcon("image-x-generic"));
+    slideshows->setData(0, Qt::UserRole, i18n("slideshows"));
+    slideshows->setExpanded(false);
+    QTreeWidgetItem *texts = new QTreeWidgetItem(files_list, QStringList() << i18n("Text clips"));
+    texts->setIcon(0, KIcon("text-plain"));
+    texts->setData(0, Qt::UserRole, i18n("texts"));
+    texts->setExpanded(false);
+    QTreeWidgetItem *others = new QTreeWidgetItem(files_list, QStringList() << i18n("Other clips"));
+    others->setIcon(0, KIcon("unknown"));
+    others->setData(0, Qt::UserRole, i18n("others"));
+    others->setExpanded(false);
+    QTreeWidgetItem *lumas = new QTreeWidgetItem(files_list, QStringList() << i18n("Luma files"));
+    lumas->setIcon(0, KIcon("image-x-generic"));
+    lumas->setData(0, Qt::UserRole, i18n("lumas"));
+    lumas->setExpanded(false);
+
     // process all files
     QStringList allFonts;
-    foreach(const QString & file, lumas) {
-        kDebug()<<"LUMA: "<<file;
-        files_list->addItem(file);
-        m_requestedSize += QFileInfo(file).size();
-    }
+    KUrl::List fileUrls;
+    QStringList fileNames;
+    generateItems(lumas, luma_list);
+
+    QStringList slideUrls;
+    QStringList audioUrls;
+    QStringList videoUrls;
+    QStringList imageUrls;
+    QStringList otherUrls;
 
     for (int i = 0; i < list.count(); i++) {
         DocClipBase *clip = list.at(i);
@@ -55,35 +94,50 @@ ArchiveWidget::ArchiveWidget(QList <DocClipBase*> list, QStringList lumas, QWidg
             QStringList subfiles = ProjectSettings::extractSlideshowUrls(clip->fileURL());
             foreach(const QString & file, subfiles) {
                 kDebug()<<"SLIDE: "<<file;
-                files_list->addItem(file);
+                new QTreeWidgetItem(slideshows, QStringList() << file);
                 m_requestedSize += QFileInfo(file).size();
             }
         } else if (!clip->fileURL().isEmpty()) {
-            files_list->addItem(clip->fileURL().path());
-            m_requestedSize += QFileInfo(clip->fileURL().path()).size();
+            if (clip->clipType() == AUDIO) audioUrls << clip->fileURL().path();
+            else videoUrls << clip->fileURL().path();
         }
         if (clip->clipType() == TEXT) {
             QStringList imagefiles = TitleWidget::extractImageList(clip->getProperty("xmldata"));
             QStringList fonts = TitleWidget::extractFontList(clip->getProperty("xmldata"));
-            foreach(const QString & file, imagefiles) {
-                kDebug()<<"TXT IMAGE: "<<file;
-                files_list->addItem(file);
-                m_requestedSize += QFileInfo(file).size();
-            }
+            imageUrls << imagefiles;
             allFonts << fonts;
         } else if (clip->clipType() == PLAYLIST) {
             QStringList files = ProjectSettings::extractPlaylistUrls(clip->fileURL().path());
-            foreach(const QString & file, files) {
-                kDebug()<<"PLAYLIST: "<<file;
-                files_list->addItem(file);
-                m_requestedSize += QFileInfo(file).size();
-            }
+            otherUrls << files;
         }
     }
+
+    generateItems(sounds, audioUrls);
+    generateItems(videos, videoUrls);
+    generateItems(images, imageUrls);
+    //generateItems(slideshows, slideUrls);
+    generateItems(others, otherUrls);
+    
 #if QT_VERSION >= 0x040500
     allFonts.removeDuplicates();
 #endif
-    project_files->setText(i18n("%1 files to archive, requires %2", files_list->count(), KIO::convertSize(m_requestedSize)));
+
+    //TODO: fonts
+
+    // Hide unused categories, add item count
+    int total = 0;
+    for (int i = 0; i < files_list->topLevelItemCount(); i++) {
+        int items = files_list->topLevelItem(i)->childCount();
+        if (items == 0) {
+            files_list->topLevelItem(i)->setHidden(true);
+        }
+        else {
+            total += items;
+            files_list->topLevelItem(i)->setText(0, files_list->topLevelItem(i)->text(0) + " " + i18n("(%1 items)", items));
+        }
+    }
+    
+    project_files->setText(i18n("%1 files to archive, requires %2", total, KIO::convertSize(m_requestedSize)));
     buttonBox->button(QDialogButtonBox::Apply)->setText(i18n("Archive"));
     connect(buttonBox->button(QDialogButtonBox::Apply), SIGNAL(clicked()), this, SLOT(slotStartArchiving()));
     buttonBox->button(QDialogButtonBox::Apply)->setEnabled(false);
@@ -95,6 +149,29 @@ ArchiveWidget::~ArchiveWidget()
 {
 }
 
+void ArchiveWidget::generateItems(QTreeWidgetItem *parentItem, QStringList items)
+{
+    QStringList filesList;
+    QString fileName;
+    foreach(const QString & file, items) {
+        QTreeWidgetItem *item = new QTreeWidgetItem(parentItem, QStringList() << file);
+        fileName = KUrl(file).fileName();
+        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);
+            }
+            fileName = newFileName;
+            item->setData(0, Qt::UserRole, fileName);
+        }
+        filesList << fileName;
+        m_requestedSize += QFileInfo(file).size();
+    }
+}
+
 void ArchiveWidget::slotCheckSpace()
 {
     KDiskFreeSpaceInfo inf = KDiskFreeSpaceInfo::freeSpaceInfo( archive_url->url().path());
@@ -112,36 +189,98 @@ void ArchiveWidget::slotCheckSpace()
     }
 }
 
-void ArchiveWidget::slotStartArchiving()
+bool ArchiveWidget::slotStartArchiving(bool firstPass)
 {
-    if (m_copyJob) {
+    if (firstPass && m_copyJob) {
         // archiving in progress, abort
         m_copyJob->kill(KJob::EmitResult);
-        return;
+        return true;
+    }
+    if (!firstPass) m_copyJob = NULL;
+    else {
+        //starting archiving
+        m_duplicateFiles.clear();
+        m_replacementList.clear();
     }
     KUrl::List files;
-    for (int i = 0; i < files_list->count(); i++) {
-        if (files_list->item(i))
-            files << KUrl(files_list->item(i)->text());
+    KUrl destUrl;
+    for (int i = 0; i < files_list->topLevelItemCount(); i++) {
+        if (files_list->topLevelItem(i)->childCount() > 0 && !files_list->topLevelItem(i)->isDisabled()) {
+            files_list->setCurrentItem(files_list->topLevelItem(i));
+            files_list->topLevelItem(i)->setDisabled(true);
+            destUrl = KUrl(archive_url->url().path(KUrl::AddTrailingSlash) + files_list->topLevelItem(i)->data(0, Qt::UserRole).toString());
+            KIO::NetAccess::mkdir(destUrl, this);
+            QTreeWidgetItem *item;
+            for (int j = 0; j < files_list->topLevelItem(i)->childCount(); j++) {
+                item = files_list->topLevelItem(i)->child(j);
+                if (item->data(0, Qt::UserRole).isNull()) {
+                    files << KUrl(item->text(0));
+                }
+                else {
+                    // We must rename the destination file, since another file with same name exists
+                    //TODO: monitor progress
+                    m_duplicateFiles.insert(KUrl(item->text(0)), KUrl(destUrl.path(KUrl::AddTrailingSlash) + item->data(0, Qt::UserRole).toString()));
+                }
+            }
+            break;
+        }
     }
-    
-    progressBar->setValue(0);
-    buttonBox->button(QDialogButtonBox::Apply)->setText(i18n("Abort"));
-    m_copyJob = KIO::copy (files, archive_url->url(), KIO::HideProgressInfo);
+
+    if (destUrl.isEmpty()) {
+        if (m_duplicateFiles.isEmpty()) return false;        
+        QMapIterator<KUrl, KUrl> i(m_duplicateFiles);
+        KUrl startJob;
+        if (i.hasNext()) {
+            i.next();
+            startJob = i.key();
+            KIO::CopyJob *job = KIO::copyAs(startJob, i.value(), KIO::HideProgressInfo);
+            connect(job, SIGNAL(result(KJob *)), this, SLOT(slotArchivingFinished(KJob *)));
+            connect(job, SIGNAL(processedSize(KJob *, qulonglong)), this, SLOT(slotArchivingProgress(KJob *, qulonglong)));
+            m_duplicateFiles.remove(startJob);
+        }
+        return true;
+    }
+
+    m_copyJob = KIO::copy (files, destUrl, KIO::HideProgressInfo);
     connect(m_copyJob, SIGNAL(result(KJob *)), this, SLOT(slotArchivingFinished(KJob *)));
     connect(m_copyJob, SIGNAL(processedSize(KJob *, qulonglong)), this, SLOT(slotArchivingProgress(KJob *, qulonglong)));
+
+    if (firstPass) {
+        progressBar->setValue(0);
+        buttonBox->button(QDialogButtonBox::Apply)->setText(i18n("Abort"));
+    }
+    return true;
 }
 
 void ArchiveWidget::slotArchivingFinished(KJob *job)
 {
-    progressBar->setValue(100);
-    buttonBox->button(QDialogButtonBox::Apply)->setText(i18n("Archive"));
-    if (job->error() == 0) text_info->setText(i18n("Project was successfully archived."));
+    if (job->error() == 0) {
+        if (slotStartArchiving(false)) {
+            // We still have files to archive
+            return;
+        }
+        else {
+            // Archiving finished
+            progressBar->setValue(100);
+            if (processProjectFile()) {
+                icon_info->setPixmap(KIcon("dialog-ok").pixmap(16, 16));
+                text_info->setText(i18n("Project was successfully archived."));
+            }
+            else {
+                icon_info->setPixmap(KIcon("dialog-close").pixmap(16, 16));
+                text_info->setText(i18n("There was an error processing project file"));
+            }
+        }
+    }
     else {
+        m_copyJob = NULL;
         icon_info->setPixmap(KIcon("dialog-close").pixmap(16, 16));
         text_info->setText(i18n("There was an error while copying the files: %1", job->errorString()));
     }
-    m_copyJob = NULL;
+    buttonBox->button(QDialogButtonBox::Apply)->setText(i18n("Archive"));
+    for (int i = 0; i < files_list->topLevelItemCount(); i++) {
+        files_list->topLevelItem(i)->setDisabled(false);
+    }
 }
 
 void ArchiveWidget::slotArchivingProgress(KJob *, qulonglong size)
@@ -149,3 +288,57 @@ void ArchiveWidget::slotArchivingProgress(KJob *, qulonglong size)
     progressBar->setValue((int) 100 * size / m_requestedSize);
 }
 
+
+bool ArchiveWidget::processProjectFile()
+{
+    KUrl destUrl;
+    QTreeWidgetItem *item;
+    for (int i = 0; i < files_list->topLevelItemCount(); i++) {
+        if (files_list->topLevelItem(i)->childCount() > 0) {
+            destUrl = KUrl(archive_url->url().path(KUrl::AddTrailingSlash) + files_list->topLevelItem(i)->data(0, Qt::UserRole).toString());
+            for (int j = 0; j < files_list->topLevelItem(i)->childCount(); j++) {
+                item = files_list->topLevelItem(i)->child(j);
+                KUrl src(item->text(0));
+                KUrl dest = destUrl;
+                if (item->data(0, Qt::UserRole).isNull()) {
+                    dest.addPath(src.fileName());
+                }
+                else {
+                    dest.addPath(item->data(0, Qt::UserRole).toString());
+                }
+                m_replacementList.insert(src, dest);
+            }
+        }
+    }
+    // process kdenlive producers
+            
+    QDomElement mlt = m_doc.documentElement();
+    QDomNodeList prods = mlt.elementsByTagName("kdenlive_producer");
+    for (int i = 0; i < prods.count(); i++) {
+        QDomElement e = prods.item(i).toElement();
+        if (e.isNull()) continue;
+        if (e.hasAttribute("resource")) {
+            KUrl src(e.attribute("resource"));
+            KUrl dest = m_replacementList.value(src);
+            if (!dest.isEmpty()) e.setAttribute("resource", dest.path());
+        }
+    }
+
+    QString path = archive_url->url().path(KUrl::AddTrailingSlash) + "project.kdenlive";
+    QFile file(path);
+    if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
+        kWarning() << "//////  ERROR writing to file: " << path;
+        KMessageBox::error(kapp->activeWindow(), i18n("Cannot write to file %1", path));
+        return false;
+    }
+
+    file.write(m_doc.toString().toUtf8());
+    if (file.error() != QFile::NoError) {
+        KMessageBox::error(kapp->activeWindow(), i18n("Cannot write to file %1", path));
+        file.close();
+        return false;
+    }
+    file.close();
+    return true;
+}
+
index bbaf23c16cd518fca17a33b4505076f8d404975f..188f13207b31998d5e798cb523a6d7129ee09bae 100644 (file)
@@ -45,18 +45,26 @@ class ArchiveWidget : public QDialog, public Ui::ArchiveWidget_UI
     Q_OBJECT
 
 public:
-    ArchiveWidget(QList <DocClipBase*> list, QStringList lumas, QWidget * parent = 0);
+    ArchiveWidget(QDomDocument doc, QList <DocClipBase*> list, QStringList luma_list, QWidget * parent = 0);
     ~ArchiveWidget();
     
 private slots:
     void slotCheckSpace();
-    void slotStartArchiving();
+    bool slotStartArchiving(bool firstPass = true);
     void slotArchivingFinished(KJob *job);
     void slotArchivingProgress(KJob *, qulonglong);
 
 private:
     KIO::filesize_t m_requestedSize;
     KIO::CopyJob *m_copyJob;
+    QMap <KUrl, KUrl> m_duplicateFiles;
+    QMap <KUrl, KUrl> m_replacementList;
+    QDomDocument m_doc;
+
+    /** @brief Generate tree widget subitems from a string list of urls. */
+    void generateItems(QTreeWidgetItem *parentItem, QStringList items);
+    /** @brief Replace urls in project file. */
+    bool processProjectFile();
 
 signals:
 
index e953b2eb381392b51b0d3091a9cc9e34041b9946..a69fe31fc186eb2c144c3d8719dc9d6d07f64de4 100644 (file)
@@ -535,15 +535,14 @@ QPoint KdenliveDoc::zone() const
     return QPoint(m_documentProperties.value("zonein").toInt(), m_documentProperties.value("zoneout").toInt());
 }
 
-bool KdenliveDoc::saveSceneList(const QString &path, const QString &scene, const QStringList expandedFolders)
+QDomDocument KdenliveDoc::xmlSceneList(const QString &scene, const QStringList expandedFolders)
 {
     QDomDocument sceneList;
     sceneList.setContent(scene, true);
     QDomElement mlt = sceneList.firstChildElement("mlt");
     if (mlt.isNull() || !mlt.hasChildNodes()) {
-        //Make sure we don't save if scenelist is corrupted
-        KMessageBox::error(kapp->activeWindow(), i18n("Cannot write to file %1, scene list is corrupted.", path));
-        return false;
+        //scenelist is corrupted
+        return sceneList;
     }
 
     QDomElement addedXml = sceneList.createElement("kdenlivedoc");
@@ -658,6 +657,17 @@ bool KdenliveDoc::saveSceneList(const QString &path, const QString &scene, const
     addedXml.appendChild(sceneList.importNode(m_clipManager->groupsXml(), true));
 
     //wes.appendChild(doc.importNode(kdenliveData, true));
+    return sceneList;
+}
+
+bool KdenliveDoc::saveSceneList(const QString &path, const QString &scene, const QStringList expandedFolders)
+{
+    QDomDocument sceneList = xmlSceneList(scene, expandedFolders);
+    if (sceneList.isNull()) {
+        //Make sure we don't save if scenelist is corrupted
+        KMessageBox::error(kapp->activeWindow(), i18n("Cannot write to file %1, scene list is corrupted.", path));
+        return false;
+    }
 
     QFile file(path);
     if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
index f38051a814c855c4760966ffc6bf430760542bf4..1e06519340c4e06a71490e95f150fd01eb69275b 100644 (file)
@@ -112,6 +112,9 @@ Q_OBJECT public:
     QPoint zoom() const;
     double dar() const;
     double projectDuration() const;
+    /** @brief Returns the project file xml. */
+    QDomDocument xmlSceneList(const QString &scene, const QStringList expandedFolders);
+    /** @brief Saves the project file xml to a file. */
     bool saveSceneList(const QString &path, const QString &scene, const QStringList expandedFolders);
     int tracksCount() const;
     TrackInfo trackInfoAt(int ix) const;
index a02d12a4a74660d1472e093a774d01d52e7e034d..4c50fa607f64b27cc795ebecc8bc0b5fed7201be 100644 (file)
@@ -4249,7 +4249,8 @@ void MainWindow::slotInsertNotesTimecode()
 void MainWindow::slotArchiveProject()
 {
     QList <DocClipBase*> list = m_projectList->documentClipList();
-    ArchiveWidget *d = new ArchiveWidget(list, m_activeTimeline->projectView()->extractTransitionsLumas(), this);
+    QDomDocument doc = m_activeDocument->xmlSceneList(m_projectMonitor->sceneList(), m_projectList->expandedFolders());
+    ArchiveWidget *d = new ArchiveWidget(doc, list, m_activeTimeline->projectView()->extractTransitionsLumas(), this);
     d->exec();
 }
 
index 23b5bfd3dfd9303624747f50348a530a3167929e..1eee924418f98e934d0295c8591acc78b31ca7a3 100644 (file)
@@ -63,9 +63,6 @@
      </property>
     </widget>
    </item>
-   <item row="3" column="0" colspan="2">
-    <widget class="QListWidget" name="files_list"/>
-   </item>
    <item row="4" column="0">
     <widget class="QProgressBar" name="progressBar">
      <property name="value">
      </property>
     </widget>
    </item>
+   <item row="3" column="0" colspan="2">
+    <widget class="QTreeWidget" name="files_list">
+     <property name="alternatingRowColors">
+      <bool>true</bool>
+     </property>
+     <property name="selectionBehavior">
+      <enum>QAbstractItemView::SelectItems</enum>
+     </property>
+     <attribute name="headerVisible">
+      <bool>false</bool>
+     </attribute>
+     <column>
+      <property name="text">
+       <string notr="true">1</string>
+      </property>
+     </column>
+    </widget>
+   </item>
   </layout>
  </widget>
  <customwidgets>