Use KAutosaveFile for document recovery

Fix possible crash because of uninitialized video profile or opening invalid file

svn path=/branches/KDE4/; revision=2387
parent a97b0c01
......@@ -37,7 +37,7 @@
#include "mainwindow.h"
KdenliveDoc::KdenliveDoc(const KUrl &url, const KUrl &projectFolder, QUndoGroup *undoGroup, MainWindow *parent): QObject(parent), m_render(NULL), m_url(url), m_projectFolder(projectFolder), m_commandStack(new QUndoStack(undoGroup)), m_modified(false), m_documentLoadingProgress(0), m_documentLoadingStep(0.0), m_startPos(0), m_zoom(7) {
KdenliveDoc::KdenliveDoc(const KUrl &url, const KUrl &projectFolder, QUndoGroup *undoGroup, const QString &profileName, MainWindow *parent): QObject(parent), m_render(NULL), m_url(url), m_projectFolder(projectFolder), m_commandStack(new QUndoStack(undoGroup)), m_modified(false), m_documentLoadingProgress(0), m_documentLoadingStep(0.0), m_startPos(0), m_zoom(7), m_autosave(NULL) {
m_clipManager = new ClipManager(this);
if (!url.isEmpty()) {
QString tmpFile;
......@@ -110,90 +110,22 @@ KdenliveDoc::KdenliveDoc(const KUrl &url, const KUrl &projectFolder, QUndoGroup
m_document.removeChild(infoXmlNode);
kDebug() << "Reading file: " << url.path() << ", found clips: " << producers.count();
} else kWarning() << " NO KDENLIVE INFO FOUND IN FILE: " << url.path();
} else {
parent->slotGotProgressInfo(i18n("File %1 is not a Kdenlive project file."), 100);
kWarning() << " NO KDENLIVE INFO FOUND IN FILE: " << url.path();
m_document = createEmptyDocument();
setProfilePath(profileName);
}
KIO::NetAccess::removeTempFile(tmpFile);
} else {
KMessageBox::error(parent, KIO::NetAccess::lastErrorString());
parent->slotGotProgressInfo(i18n("File %1 is not a Kdenlive project file."), 100);
m_document = createEmptyDocument();
setProfilePath(profileName);
}
} else {
// Creating new document
QDomElement westley = m_document.createElement("westley");
m_document.appendChild(westley);
QDomElement tractor = m_document.createElement("tractor");
tractor.setAttribute("id", "maintractor");
QDomElement multitrack = m_document.createElement("multitrack");
QDomElement playlist = m_document.createElement("playlist");
playlist.setAttribute("id", "black_track");
westley.appendChild(playlist);
// create playlists
int audiotracks = 2;
int videotracks = 3;
int total = audiotracks + videotracks + 1;
for (int i = 1; i < total; i++) {
QDomElement playlist = m_document.createElement("playlist");
playlist.setAttribute("id", "playlist" + QString::number(i));
westley.appendChild(playlist);
}
QDomElement track0 = m_document.createElement("track");
track0.setAttribute("producer", "black_track");
tractor.appendChild(track0);
// create audio tracks
for (int i = 1; i < audiotracks + 1; i++) {
QDomElement track = m_document.createElement("track");
track.setAttribute("producer", "playlist" + QString::number(i));
track.setAttribute("hide", "video");
tractor.appendChild(track);
}
// create video tracks
for (int i = audiotracks + 1; i < total; i++) {
QDomElement track = m_document.createElement("track");
track.setAttribute("producer", "playlist" + QString::number(i));
tractor.appendChild(track);
}
for (uint i = 2; i < total ; i++) {
QDomElement transition = m_document.createElement("transition");
transition.setAttribute("always_active", "1");
QDomElement property = m_document.createElement("property");
property.setAttribute("name", "a_track");
QDomText value = m_document.createTextNode(QString::number(1));
property.appendChild(value);
transition.appendChild(property);
property = m_document.createElement("property");
property.setAttribute("name", "b_track");
value = m_document.createTextNode(QString::number(i));
property.appendChild(value);
transition.appendChild(property);
property = m_document.createElement("property");
property.setAttribute("name", "mlt_service");
value = m_document.createTextNode("mix");
property.appendChild(value);
transition.appendChild(property);
property = m_document.createElement("property");
property.setAttribute("name", "combine");
value = m_document.createTextNode("1");
property.appendChild(value);
transition.appendChild(property);
property = m_document.createElement("property");
property.setAttribute("name", "internal_added");
value = m_document.createTextNode("237");
property.appendChild(value);
transition.appendChild(property);
tractor.appendChild(transition);
}
westley.appendChild(tractor);
m_document = createEmptyDocument();
setProfilePath(profileName);
}
m_scenelist = m_document.toString();
kDebug() << "KDEnnlive document, init timecode: " << m_fps;
......@@ -202,10 +134,6 @@ KdenliveDoc::KdenliveDoc(const KUrl &url, const KUrl &projectFolder, QUndoGroup
m_autoSaveTimer = new QTimer(this);
m_autoSaveTimer->setSingleShot(true);
QString directory = m_url.directory();
QString fileName = m_url.fileName();
m_recoveryUrl.setDirectory(directory);
m_recoveryUrl.setFileName("~" + fileName);
connect(m_autoSaveTimer, SIGNAL(timeout()), this, SLOT(slotAutoSave()));
}
......@@ -213,13 +141,93 @@ KdenliveDoc::~KdenliveDoc() {
delete m_commandStack;
delete m_clipManager;
delete m_autoSaveTimer;
if (!m_url.isEmpty()) {
// remove backup file
if (KIO::NetAccess::exists(m_recoveryUrl, KIO::NetAccess::SourceSide, NULL))
KIO::NetAccess::del(m_recoveryUrl, NULL);
m_autosave->remove();
}
QDomDocument KdenliveDoc::createEmptyDocument() {
// Creating new document
QDomDocument doc;
QDomElement westley = doc.createElement("westley");
doc.appendChild(westley);
QDomElement tractor = doc.createElement("tractor");
tractor.setAttribute("id", "maintractor");
QDomElement multitrack = doc.createElement("multitrack");
QDomElement playlist = doc.createElement("playlist");
playlist.setAttribute("id", "black_track");
westley.appendChild(playlist);
// create playlists
const int audiotracks = 2;
const int videotracks = 3;
int total = audiotracks + videotracks + 1;
for (int i = 1; i < total; i++) {
QDomElement playlist = doc.createElement("playlist");
playlist.setAttribute("id", "playlist" + QString::number(i));
westley.appendChild(playlist);
}
QDomElement track0 = doc.createElement("track");
track0.setAttribute("producer", "black_track");
tractor.appendChild(track0);
// create audio tracks
for (int i = 1; i < audiotracks + 1; i++) {
QDomElement track = doc.createElement("track");
track.setAttribute("producer", "playlist" + QString::number(i));
track.setAttribute("hide", "video");
tractor.appendChild(track);
}
// create video tracks
for (int i = audiotracks + 1; i < total; i++) {
QDomElement track = doc.createElement("track");
track.setAttribute("producer", "playlist" + QString::number(i));
tractor.appendChild(track);
}
for (uint i = 2; i < total ; i++) {
QDomElement transition = doc.createElement("transition");
transition.setAttribute("always_active", "1");
QDomElement property = doc.createElement("property");
property.setAttribute("name", "a_track");
QDomText value = doc.createTextNode(QString::number(1));
property.appendChild(value);
transition.appendChild(property);
property = doc.createElement("property");
property.setAttribute("name", "b_track");
value = doc.createTextNode(QString::number(i));
property.appendChild(value);
transition.appendChild(property);
property = doc.createElement("property");
property.setAttribute("name", "mlt_service");
value = doc.createTextNode("mix");
property.appendChild(value);
transition.appendChild(property);
property = doc.createElement("property");
property.setAttribute("name", "combine");
value = doc.createTextNode("1");
property.appendChild(value);
transition.appendChild(property);
property = doc.createElement("property");
property.setAttribute("name", "internal_added");
value = doc.createTextNode("237");
property.appendChild(value);
transition.appendChild(property);
tractor.appendChild(transition);
}
westley.appendChild(tractor);
return doc;
}
void KdenliveDoc::syncGuides(QList <Guide *> guides) {
QDomDocument doc;
QDomElement e;
......@@ -234,9 +242,14 @@ void KdenliveDoc::syncGuides(QList <Guide *> guides) {
}
void KdenliveDoc::slotAutoSave() {
if (m_render)
m_render->saveSceneList(m_recoveryUrl.path(), documentInfoXml());
if (m_render) {
if (!m_autosave->isOpen() && !m_autosave->open(QIODevice::ReadWrite)) {
// show error: could not open the autosave file
kDebug() << "ERROR; CANNOT CREATE AUTOSAVE FILE";
}
kDebug() << "// AUTOSAVE FILE: " << m_autosave->fileName();
m_render->saveSceneList(m_autosave->fileName(), documentInfoXml());
}
}
void KdenliveDoc::setZoom(int factor) {
......@@ -630,10 +643,6 @@ KUrl KdenliveDoc::url() const {
void KdenliveDoc::setUrl(KUrl url) {
m_url = url;
QString directory = m_url.directory();
QString fileName = m_url.fileName();
m_recoveryUrl.setDirectory(directory);
m_recoveryUrl.setFileName("~" + fileName);
}
void KdenliveDoc::setModified(bool mod) {
......
......@@ -31,6 +31,7 @@
#include <QTimer>
#include <KUrl>
#include <kautosavefile.h>
#include "gentime.h"
#include "timecode.h"
......@@ -45,13 +46,14 @@ class MainWindow;
class KdenliveDoc: public QObject {
Q_OBJECT public:
KdenliveDoc(const KUrl &url, const KUrl &projectFolder, QUndoGroup *undoGroup, MainWindow *parent = 0);
KdenliveDoc(const KUrl &url, const KUrl &projectFolder, QUndoGroup *undoGroup, const QString &profileName, MainWindow *parent = 0);
~KdenliveDoc();
QDomNodeList producersList();
double fps() const;
int width() const;
int height() const;
KUrl url() const;
KAutoSaveFile *m_autosave;
void backupMltPlaylist();
Timecode timecode() const;
QDomDocument toXml() const;
......@@ -107,7 +109,6 @@ Q_OBJECT public:
private:
KUrl m_url;
KUrl m_recoveryUrl;
QDomDocument m_document;
QString m_projectName;
double m_fps;
......@@ -131,6 +132,7 @@ private:
double m_documentLoadingStep;
double m_documentLoadingProgress;
void convertDocument(double version);
QDomDocument createEmptyDocument();
public slots:
void slotCreateTextClip(QString group, const QString &groupId);
......
......@@ -278,7 +278,17 @@ MainWindow::MainWindow(QWidget *parent)
if (KdenliveSettings::openlastproject()) {
openLastFile();
} else newFile();
} else {
/*QList<KAutoSaveFile *> staleFiles = KAutoSaveFile::allStaleFiles();
if (!staleFiles.isEmpty()) {
if (KMessageBox::questionYesNo(this, i18n("Auto-saved files exist. Do you want to recover them now?"), i18n("File Recovery"), KGuiItem(i18n("Recover")), KGuiItem(i18n("Don't recover"))) == KMessageBox::Yes) {
recoverFiles(staleFiles);
}
else newFile();
}
else*/
newFile();
}
activateShuttleDevice();
projectListDock->raise();
......@@ -405,7 +415,7 @@ void MainWindow::slotSetClipDuration(const QString &id, int duration) {
void MainWindow::slotConnectMonitors() {
m_projectList->setRenderer(m_clipMonitor->render);
connect(m_projectList, SIGNAL(receivedClipDuration(int, int)), this, SLOT(slotSetClipDuration(int, int)));
connect(m_projectList, SIGNAL(receivedClipDuration(const QString &, int)), this, SLOT(slotSetClipDuration(const QString &, int)));
connect(m_projectList, SIGNAL(showClipProperties(DocClipBase *)), this, SLOT(slotShowClipProperties(DocClipBase *)));
connect(m_projectList, SIGNAL(getFileProperties(const QDomElement &, const QString &)), m_clipMonitor->render, SLOT(getFileProperties(const QDomElement &, const QString &)));
connect(m_clipMonitor->render, SIGNAL(replyGetImage(const QString &, int, const QPixmap &, int, int)), m_projectList, SLOT(slotReplyGetImage(const QString &, int, const QPixmap &, int, int)));
......@@ -773,8 +783,8 @@ void MainWindow::newFile() {
projectFolder = w->selectedFolder();
delete w;
}
KdenliveDoc *doc = new KdenliveDoc(KUrl(), projectFolder, m_commandStack, this);
doc->setProfilePath(profileName);
KdenliveDoc *doc = new KdenliveDoc(KUrl(), projectFolder, m_commandStack, profileName, this);
doc->m_autosave = new KAutoSaveFile(KUrl(), doc);
TrackView *trackView = new TrackView(doc, this);
m_timelineArea->addTab(trackView, KIcon("kdenlive"), doc->description());
if (m_timelineArea->count() == 1) {
......@@ -807,6 +817,9 @@ void MainWindow::slotRemoveTab() {
void MainWindow::saveFileAs(const QString &outputFileName) {
m_projectMonitor->saveSceneList(outputFileName, m_activeDocument->documentInfoXml());
m_activeDocument->setUrl(KUrl(outputFileName));
if (m_activeDocument->m_autosave == NULL) {
m_activeDocument->m_autosave = new KAutoSaveFile(KUrl(outputFileName), this);
} else m_activeDocument->m_autosave->setManagedFile(KUrl(outputFileName));
setCaption(m_activeDocument->description());
m_timelineArea->setTabText(m_timelineArea->currentIndex(), m_activeDocument->description());
m_timelineArea->setTabToolTip(m_timelineArea->currentIndex(), m_activeDocument->url().path());
......@@ -828,6 +841,7 @@ void MainWindow::saveFile() {
saveFileAs();
} else {
saveFileAs(m_activeDocument->url().path());
m_activeDocument->m_autosave->resize(0);
}
}
......@@ -847,32 +861,38 @@ void MainWindow::openLastFile() {
void MainWindow::openFile(const KUrl &url) {
// Check for backup file
bool recovery = false;
QString directory = url.directory();
QString fileName = url.fileName();
KUrl recoveryUrl;
recoveryUrl.setDirectory(directory);
recoveryUrl.setFileName("~" + fileName);
if (KIO::NetAccess::exists(recoveryUrl, KIO::NetAccess::SourceSide, this)) {
KFileItem bkup(KFileItem::Unknown, KFileItem::Unknown, recoveryUrl, true);
KFileItem src(KFileItem::Unknown, KFileItem::Unknown, url, true);
if (bkup.time(KFileItem::ModificationTime) > src.time(KFileItem::ModificationTime)) {
// Backup file is more recent than source file, ask user for recovery
if (KMessageBox::questionYesNo(this, i18n("A newer recovery file exists for <b>%1</b>\nOpen recovery file ?", url.fileName())) == KMessageBox::Yes) recovery = true;
QList<KAutoSaveFile *> staleFiles = KAutoSaveFile::staleFiles(url);
if (!staleFiles.isEmpty()) {
if (KMessageBox::questionYesNo(this,
i18n("Auto-saved files exist. Do you want to recover them now?"),
i18n("File Recovery"),
KGuiItem(i18n("Recover")), KGuiItem(i18n("Don't recover"))) == KMessageBox::Yes) {
recoverFiles(staleFiles);
return;
} else {
// remove the stale files
foreach(KAutoSaveFile *stale, staleFiles) {
stale->open(QIODevice::ReadWrite);
delete stale;
}
}
}
doOpenFile(url, NULL);
}
//TODO: get video profile from url before opening it
/*MltVideoProfile prof = ProfilesDialog::getVideoProfile(KdenliveSettings::default_profile());
if (prof.width == 0) prof = ProfilesDialog::getVideoProfile("dv_pal");
KdenliveSettings::setCurrent_profile(prof.path);*/
void MainWindow::doOpenFile(const KUrl &url, KAutoSaveFile *stale) {
KdenliveDoc *doc;
if (recovery) {
doc = new KdenliveDoc(recoveryUrl, KUrl(), m_commandStack, this);
doc->setUrl(url);
doc = new KdenliveDoc(url, KUrl(), m_commandStack, QString(), this);
if (stale == NULL) {
stale = new KAutoSaveFile(url, doc);
doc->m_autosave = stale;
} else {
doc->m_autosave = stale;
doc->setUrl(stale->managedFile());
doc->setModified(true);
} else doc = new KdenliveDoc(url, KUrl(), m_commandStack, this);
stale->setParent(doc);
}
connectDocumentInfo(doc);
TrackView *trackView = new TrackView(doc, this);
m_timelineArea->setCurrentIndex(m_timelineArea->addTab(trackView, KIcon("kdenlive"), doc->description()));
......@@ -882,6 +902,21 @@ void MainWindow::openFile(const KUrl &url) {
m_projectMonitor->refreshMonitor(true);
}
void MainWindow::recoverFiles(QList<KAutoSaveFile *> staleFiles) {
foreach(KAutoSaveFile *stale, staleFiles) {
/*if (!stale->open(QIODevice::QIODevice::ReadOnly)) {
// show an error message; we could not steal the lockfile
// maybe another application got to the file before us?
delete stale;
continue;
}*/
kDebug() << "// OPENING RECOVERY: " << stale->fileName() << "\nMANAGED: " << stale->managedFile().path();
// the stalefiles also contain ".lock" files so we must ignore them... bug in KAutoSaveFile ?
if (!stale->fileName().endsWith(".lock")) doOpenFile(KUrl(stale->fileName()), stale);
else KIO::NetAccess::del(KUrl(stale->fileName()), this);
}
}
void MainWindow::parseProfiles() {
//kdDebug()<<" + + YOUR MLT INSTALL WAS FOUND IN: "<< MLT_PREFIX <<endl;
......@@ -950,9 +985,8 @@ void MainWindow::slotEditProjectSettings() {
KdenliveSettings::setCurrent_profile(profile);
KdenliveSettings::setProject_fps(m_activeDocument->fps());
setCaption(m_activeDocument->description());
m_monitorManager->resetProfiles();
m_monitorManager->resetProfiles(m_activeDocument->timecode());
if (m_renderWidget) m_renderWidget->setDocumentStandard(m_activeDocument->getDocumentStandard());
m_monitorManager->setTimecode(m_activeDocument->timecode());
m_timelineArea->setTabText(m_timelineArea->currentIndex(), m_activeDocument->description());
// We need to desactivate & reactivate monitors to get a refresh
......@@ -1069,7 +1103,7 @@ void MainWindow::connectDocument(TrackView *trackView, KdenliveDoc *doc) { //cha
}
KdenliveSettings::setCurrent_profile(doc->profilePath());
KdenliveSettings::setProject_fps(doc->fps());
m_monitorManager->resetProfiles();
m_monitorManager->resetProfiles(doc->timecode());
m_projectList->setDocument(doc);
connect(m_projectList, SIGNAL(clipSelected(DocClipBase *)), m_clipMonitor, SLOT(slotSetXml(DocClipBase *)));
connect(trackView, SIGNAL(cursorMoved()), m_projectMonitor, SLOT(activateMonitor()));
......@@ -1113,7 +1147,6 @@ void MainWindow::connectDocument(TrackView *trackView, KdenliveDoc *doc) { //cha
trackView->projectView()->setContextMenu(m_timelineContextMenu, m_timelineContextClipMenu, m_timelineContextTransitionMenu);
m_activeTimeline = trackView;
if (m_renderWidget) m_renderWidget->setDocumentStandard(doc->getDocumentStandard());
m_monitorManager->setTimecode(doc->timecode());
doc->setRenderer(m_projectMonitor->render);
m_commandStack->setActiveStack(doc->commandStack());
KdenliveSettings::setProject_display_ratio(doc->dar());
......@@ -1143,7 +1176,7 @@ void MainWindow::slotPreferences(int page, int option) {
// create it :
KdenliveSettingsDialog* dialog = new KdenliveSettingsDialog(this);
connect(dialog, SIGNAL(settingsChanged(const QString&)), this, SLOT(updateConfiguration()));
connect(dialog, SIGNAL(doResetProfile()), m_monitorManager, SLOT(resetProfiles()));
connect(dialog, SIGNAL(doResetProfile()), m_monitorManager, SLOT(slotResetProfiles()));
dialog->show();
if (page != -1) dialog->showPage(page, option);
}
......
......@@ -35,6 +35,7 @@
#include <KUndoStack>
#include <KRecentFilesAction>
#include <KComboBox>
#include <kautosavefile.h>
#include "effectslist.h"
#include "gentime.h"
......@@ -158,6 +159,8 @@ private:
void slotShuttleAction(int code);
void connectDocumentInfo(KdenliveDoc *doc);
void findAhead();
void doOpenFile(const KUrl &url, KAutoSaveFile *stale);
void recoverFiles(QList<KAutoSaveFile *> staleFiles);
public slots:
void openFile(const KUrl &url);
......
......@@ -28,10 +28,6 @@ MonitorManager::MonitorManager(QWidget *parent)
: QObject(parent) {
}
void MonitorManager::setTimecode(Timecode tc) {
m_timecode = tc;
}
Timecode MonitorManager::timecode() {
return m_timecode;
}
......@@ -111,7 +107,12 @@ void MonitorManager::slotEnd() {
else m_projectMonitor->slotEnd();
}
void MonitorManager::resetProfiles() {
void MonitorManager::resetProfiles(Timecode tc) {
m_timecode = tc;
slotResetProfiles();
}
void MonitorManager::slotResetProfiles() {
activateMonitor("clip");
m_clipMonitor->resetProfile();
activateMonitor("project");
......
......@@ -33,9 +33,9 @@ public:
MonitorManager(QWidget *parent = 0);
void initMonitors(Monitor *clipMonitor, Monitor *projectMonitor);
Timecode timecode();
void setTimecode(Timecode tc);
void switchMonitors();
bool projectMonitorFocused();
void resetProfiles(Timecode tc);
public slots:
void activateMonitor(QString name = QString::null);
......@@ -46,7 +46,7 @@ public slots:
void slotForwardOneFrame();
void slotStart();
void slotEnd();
void resetProfiles();
void slotResetProfiles();
private:
Monitor *m_clipMonitor;
......
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