]> git.sesse.net Git - kdenlive/commitdiff
Backup project file everytime it is saved, backup files can be browsed and opened...
authorJean-Baptiste Mardelle <jb@kdenlive.org>
Sun, 5 Jun 2011 21:54:05 +0000 (21:54 +0000)
committerJean-Baptiste Mardelle <jb@kdenlive.org>
Sun, 5 Jun 2011 21:54:05 +0000 (21:54 +0000)
svn path=/trunk/kdenlive/; revision=5664

src/CMakeLists.txt
src/customtrackview.cpp
src/customtrackview.h
src/kdenlivedoc.cpp
src/kdenlivedoc.h
src/kdenliveui.rc
src/mainwindow.cpp
src/mainwindow.h
src/widgets/backupdialog_ui.ui [new file with mode: 0644]

index f0072341c28a456295b3dddea302e07b4d1175b3..a17eec703ce42d715eff4075068b597c57da32b9 100644 (file)
@@ -136,6 +136,7 @@ kde4_add_ui_files(kdenlive_UI
   widgets/monitoreditwidget_ui.ui
   widgets/archivewidget_ui.ui
   widgets/manageencodingprofile_ui.ui
+  widgets/backupdialog_ui.ui
 )
 
 set(kdenlive_SRCS
index ab56e401d1f9cd4ca73441eea694b6cee097ebf0..6594e844d47283fc84dba294e2624cfd5197db83 100644 (file)
@@ -6693,3 +6693,17 @@ void CustomTrackView::adjustEffects(ClipItem* item, ItemInfo oldInfo, QUndoComma
         }
     }
 }
+
+
+void CustomTrackView::saveTimelinePreview(const QString path)
+{
+    QRect viewrect = viewport()->rect();
+    QImage img(viewrect.width(), viewrect.height(), QImage::Format_ARGB32_Premultiplied);
+    img.fill(palette().base().color().rgb());
+    QPainter painter(&img);
+    render(&painter);
+    painter.end();
+    img = img.scaledToWidth(600, Qt::SmoothTransformation);
+    img.save(path);
+}
+
index 1de28076e4fd5cded97b3d5eb137f771ad4b028b..0c47b3efd25789c41fbe8d5bbf9d83e7b06d8f33 100644 (file)
@@ -267,6 +267,8 @@ public slots:
     * @param offsetList The list of points that should also snap (for example when movin a clip, start and end points should snap
     * @param skipSelectedItems if true, the selected item start and end points will not be added to snap list */
     void updateSnapPoints(AbstractClipItem *selected, QList <GenTime> offsetList = QList <GenTime> (), bool skipSelectedItems = false);
+    /** @brief Save a snapshot image of current timeline view */
+    void saveTimelinePreview(const QString path);
 
 protected:
     virtual void drawBackground(QPainter * painter, const QRectF & rect);
index b33eccc3887f56d41c364c184ac2591a5a7e2822..7a4b9482698dc0d23adadadaf291e3848e554dc9 100644 (file)
@@ -509,7 +509,7 @@ void KdenliveDoc::slotAutoSave()
             kDebug() << "ERROR; CANNOT CREATE AUTOSAVE FILE";
         }
         kDebug() << "// AUTOSAVE FILE: " << m_autosave->fileName();
-        saveSceneList(m_autosave->fileName(), m_render->sceneList(), QStringList());
+        saveSceneList(m_autosave->fileName(), m_render->sceneList(), QStringList(), true);
     }
 }
 
@@ -660,7 +660,7 @@ QDomDocument KdenliveDoc::xmlSceneList(const QString &scene, const QStringList e
     return sceneList;
 }
 
-bool KdenliveDoc::saveSceneList(const QString &path, const QString &scene, const QStringList expandedFolders)
+bool KdenliveDoc::saveSceneList(const QString &path, const QString &scene, const QStringList expandedFolders, bool autosave)
 {
     QDomDocument sceneList = xmlSceneList(scene, expandedFolders);
     if (sceneList.isNull()) {
@@ -668,8 +668,11 @@ bool KdenliveDoc::saveSceneList(const QString &path, const QString &scene, const
         KMessageBox::error(kapp->activeWindow(), i18n("Cannot write to file %1, scene list is corrupted.", path));
         return false;
     }
-
+    
+    // Backup current version
+    if (!autosave) backupLastSavedVersion(path);
     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));
@@ -683,6 +686,9 @@ bool KdenliveDoc::saveSceneList(const QString &path, const QString &scene, const
         return false;
     }
     file.close();
+    if (!autosave) {
+        cleanupBackupFiles();
+    }
     return true;
 }
 
@@ -1576,5 +1582,124 @@ double KdenliveDoc::getDisplayRatio(const QString &path)
     return 0;
 }
 
+void KdenliveDoc::backupLastSavedVersion(const QString &path)
+{
+    // Ensure backup folder exists
+    QFile file(path);
+    KUrl backupFile = m_projectFolder;
+    backupFile.addPath(".backup/");
+    KIO::NetAccess::mkdir(backupFile, kapp->activeWindow());
+    QString fileName = KUrl(path).fileName().section('.', 0, -2);
+    QFileInfo info(file);
+    fileName.append(info.lastModified().toString("-yyyy-MM-dd-hh-mm"));
+    fileName.append(".kdenlive");
+    backupFile.addPath(fileName);
+
+    emit saveTimelinePreview(backupFile.path() + ".png");
+
+    if (file.exists()) {
+        // delete previous backup if it was done less than 60 seconds ago
+        QFile::remove(backupFile.path());
+        if (!QFile::copy(path, backupFile.path())) {
+            KMessageBox::information(kapp->activeWindow(), i18n("Cannot create backup copy:\n%1", backupFile.path()));
+        }
+    }    
+}
+
+void KdenliveDoc::cleanupBackupFiles()
+{
+    KUrl backupFile = m_projectFolder;
+    backupFile.addPath(".backup/");
+    QDir dir(backupFile.path());
+    QString projectFile = url().fileName().section('.', 0, -2);
+    projectFile.append("-??");
+    projectFile.append("??");
+    projectFile.append("-??");
+    projectFile.append("-??");
+    projectFile.append("-??");
+    projectFile.append("-??.kdenlive");
+
+    QStringList filter;
+    backupFile.addPath(projectFile);
+    filter << projectFile;
+    dir.setNameFilters(filter);
+    QFileInfoList resultList = dir.entryInfoList(QDir::Files, QDir::Time);
+
+    QDateTime d = QDateTime::currentDateTime();
+    QStringList hourList;
+    QStringList dayList;
+    QStringList weekList;
+    QStringList oldList;
+    for (int i = 0; i < resultList.count(); i++) {
+        if (d.secsTo(resultList.at(i).lastModified()) < 3600) {
+            // files created in the last hour
+            hourList.append(resultList.at(i).absoluteFilePath());
+        }
+        else if (d.secsTo(resultList.at(i).lastModified()) < 43200) {
+            // files created in the day
+            dayList.append(resultList.at(i).absoluteFilePath());
+        }
+        else if (d.daysTo(resultList.at(i).lastModified()) < 8) {
+            // files created in the week
+            weekList.append(resultList.at(i).absoluteFilePath());
+        }
+        else {
+            // older files
+            oldList.append(resultList.at(i).absoluteFilePath());
+        }
+    }
+    if (hourList.count() > 20) {
+        int step = hourList.count() / 10;
+        for (int i = 0; i < hourList.count(); i += step) {
+            kDebug()<<"REMOVE AT: "<<i<<", COUNT: "<<hourList.count();
+            hourList.removeAt(i);
+            i--;
+        }
+    } else hourList.clear();
+    if (dayList.count() > 20) {
+        int step = dayList.count() / 10;
+        for (int i = 0; i < dayList.count(); i += step) {
+            dayList.removeAt(i);
+            i--;
+        }
+    } else dayList.clear();
+    if (weekList.count() > 20) {
+        int step = weekList.count() / 10;
+        for (int i = 0; i < weekList.count(); i += step) {
+            weekList.removeAt(i);
+            i--;
+        }
+    } else weekList.clear();
+    if (oldList.count() > 20) {
+        int step = oldList.count() / 10;
+        for (int i = 0; i < oldList.count(); i += step) {
+            oldList.removeAt(i);
+            i--;
+        }
+    } else oldList.clear();
+    
+    QString f;
+    while (hourList.count() > 0) {
+        f = hourList.takeFirst();
+        QFile::remove(f);
+        QFile::remove(f + ".png");
+    }
+    while (dayList.count() > 0) {
+        f = dayList.takeFirst();
+        QFile::remove(f);
+        QFile::remove(f + ".png");
+    }
+    while (weekList.count() > 0) {
+        f = weekList.takeFirst();
+        QFile::remove(f);
+        QFile::remove(f + ".png");
+    }
+    while (oldList.count() > 0) {
+        f = oldList.takeFirst();
+        QFile::remove(f);
+        QFile::remove(f + ".png");
+    }
+}
+
 #include "kdenlivedoc.moc"
 
index 3392519afc8bfba011ae6773dff2689ec44089fa..e3b914a72fe576b29297b1ec3f9fd4519110e955 100644 (file)
@@ -115,7 +115,7 @@ Q_OBJECT public:
     /** @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);
+    bool saveSceneList(const QString &path, const QString &scene, const QStringList expandedFolders, bool autosave = false);
     int tracksCount() const;
     TrackInfo trackInfoAt(int ix) const;
     void insertTrack(int ix, TrackInfo type);
@@ -161,6 +161,8 @@ Q_OBJECT public:
     QStringList getExpandedFolders();
     /** @brief Read the display ratio from an xml project file. */
     static double getDisplayRatio(const QString &path);
+    /** @brief Backup the project file */
+    void backupLastSavedVersion(const QString &path);
     
 private:
     KUrl m_url;
@@ -199,6 +201,8 @@ private:
 
     /** @brief Updates the project folder location entry in the kdenlive file dialogs to point to the current project folder. */
     void updateProjectFolderPlacesEntry();
+    /** @brief Only keep some backup files, delete some */
+    void cleanupBackupFiles();
 
 public slots:
     void slotCreateXmlClip(const QString &name, const QDomElement xml, QString group, const QString &groupId);
@@ -234,6 +238,8 @@ signals:
     void docModified(bool);
     void selectLastAddedClip(const QString &);
     void guidesUpdated();
+    /** @brief When creating a backup file, also save a thumbnail of current timeline */
+    void saveTimelinePreview(const QString path);
 };
 
 #endif
index 5a7eb602d027948cd5e0a355e9b740c5d34dfe1e..e676813c079ff0eea9c492f20bdb59062e9897cb 100644 (file)
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!DOCTYPE kpartgui SYSTEM "kpartgui.dtd">
-<gui name="kdenlive" version="61">
+<gui name="kdenlive" version="62">
   <ToolBar name="extraToolBar" >
     <text>Extra Toolbar</text>
        <Action name="project_render" />
@@ -49,6 +49,7 @@
       <Action name="project_render" />
       <Action name="project_adjust_profile" />
       <Action name="project_settings" />
+      <Action name="open_backup" />
     </Menu>
 
     <Menu name="tool" ><text>Tool</text>
index 7ebd999071b4f01f2248737324271d40eccc17ab..f9918685ba363fb2a5a613aa7b1150dad40bc16b 100644 (file)
@@ -146,7 +146,8 @@ MainWindow::MainWindow(const QString &MltPath, const KUrl & Url, const QString &
     m_jogShuttle(NULL),
 #endif /* NO_JOGSHUTTLE */
     m_findActivated(false),
-    m_stopmotion(NULL)
+    m_stopmotion(NULL),
+    m_backup_ui(NULL)
 {
     qRegisterMetaType<QVector<int16_t> > ();
     // Create DBus interface
@@ -1161,6 +1162,10 @@ void MainWindow::setupActions()
     collection.addAction("project_settings", projectAction);
     connect(projectAction, SIGNAL(triggered(bool)), this, SLOT(slotEditProjectSettings()));
 
+    KAction* backupAction = new KAction(KIcon("edit-undo"), i18n("Open Backup File"), this);
+    collection.addAction("open_backup", backupAction);
+    connect(backupAction, SIGNAL(triggered(bool)), this, SLOT(slotOpenBackupDialog()));
+
     KAction* projectRender = new KAction(KIcon("media-record"), i18n("Render"), this);
     collection.addAction("project_render", projectRender);
     projectRender->setShortcut(Qt::CTRL + Qt::Key_Return);
@@ -2457,6 +2462,7 @@ void MainWindow::connectDocument(TrackView *trackView, KdenliveDoc *doc)   //cha
 
     connect(doc, SIGNAL(docModified(bool)), this, SLOT(slotUpdateDocumentState(bool)));
     connect(doc, SIGNAL(guidesUpdated()), this, SLOT(slotGuidesUpdated()));
+    connect(doc, SIGNAL(saveTimelinePreview(const QString)), trackView->projectView(), SLOT(saveTimelinePreview(const QString)));
     connect(m_notesWidget, SIGNAL(textChanged()), doc, SLOT(setModified()));
 
     connect(trackView->projectView(), SIGNAL(clipItemSelected(ClipItem*, int, bool)), m_effectStack, SLOT(slotClipItemSelected(ClipItem*, int)));
@@ -4259,6 +4265,59 @@ void MainWindow::slotArchiveProject()
 }
 
 
+void MainWindow::slotOpenBackupDialog()
+{
+    QDialog *dia = new QDialog(this);
+    m_backup_ui = new Ui::BackupDialog_UI;
+    m_backup_ui->setupUi(dia);
+    dia->setWindowTitle(i18n("Backup Files"));
+    KUrl backupFile = m_activeDocument->projectFolder();
+    backupFile.addPath(".backup/");
+    QDir dir(backupFile.path());
+    QString projectFile = m_activeDocument->url().fileName().section('.', 0, -2);
+    projectFile.append("-??");
+    projectFile.append("??");
+    projectFile.append("-??");
+    projectFile.append("-??");
+    projectFile.append("-??");
+    projectFile.append("-??.kdenlive");
+
+    QStringList filter;
+    backupFile.addPath(projectFile);
+    filter << projectFile;
+    dir.setNameFilters(filter);
+    QFileInfoList resultList = dir.entryInfoList(QDir::Files, QDir::Time);
+    QStringList results;
+    QListWidgetItem *item;
+    for (int i = 0; i < resultList.count(); i++) {
+        item = new QListWidgetItem(resultList.at(i).lastModified().toString(Qt::DefaultLocaleLongDate), m_backup_ui->backup_list);
+        item->setData(Qt::UserRole, resultList.at(i).absoluteFilePath());
+    }
+    connect(m_backup_ui->backup_list, SIGNAL(currentRowChanged(int)), this, SLOT(slotDisplayBackupPreview()));
+    m_backup_ui->backup_list->setCurrentRow(0);
+    m_backup_ui->backup_list->setMinimumHeight(QFontMetrics(font()).lineSpacing() * 12);
+    if (dia->exec() == QDialog::Accepted) {
+        QString requestedBackup = m_backup_ui->backup_list->currentItem()->data(Qt::UserRole).toString();
+        KUrl currentUrl = m_activeDocument->url();
+        m_activeDocument->backupLastSavedVersion(currentUrl.path());
+        closeCurrentDocument(false);
+        doOpenFile(KUrl(requestedBackup), NULL);
+        m_activeDocument->setUrl(currentUrl);
+        setCaption(m_activeDocument->description());
+    }
+    delete dia;
+    delete m_backup_ui;
+    m_backup_ui = NULL;
+}
+
+void MainWindow::slotDisplayBackupPreview()
+{
+    QString path = m_backup_ui->backup_list->currentItem()->data(Qt::UserRole).toString();
+    QPixmap pix(path + ".png");
+    m_backup_ui->backup_preview->setPixmap(pix);
+}
+
+
 #include "mainwindow.moc"
 
 #ifdef DEBUG_MAINW
index f4d0d3b478be0a7f1989d79d2f0deabd258447f9..6bc6f7df520caa80c8d0e602acde6907e322670c 100644 (file)
@@ -46,6 +46,7 @@
 #include "dvdwizard.h"
 #include "stopmotion/stopmotion.h"
 #include "noteswidget.h"
+#include "ui_backupdialog_ui.h"
 
 class KdenliveDoc;
 class TrackView;
@@ -305,6 +306,9 @@ private:
 
     StopmotionWidget *m_stopmotion;
 
+    /** @brief UI for backup dialog. */
+    Ui::BackupDialog_UI *m_backup_ui;
+
 public slots:
     /** @brief Prepares opening @param url.
     *
@@ -540,6 +544,10 @@ private slots:
     void slotUpdateProxySettings();
     /** @brief Insert current project's timecode into the notes widget. */
     void slotInsertNotesTimecode();
+    /** @brief Open the project's backupdialog. */
+    void slotOpenBackupDialog();
+    /** @brief Display chosen backup thumbnail. */
+    void slotDisplayBackupPreview();
 signals:
     Q_SCRIPTABLE void abortRenderJob(const QString &url);
 };
diff --git a/src/widgets/backupdialog_ui.ui b/src/widgets/backupdialog_ui.ui
new file mode 100644 (file)
index 0000000..0467ba7
--- /dev/null
@@ -0,0 +1,88 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>BackupDialog_UI</class>
+ <widget class="QDialog" name="BackupDialog_UI">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>485</width>
+    <height>216</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Dialog</string>
+  </property>
+  <layout class="QGridLayout" name="gridLayout">
+   <item row="2" column="0">
+    <widget class="QDialogButtonBox" name="buttonBox">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+     <property name="standardButtons">
+      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Open</set>
+     </property>
+    </widget>
+   </item>
+   <item row="0" column="0">
+    <widget class="KListWidget" name="backup_list">
+     <property name="alternatingRowColors">
+      <bool>true</bool>
+     </property>
+    </widget>
+   </item>
+   <item row="1" column="0">
+    <widget class="QLabel" name="backup_preview">
+     <property name="frameShape">
+      <enum>QFrame::Box</enum>
+     </property>
+     <property name="text">
+      <string/>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>KListWidget</class>
+   <extends>QListWidget</extends>
+   <header>klistwidget.h</header>
+  </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>accepted()</signal>
+   <receiver>BackupDialog_UI</receiver>
+   <slot>accept()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>248</x>
+     <y>254</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>157</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>rejected()</signal>
+   <receiver>BackupDialog_UI</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>316</x>
+     <y>260</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>286</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>