widgets/monitoreditwidget_ui.ui
widgets/archivewidget_ui.ui
widgets/manageencodingprofile_ui.ui
+ widgets/backupdialog_ui.ui
)
set(kdenlive_SRCS
}
}
}
+
+
+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);
+}
+
* @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);
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);
}
}
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()) {
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));
return false;
}
file.close();
+ if (!autosave) {
+ cleanupBackupFiles();
+ }
return true;
}
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"
/** @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);
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;
/** @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);
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" />
<Action name="project_render" />
<Action name="project_adjust_profile" />
<Action name="project_settings" />
+ <Action name="open_backup" />
</Menu>
<Menu name="tool" ><text>Tool</text>
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
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);
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)));
}
+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
#include "dvdwizard.h"
#include "stopmotion/stopmotion.h"
#include "noteswidget.h"
+#include "ui_backupdialog_ui.h"
class KdenliveDoc;
class TrackView;
StopmotionWidget *m_stopmotion;
+ /** @brief UI for backup dialog. */
+ Ui::BackupDialog_UI *m_backup_ui;
+
public slots:
/** @brief Prepares opening @param url.
*
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);
};
--- /dev/null
+<?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>