Backup project file everytime it is saved, backup files can be browsed and...

Backup project file everytime it is saved, backup files can be browsed and opened from the Project menu.

svn path=/trunk/kdenlive/; revision=5664
parent 0395e7c3
......@@ -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
......
......@@ -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);
}
......@@ -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);
......
......@@ -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"
......@@ -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
<?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>
......
......@@ -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
......
......@@ -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);
};
......
<?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>
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment