From 3ec710471ae45caa311fae972c02aece12e1c799 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Mardelle Date: Fri, 29 Aug 2008 21:42:05 +0000 Subject: [PATCH] Use KAutosaveFile for document recovery Fix possible crash because of uninitialized video profile or opening invalid file svn path=/branches/KDE4/; revision=2387 --- src/kdenlivedoc.cpp | 199 +++++++++++++++++++++-------------------- src/kdenlivedoc.h | 6 +- src/mainwindow.cpp | 93 ++++++++++++------- src/mainwindow.h | 3 + src/monitormanager.cpp | 11 +-- src/monitormanager.h | 4 +- 6 files changed, 182 insertions(+), 134 deletions(-) diff --git a/src/kdenlivedoc.cpp b/src/kdenlivedoc.cpp index 18e2d6b9..0dfdfb22 100644 --- a/src/kdenlivedoc.cpp +++ b/src/kdenlivedoc.cpp @@ -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 guides) { QDomDocument doc; QDomElement e; @@ -234,9 +242,14 @@ void KdenliveDoc::syncGuides(QList 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) { diff --git a/src/kdenlivedoc.h b/src/kdenlivedoc.h index acf17cd0..4c03b250 100644 --- a/src/kdenlivedoc.h +++ b/src/kdenlivedoc.h @@ -31,6 +31,7 @@ #include #include +#include #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); diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 1475f1a8..d465ac41 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -278,7 +278,17 @@ MainWindow::MainWindow(QWidget *parent) if (KdenliveSettings::openlastproject()) { openLastFile(); - } else newFile(); + } else { + /*QList 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 %1\nOpen recovery file ?", url.fileName())) == KMessageBox::Yes) recovery = true; + + QList 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 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 <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); } diff --git a/src/mainwindow.h b/src/mainwindow.h index 68994486..4190e29b 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -35,6 +35,7 @@ #include #include #include +#include #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 staleFiles); public slots: void openFile(const KUrl &url); diff --git a/src/monitormanager.cpp b/src/monitormanager.cpp index 62b1773d..cdf80025 100644 --- a/src/monitormanager.cpp +++ b/src/monitormanager.cpp @@ -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"); diff --git a/src/monitormanager.h b/src/monitormanager.h index d0470d08..a1cd3b28 100644 --- a/src/monitormanager.h +++ b/src/monitormanager.h @@ -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; -- 2.39.2