]> git.sesse.net Git - kdenlive/commitdiff
* new: crash recovery feature
authorJean-Baptiste Mardelle <jb@kdenlive.org>
Wed, 16 Jul 2008 22:17:07 +0000 (22:17 +0000)
committerJean-Baptiste Mardelle <jb@kdenlive.org>
Wed, 16 Jul 2008 22:17:07 +0000 (22:17 +0000)
* fix project file corruption when the same clip was inserted several time

svn path=/branches/KDE4/; revision=2319

12 files changed:
src/customtrackview.cpp
src/customtrackview.h
src/kdenlivedoc.cpp
src/kdenlivedoc.h
src/kdenlivesettings.kcfg
src/mainwindow.cpp
src/projectlist.cpp
src/renderer.cpp
src/renderer.h
src/renderwidget.cpp
src/trackview.cpp
src/widgets/configmisc_ui.ui

index 778d80757f7057b78db63974d3dda7c3db9a833b..0e48dfaf014da8e369d60e44e52c956f4c66626d 100644 (file)
@@ -932,8 +932,11 @@ void CustomTrackView::dropEvent(QDropEvent * event) {
         m_commandStack->push(command);
         m_dropItem->baseClip()->addReference();
         m_document->updateClip(m_dropItem->baseClip()->getId());
+        ItemInfo info;
+        info = m_dropItem->info();
+        info.track = m_tracksList.count() - m_dropItem->track();
         // kDebug()<<"IIIIIIIIIIIIIIIIIIIIIIII TRAX CNT: "<<m_tracksList.count()<<", DROP: "<<m_dropItem->track();
-        m_document->renderer()->mltInsertClip(m_tracksList.count() - m_dropItem->track(), m_dropItem->startPos(), m_dropItem->cropStart(), m_dropItem->xml());
+        m_document->renderer()->mltInsertClip(info, m_dropItem->xml());
         m_document->setModified(true);
     } else QGraphicsView::dropEvent(event);
     m_dropItem = NULL;
@@ -1125,7 +1128,9 @@ void CustomTrackView::mouseReleaseEvent(QMouseEvent * event) {
                     if (item->type() == AVWIDGET) {
                         ClipItem *clip = static_cast <ClipItem*>(item);
                         new AddTimelineClipCommand(this, clip->xml(), clip->clipProducer(), item->info(), false, false, moveClips);
-                        m_document->renderer()->mltInsertClip(m_tracksList.count() - item->track(), item->startPos(), item->cropStart(), clip->xml());
+                        ItemInfo info = item->info();
+                        info.track = m_tracksList.count() - item->track();
+                        m_document->renderer()->mltInsertClip(info, clip->xml());
                     } else {
                         Transition *tr = static_cast <Transition*>(item);
                         ItemInfo transitionInfo = tr->info();
@@ -1257,10 +1262,10 @@ void CustomTrackView::deleteSelectedClips() {
     deleteSelected->setText("Delete selected items");
     for (int i = 0; i < itemList.count(); i++) {
         if (itemList.at(i)->type() == AVWIDGET) {
-            ClipItem *item = static_cast <ClipItem *> (itemList.at(i));
+            ClipItem *item = static_cast <ClipItem *>(itemList.at(i));
             new AddTimelineClipCommand(this, item->xml(), item->clipProducer(), item->info(), true, true, deleteSelected);
         } else if (itemList.at(i)->type() == TRANSITIONWIDGET) {
-            Transition *item = static_cast <Transition *> (itemList.at(i));
+            Transition *item = static_cast <Transition *>(itemList.at(i));
             ItemInfo info;
             info.startPos = item->startPos();
             info.endPos = item->endPos();
@@ -1295,7 +1300,8 @@ void CustomTrackView::addClip(QDomElement xml, int clipId, ItemInfo info) {
     scene()->addItem(item);
     baseclip->addReference();
     m_document->updateClip(baseclip->getId());
-    m_document->renderer()->mltInsertClip(m_tracksList.count() - info.track, info.startPos, info.cropStart, xml);
+    info.track = m_tracksList.count() - info.track;
+    m_document->renderer()->mltInsertClip(info, xml);
     m_document->renderer()->doRefresh();
 }
 
@@ -1307,7 +1313,9 @@ void CustomTrackView::slotUpdateClip(int clipId) {
             clip = static_cast <ClipItem *>(list.at(i));
             if (clip->clipProducer() == clipId) {
                 clip->refreshClip();
-                m_document->renderer()->mltUpdateClip(m_tracksList.count() - clip->track(), clip->startPos(), clip->cropStart(), clip->xml());
+                ItemInfo info = clip->info();
+                info.track = m_tracksList.count() - clip->track();
+                m_document->renderer()->mltUpdateClip(info, clip->xml());
             }
         }
     }
@@ -1725,6 +1733,7 @@ void CustomTrackView::editGuide(const GenTime oldPos, const GenTime pos, const Q
         }
         if (!found) emit displayMessage(i18n("No guide at cursor time"), ErrorMessage);
     }
+    m_document->syncGuides(m_guides);
 }
 
 bool CustomTrackView::addGuide(const GenTime pos, const QString &comment) {
@@ -1737,6 +1746,7 @@ bool CustomTrackView::addGuide(const GenTime pos, const QString &comment) {
     Guide *g = new Guide(this, pos, comment, m_scale, m_document->fps(), m_tracksHeight * m_tracksList.count());
     scene()->addItem(g);
     m_guides.append(g);
+    m_document->syncGuides(m_guides);
     return true;
 }
 
@@ -1859,19 +1869,6 @@ void CustomTrackView::drawBackground(QPainter * painter, const QRectF & rect) {
         painter->fillRect(QRectF(rectInView.left(), lowerLimit, rectInView.width(), height() - lowerLimit), QBrush(base));
 }
 
-QDomElement CustomTrackView::xmlInfo() {
-    QDomDocument doc;
-    QDomElement e;
-    QDomElement guides = doc.createElement("guides");
-    for (int i = 0; i < m_guides.count(); i++) {
-        e = doc.createElement("guide");
-        e.setAttribute("time", m_guides.at(i)->position().ms() / 1000);
-        e.setAttribute("comment", m_guides.at(i)->label());
-        guides.appendChild(e);
-    }
-    return guides;
-}
-
 bool CustomTrackView::findString(const QString &text) {
     QString marker;
     for (int i = 0; i < m_searchPoints.size(); ++i) {
index 1ed4541d9bc6e73d7766a1709249ceeeca457430..1cbdca002691e65aa97788196a668280158a3486 100644 (file)
@@ -84,7 +84,6 @@ public:
     void slotSeekToPreviousSnap();
     void slotSeekToNextSnap();
     double getSnapPointForPos(double pos);
-    QDomElement xmlInfo();
     void editKeyFrame(const GenTime pos, const int track, const int index, const QString keyframes);
     bool findString(const QString &text);
     bool findNextString(const QString &text);
index c9b8be8f5e7ff4c43673ddf3d5ced7c399d661f8..3bebe75a166691868bd5efbb71e9f4c765a43dea 100644 (file)
@@ -36,6 +36,7 @@
 #include "titlewidget.h"
 #include "mainwindow.h"
 
+
 KdenliveDoc::KdenliveDoc(const KUrl &url, const KUrl &projectFolder, MltVideoProfile profile, QUndoGroup *undoGroup, MainWindow *parent): QObject(parent), m_render(NULL), m_url(url), m_projectFolder(projectFolder), m_profile(profile), m_fps((double)profile.frame_rate_num / profile.frame_rate_den), m_width(profile.width), m_height(profile.height), m_commandStack(new KUndoStack(undoGroup)), m_modified(false), m_documentLoadingProgress(0), m_documentLoadingStep(0.0), m_startPos(0) {
     kDebug() << "// init profile, ratnum: " << profile.frame_rate_num << ", " << profile.frame_rate_num << ", width: " << profile.width;
     m_clipManager = new ClipManager(this);
@@ -199,11 +200,44 @@ KdenliveDoc::KdenliveDoc(const KUrl &url, const KUrl &projectFolder, MltVideoPro
     kDebug() << "KDEnnlive document, init timecode: " << m_fps;
     if (m_fps == 30000.0 / 1001.0) m_timecode.setFormat(30, true);
     else m_timecode.setFormat((int) m_fps);
+
+    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()));
 }
 
 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);
+    }
+}
+
+void KdenliveDoc::syncGuides(QList <Guide *> guides) {
+    QDomDocument doc;
+    QDomElement e;
+    m_guidesXml.clear();
+    m_guidesXml = doc.createElement("guides");
+    for (int i = 0; i < guides.count(); i++) {
+        e = doc.createElement("guide");
+        e.setAttribute("time", guides.at(i)->position().ms() / 1000);
+        e.setAttribute("comment", guides.at(i)->label());
+        m_guidesXml.appendChild(e);
+    }
+}
+
+void KdenliveDoc::slotAutoSave() {
+    if (m_render)
+        m_render->saveSceneList(m_recoveryUrl.path(), documentInfoXml());
+
 }
 
 void KdenliveDoc::convertDocument(double version) {
@@ -313,7 +347,7 @@ void KdenliveDoc::convertDocument(double version) {
     //kDebug() << "/////////////////  END CONVERTED DOC:";
 }
 
-QDomElement KdenliveDoc::documentInfoXml(QDomElement timelineInfo) {
+QDomElement KdenliveDoc::documentInfoXml() {
     QDomDocument doc;
     QDomElement e;
     QDomElement addedXml = doc.createElement("kdenlivedoc");
@@ -336,7 +370,7 @@ QDomElement KdenliveDoc::documentInfoXml(QDomElement timelineInfo) {
         }
     }
     addedXml.appendChild(markers);
-    addedXml.appendChild(doc.importNode(timelineInfo, true));
+    if (!m_guidesXml.isNull()) addedXml.appendChild(doc.importNode(m_guidesXml, true));
     //kDebug() << m_document.toString();
     return addedXml;
 }
@@ -494,9 +528,16 @@ 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) {
+    if (!m_url.isEmpty() && mod && KdenliveSettings::crashrecovery()) {
+        m_autoSaveTimer->start(3000);
+    }
     if (mod == m_modified) return;
     m_modified = mod;
     emit docModified(m_modified);
index 084b92d690e20a7432185ecba1ffd0bf29494d9c..d2102c6ee1edb8371c447ca3415a4ffe9227c88a 100644 (file)
@@ -27,6 +27,7 @@
 #include <QList>
 #include <QObject>
 #include <QUndoGroup>
+#include <QTimer>
 
 #include <KUndoStack>
 #include <kurl.h>
@@ -34,6 +35,7 @@
 #include "gentime.h"
 #include "timecode.h"
 #include "definitions.h"
+#include "guide.h"
 
 class Render;
 class ClipManager;
@@ -59,6 +61,7 @@ Q_OBJECT public:
     void setProducerDuration(int id, int duration);
     int getProducerDuration(int id);
     Render *renderer();
+    QDomElement m_guidesXml;
     ClipManager *clipManager();
     void addClip(const QDomElement &elem, const int clipId);
     void addFolder(const QString foldername, int clipId, bool edit);
@@ -84,7 +87,7 @@ Q_OBJECT public:
     /** Returns the document format: PAL or NTSC */
     QString getDocumentStandard();
     void setUrl(KUrl url);
-    QDomElement documentInfoXml(QDomElement timelineInfo);
+    QDomElement documentInfoXml();
     void setProfilePath(QString path);
     /** Set to true if document needs saving, false otherwise */
     void setModified(bool mod);
@@ -96,9 +99,11 @@ Q_OBJECT public:
     /** Used to inform main app of the current document loading progress */
     void loadingProgressed();
     void updateAllProjectClips();
+    void syncGuides(QList <Guide *> guides);
 
 private:
     KUrl m_url;
+    KUrl m_recoveryUrl;
     QDomDocument m_document;
     QString m_projectName;
     double m_fps;
@@ -113,6 +118,7 @@ private:
     ClipManager *m_clipManager;
     MltVideoProfile m_profile;
     QString m_scenelist;
+    QTimer *m_autoSaveTimer;
     /** tells whether current doc has been changed since last save event */
     bool m_modified;
     /** Project folder, used to store project files (titles, effects,...) */
@@ -124,6 +130,9 @@ private:
 public slots:
     void slotCreateTextClip(QString group, int groupId);
 
+private slots:
+    void slotAutoSave();
+
 signals:
     void addProjectClip(DocClipBase *);
     void addProjectFolder(const QString, int, bool, bool edit = false);
index c4a8bec3a89f23e09aa8160b880d05d83c2d5c8b..78f42ea33f705cf831ca71ba011cf1f80970d02e 100644 (file)
@@ -9,6 +9,11 @@
       <default>false</default>
     </entry>
 
+    <entry name="crashrecovery" type="Bool">
+      <label>Auto save document in backup file on each change.</label>
+      <default>true</default>
+    </entry>
+
     <entry name="color_duration" type="String">
       <label>Default color clip duration.</label>
       <default>00:00:05:00</default>
index 2c58bf9d37db4ce01b91fb583d185cca19b311ce..c3cdefbd07fbdfd2188f4fba8c8ab146da418ccd 100644 (file)
@@ -48,6 +48,7 @@
 #include <KMenu>
 #include <locale.h>
 #include <ktogglefullscreenaction.h>
+#include <KFileItem>
 
 #include <mlt++/Mlt.h>
 
@@ -280,6 +281,7 @@ bool MainWindow::queryClose() {
         switch (KMessageBox::warningYesNoCancel(this, i18n("Save changes to document ?"))) {
         case KMessageBox::Yes :
             // save document here. If saving fails, return false;
+            saveFile();
             return true;
         case KMessageBox::No :
             return true;
@@ -750,7 +752,7 @@ void MainWindow::slotRemoveTab() {
 }
 
 void MainWindow::saveFileAs(const QString &outputFileName) {
-    m_projectMonitor->saveSceneList(outputFileName, m_activeDocument->documentInfoXml(m_activeTimeline->projectView()->xmlInfo()));
+    m_projectMonitor->saveSceneList(outputFileName, m_activeDocument->documentInfoXml());
     m_activeDocument->setUrl(KUrl(outputFileName));
     setCaption(m_activeDocument->description());
     m_timelineArea->setTabText(m_timelineArea->currentIndex(), m_activeDocument->description());
@@ -776,7 +778,7 @@ void MainWindow::saveFile() {
     }
 }
 
-void MainWindow::openFile() { //changed
+void MainWindow::openFile() {
     KUrl url = KFileDialog::getOpenUrl(KUrl(), "*.kdenlive|Kdenlive project files (*.kdenlive)\n*.westley|MLT project files (*.westley)");
     if (url.isEmpty()) return;
     m_fileOpenRecent->addUrl(url);
@@ -789,11 +791,32 @@ void MainWindow::openLastFile() {
     openFile(KUrl(Lastproject));
 }
 
-void MainWindow::openFile(const KUrl &url) { //new
+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;
+        }
+    }
+
     //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");
-    KdenliveDoc *doc = new KdenliveDoc(url, KUrl(), prof, m_commandStack, this);
+    KdenliveDoc *doc;
+    if (recovery) {
+        doc = new KdenliveDoc(recoveryUrl, KUrl(), prof, m_commandStack, this);
+        doc->setUrl(url);
+        doc->setModified(true);
+    } else doc = new KdenliveDoc(url, KUrl(), prof, m_commandStack, this);
     connectDocumentInfo(doc);
     TrackView *trackView = new TrackView(doc, this);
     m_timelineArea->setCurrentIndex(m_timelineArea->addTab(trackView, KIcon("kdenlive"), doc->description()));
index 899b41f3dcae9acf1e15b103b5b1c26493f77784..af0fddf35bd1001b46efb3cb10639c1e0bd11851 100644 (file)
@@ -310,9 +310,13 @@ void ProjectList::slotUpdateClip(int id) {
 void ProjectList::slotAddClip(QUrl givenUrl, QString group) {
     if (!m_commandStack) kDebug() << "!!!!!!!!!!!!!!!!  NO CMD STK";
     KUrl::List list;
-    if (givenUrl.isEmpty())
-        list = KFileDialog::getOpenUrls(KUrl(), "application/vnd.kde.kdenlive application/vnd.westley.scenelist application/flv application/vnd.rn-realmedia video/x-dv video/x-msvideo video/mpeg video/x-ms-wmv audio/mpeg audio/x-mp3 audio/x-wav application/ogg *.m2t *.mts *.dv video/mp4 video/quicktime image/gif image/jpeg image/png image/x-bmp image/svg+xml image/tiff image/x-xcf-gimp image/x-vnd.adobe.photoshop image/x-pcx image/x-exr");
-    else list.append(givenUrl);
+    if (givenUrl.isEmpty()) {
+        KFileDialog d(KUrl("kfiledialog:///clipfolder"), "application/x-kdenlive application/flv application/vnd.rn-realmedia video/x-dv video/x-msvideo video/mpeg video/x-ms-wmv audio/mpeg audio/x-mp3 audio/x-wav application/ogg video/mp4 video/quicktime image/gif image/jpeg image/png image/x-bmp image/svg+xml image/tiff image/x-xcf-gimp image/x-vnd.adobe.photoshop image/x-pcx image/x-exr", this);
+        d.setFilter(d.currentFilter() + "\n*.m2t *.mts|HDV video\n*.dv|DV video");
+        if (d.exec() == QDialog::Accepted)
+            list = d.selectedUrls();
+        /*list = KFileDialog::getOpenUrls(KUrl("kfiledialog:///clipfolder"), "application/vnd.kde.kdenlive application/vnd.westley.scenelist application/flv application/vnd.rn-realmedia video/x-dv video/x-msvideo video/mpeg video/x-ms-wmv audio/mpeg audio/x-mp3 audio/x-wav application/ogg video/mp4 video/quicktime image/gif image/jpeg image/png image/x-bmp image/svg+xml image/tiff image/x-xcf-gimp image/x-vnd.adobe.photoshop image/x-pcx image/x-exr\n*.m2t *.mts|HDV video\n*.dv|DV video");*/
+    } else list.append(givenUrl);
     if (list.isEmpty()) return;
     KUrl::List::Iterator it;
     int groupId = -1;
index 533b31dd51978a2687879881952ed4c80560ee95..6e0f198e2a0f6f082447437b2d116c2cb417c748 100644 (file)
@@ -121,6 +121,7 @@ void Render::closeMlt() {
         delete m_mltConsumer;
     if (m_mltProducer)
         delete m_mltProducer;
+    while (! m_producersList.isEmpty()) delete m_producersList.takeFirst();
     //delete m_osdInfo;
 }
 
@@ -161,6 +162,7 @@ int Render::resetProfile(QString profile) {
     Mlt::Producer *producer = new Mlt::Producer(*m_mltProfile , "westley-xml", tmp);
     delete[] tmp;
     m_mltProducer = producer;
+    m_mltProducer->optimise();
     m_mltProducer->set_speed(0);
     connectPlaylist();
 
@@ -382,29 +384,35 @@ void Render::getFileProperties(const QDomElement &xml, int clipId) {
     QMap < QString, QString > metadataPropertyMap;
 
     KUrl url = KUrl(xml.attribute("resource", QString::null));
+    bool newProducer = false;
+
+    Mlt::Producer *producer = getProducerById(QString::number(clipId));
+    if (producer == NULL) {
+        if (url.isEmpty()) {
+            QDomDocument doc;
+            QDomElement westley = doc.createElement("westley");
+            QDomElement play = doc.createElement("playlist");
+            doc.appendChild(westley);
+            westley.appendChild(play);
+            play.appendChild(doc.importNode(xml, true));
+            char *tmp = decodedString(doc.toString());
+            producer = new Mlt::Producer(*m_mltProfile, "westley-xml", tmp);
+            delete[] tmp;
+        } else {
+            char *tmp = decodedString(url.path());
+            producer = new Mlt::Producer(*m_mltProfile, tmp);
+            delete[] tmp;
+        }
 
-    Mlt::Producer *producer;
-
-    if (url.isEmpty()) {
-        QDomDocument doc;
-        QDomElement westley = doc.createElement("westley");
-        QDomElement play = doc.createElement("playlist");
-        doc.appendChild(westley);
-        westley.appendChild(play);
-        play.appendChild(doc.importNode(xml, true));
-        char *tmp = decodedString(doc.toString());
-        producer = new Mlt::Producer(*m_mltProfile, "westley-xml", tmp);
-        delete[] tmp;
-    } else {
-        char *tmp = decodedString(url.path());
-        producer = new Mlt::Producer(*m_mltProfile, tmp);
-        delete[] tmp;
+        if (producer->is_blank()) {
+            kDebug() << " / / / / / / / /ERRROR / / / / // CANNOT LOAD PRODUCER: ";
+            return;
+        }
+        m_producersList.append(producer);
+        newProducer = true;
     }
 
-    if (producer->is_blank()) {
-        kDebug() << " / / / / / / / /ERRROR / / / / // CANNOT LOAD PRODUCER: ";
-        return;
-    }
+
 
     int frameNumber = xml.attribute("thumbnail", "0").toInt();
     if (frameNumber != 0) producer->seek(frameNumber);
@@ -508,7 +516,7 @@ void Render::getFileProperties(const QDomElement &xml, int clipId) {
     emit replyGetFileProperties(clipId, filePropertyMap, metadataPropertyMap);
     kDebug() << "REquested fuile info for: " << url.path();
     if (frame) delete frame;
-    if (producer) delete producer;
+    if (!newProducer && producer) delete producer;
 }
 
 /** Create the producer from the Westley QDomDocument */
@@ -585,7 +593,7 @@ void Render::setSceneList(QString playlist, int position) {
     m_mltProducer = new Mlt::Producer(*m_mltProfile, "westley-xml", tmp);
     delete[] tmp;
     if (!m_mltProducer || !m_mltProducer->is_valid()) kDebug() << " WARNING - - - - -INVALID PLAYLIST: " << tmp;
-    //m_mltProducer->optimise();
+    m_mltProducer->optimise();
 
     /*if (KdenliveSettings::osdtimecode()) {
     // Attach filter for on screen display of timecode
@@ -645,6 +653,7 @@ void Render::saveSceneList(QString path, QDomElement kdenliveData) {
 
     char *tmppath = decodedString("westley:" + path);
     Mlt::Consumer westleyConsumer(*m_mltProfile , tmppath);
+    m_mltProducer->optimise();
     delete[] tmppath;
     westleyConsumer.set("terminate_on_pause", 1);
     Mlt::Producer prod(m_mltProducer->get_producer());
@@ -682,6 +691,7 @@ void Render::connectPlaylist() {
     m_mltConsumer->connect(*m_mltProducer);
     m_mltProducer->set_speed(0);
     m_mltConsumer->start();
+    parsePlaylistForClips();
     emit durationChanged(m_mltProducer->get_playtime());
     //refresh();
     /*
@@ -1029,23 +1039,26 @@ void Render::mltCheckLength(bool reload) {
         black.setAttribute("mlt_service", "colour");
         black.setAttribute("colour", "black");
         black.setAttribute("id", "black");
-        black.setAttribute("in", "0");
-        black.setAttribute("out", "13999");
+        ItemInfo info;
+        info.track = 0;
         while (dur > 14000) {
-            mltInsertClip(0, GenTime(i * 14000, m_fps), GenTime(), black);
+            info.startPos = GenTime(i * 14000, m_fps);
+            info.endPos = info.startPos + GenTime(13999, m_fps);
+            mltInsertClip(info, black);
             dur = dur - 14000;
             i++;
         }
         if (dur > 0) {
-            black.setAttribute("out", QString::number(dur));
-            mltInsertClip(0, GenTime(i * 14000, m_fps), GenTime(), black);
+            info.startPos = GenTime(i * 14000, m_fps);
+            info.endPos = info.startPos + GenTime(dur, m_fps);
+            mltInsertClip(info, black);
         }
         m_mltProducer->set("out", duration);
         emit durationChanged((int)duration);
     }
 }
 
-void Render::mltInsertClip(int track, GenTime position, GenTime crop, QDomElement element) {
+void Render::mltInsertClip(ItemInfo info, QDomElement element) {
     if (!m_mltProducer) {
         kDebug() << "PLAYLIST NOT INITIALISED //////";
         return;
@@ -1059,24 +1072,68 @@ void Render::mltInsertClip(int track, GenTime position, GenTime crop, QDomElemen
     Mlt::Service service(parentProd.get_service());
     Mlt::Tractor tractor(service);
     mlt_service_lock(service.get_service());
-    Mlt::Producer trackProducer(tractor.track(track));
+    Mlt::Producer trackProducer(tractor.track(info.track));
     Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service());
 
-    QDomDocument doc;
-    doc.appendChild(doc.importNode(element, true));
-    QString resource = doc.toString();
-    char *tmp = decodedString(resource);
-    Mlt::Producer clip(*m_mltProfile, "westley-xml", tmp);
-    clip.set("in", crop.frames(m_fps));
-    //clip.set_in_and_out(in.frames(m_fps), out.frames(m_fps));
-    delete[] tmp;
-    trackPlaylist.insert_at((int) position.frames(m_fps), clip, 1);
+    Mlt::Producer *prod = getProducerById(element.attribute("id"));
+    if (prod == NULL) {
+        // clip was never used yet
+        QDomDocument doc;
+        doc.appendChild(doc.importNode(element, true));
+        QString resource = doc.toString();
+        kDebug() << "// INSERTING CLIP: " << resource;
+        char *tmp = decodedString(resource);
+        prod = new Mlt::Producer(*m_mltProfile, "westley-xml", tmp);
+        delete[] tmp;
+        m_producersList.append(prod);
+    }
+
+    Mlt::Producer *clip = prod->cut(info.cropStart.frames(m_fps), (info.endPos - info.startPos).frames(m_fps));
+    trackPlaylist.insert_at((int) info.startPos.frames(m_fps), *clip, 1);
+
     mlt_service_unlock(service.get_service());
-    if (track != 0) mltCheckLength();
+
+    if (info.track != 0) mltCheckLength();
     //tractor.multitrack()->refresh();
     //tractor.refresh();
 }
 
+Mlt::Producer *Render::getProducerById(const QString &id) {
+    for (int i = 0; i < m_producersList.count(); i++) {
+        if (m_producersList.at(i)->get("id") == id) return m_producersList.at(i);
+    }
+    return NULL;
+}
+
+void Render::parsePlaylistForClips() {
+    // clear current producers list
+    while (! m_producersList.isEmpty()) delete m_producersList.takeFirst();
+
+    //parse entire playlists to find all the different clips
+    Mlt::Producer parentProd(m_mltProducer->parent());
+    if (parentProd.get_producer() == NULL) {
+        kDebug() << "PLAYLIST BROKEN, CANNOT INSERT CLIP //////";
+        return;
+    }
+    Mlt::Service service(parentProd.get_service());
+    if (service.type() != tractor_type) return;
+    Mlt::Tractor tractor(service);
+    mlt_service_lock(service.get_service());
+    for (int i = 0; i < tractor.count(); i++) {
+        Mlt::Producer trackProducer(tractor.track(i));
+        Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service());
+        for (int j = 0; j < trackPlaylist.count(); j++) {
+            if (!trackPlaylist.is_blank(j)) {
+                Mlt::Producer *clip = trackPlaylist.get_clip(j);
+                if (clip) {
+                    if (getProducerById(clip->get("id")) == NULL)
+                        m_producersList.append(new Mlt::Producer(clip->get_parent()));
+                }
+            }
+        }
+    }
+}
+
 void Render::mltCutClip(int track, GenTime position) {
     m_isBlocked = true;
 
@@ -1091,10 +1148,10 @@ void Render::mltCutClip(int track, GenTime position) {
     m_isBlocked = false;
 }
 
-void Render::mltUpdateClip(int track, GenTime position, GenTime crop, QDomElement element) {
+void Render::mltUpdateClip(ItemInfo info, QDomElement element) {
     // TODO: optimize
-    mltRemoveClip(track, position);
-    mltInsertClip(track, position, crop, element);
+    mltRemoveClip(info.track, info.startPos);
+    mltInsertClip(info, element);
 }
 
 
@@ -1575,7 +1632,7 @@ void Render::mltUpdateTransition(QString oldTag, QString tag, int a_track, int b
         mltDeleteTransition(oldTag, a_track, b_track, in, out, xml, false);
         mltAddTransition(tag, a_track, b_track, in, out, xml);
     }
-    mltSavePlaylist();
+    //mltSavePlaylist();
 }
 
 void Render::mltUpdateTransitionParams(QString type, int a_track, int b_track, GenTime in, GenTime out, QDomElement xml) {
index 979ab46a4977a8aef433c8a4b92133690246bad6..324a20d28bfb17e19ee7c7671fcf0fd39d3aea00 100644 (file)
@@ -27,9 +27,7 @@
 #include <kurl.h>
 
 #include "gentime.h"
-/*#include "docclipref.h"
-#include "effectdesc.h"
-#include "effectparamdescfactory.h"*/
+#include "definitions.h"
 
 /**Render encapsulates the client side of the interface to a renderer.
 From Kdenlive's point of view, you treat the Render object as the
@@ -149,8 +147,8 @@ Q_OBJECT public:
     const double dar() const;
 
     /** Playlist manipulation */
-    void mltInsertClip(int track, GenTime position, GenTime crop, QDomElement element);
-    void mltUpdateClip(int track, GenTime position, GenTime crop, QDomElement element);
+    void mltInsertClip(ItemInfo info, QDomElement element);
+    void mltUpdateClip(ItemInfo info, QDomElement element);
     void mltCutClip(int track, GenTime position);
     void mltResizeClipEnd(int track, GenTime pos, GenTime in, GenTime out);
     void mltResizeClipStart(int track, GenTime pos, GenTime moveEnd, GenTime moveStart, GenTime in, GenTime out);
@@ -182,6 +180,9 @@ private:   // Private attributes & methods
     uint m_monitorId;
     bool m_generateScenelist;
 
+    QList <Mlt::Producer *> m_producersList;
+    Mlt::Producer *getProducerById(const QString &id);
+    void parsePlaylistForClips();
     /** Holds the path to on screen display profile */
     QString m_osdProfile;
 
index 793bf92dc1df6afc499a71cbb8f13f7ccb9166b7..7034ed196d6f1d0527d6acfeb0623066a3ac9b82 100644 (file)
@@ -161,7 +161,7 @@ void RenderWidget::slotEditProfile() {
     ui.profile_name->setFocus();
 
     if (d->exec() == QDialog::Accepted) {
-       slotDeleteProfile();
+        slotDeleteProfile();
         QString exportFile = KStandardDirs::locateLocal("data", "kdenlive/export/customprofiles.xml");
         QDomDocument doc;
         QFile file(exportFile);
@@ -319,12 +319,11 @@ void RenderWidget::refreshParams() {
     }
 
     if (item->data(EditableRole).toString().isEmpty()) {
-       m_view.buttonDelete->setEnabled(false);
-       m_view.buttonEdit->setEnabled(false);
-    }
-    else {
-       m_view.buttonDelete->setEnabled(true);
-       m_view.buttonEdit->setEnabled(true);
+        m_view.buttonDelete->setEnabled(false);
+        m_view.buttonEdit->setEnabled(false);
+    } else {
+        m_view.buttonDelete->setEnabled(true);
+        m_view.buttonEdit->setEnabled(true);
     }
 }
 
index 32c52b0fb3ef0bd06ba48cb30866c9345f3d1838..d2da55749d7af5e0d3a1cb3c3b4a11949007ec64 100644 (file)
@@ -343,7 +343,7 @@ int TrackView::slotAddProjectTrack(int ix, QDomElement xml, bool videotrack) {
                 ItemInfo clipinfo;
                 clipinfo.startPos = GenTime(position, m_doc->fps());
                 clipinfo.endPos = clipinfo.startPos + GenTime(out - in, m_doc->fps());
-               clipinfo.cropStart = GenTime(in, m_doc->fps());
+                clipinfo.cropStart = GenTime(in, m_doc->fps());
                 clipinfo.track = ix;
                 //kDebug() << "// INSERTING CLIP: " << in << "x" << out << ", track: " << ix << ", ID: " << id << ", SCALE: " << m_scale << ", FPS: " << m_doc->fps();
                 ClipItem *item = new ClipItem(clip, clipinfo, m_scale, m_doc->fps());
index f9bc472555d695fa04e04fa0ff58c02b029a7336..4a32b4181f86411c9bb8f250a194a187f722c010 100644 (file)
@@ -6,11 +6,11 @@
     <x>0</x>
     <y>0</y>
     <width>369</width>
-    <height>258</height>
+    <height>285</height>
    </rect>
   </property>
   <layout class="QGridLayout" name="gridLayout_4" >
-   <item row="1" column="0" >
+   <item row="2" column="0" >
     <widget class="QGroupBox" name="groupBox" >
      <property name="title" >
       <string>Default Durations</string>
@@ -50,7 +50,7 @@
      </layout>
     </widget>
    </item>
-   <item row="2" column="0" >
+   <item row="3" column="0" >
     <widget class="QGroupBox" name="properties" >
      <property name="title" >
       <string>Default Profile</string>
      </layout>
     </widget>
    </item>
-   <item row="3" column="0" >
+   <item row="4" column="0" >
     <spacer>
      <property name="orientation" >
       <enum>Qt::Vertical</enum>
      </property>
     </widget>
    </item>
+   <item row="1" column="0" >
+    <widget class="QCheckBox" name="kcfg_crashrecovery" >
+     <property name="text" >
+      <string>Crash recovery (automatic backup)</string>
+     </property>
+     <property name="checked" >
+      <bool>true</bool>
+     </property>
+    </widget>
+   </item>
   </layout>
  </widget>
  <customwidgets>