]> git.sesse.net Git - kdenlive/blobdiff - src/kdenlivedoc.cpp
Save last used rendering profile in Kdenlive document
[kdenlive] / src / kdenlivedoc.cpp
index e679789b1956730d2c4b5b5cb3370d7ae0416d00..663157dbfbb89e9a88067e2549cbaa229f03b441 100644 (file)
@@ -27,7 +27,7 @@
 #include "titlewidget.h"
 #include "mainwindow.h"
 #include "documentchecker.h"
-#include "documentconvert.h"
+#include "documentvalidator.h"
 #include "kdenlive-config.h"
 
 #include <KDebug>
 
 #include <QCryptographicHash>
 #include <QFile>
+#include <QInputDialog>
 
 #include <mlt++/Mlt.h>
 
 const double DOCUMENTVERSION = 0.83;
 
-KdenliveDoc::KdenliveDoc(const KUrl &url, const KUrl &projectFolder, QUndoGroup *undoGroup, const QString &profileName, const QPoint tracks, Render *render, MainWindow *parent) :
+KdenliveDoc::KdenliveDoc(const KUrl &url, const KUrl &projectFolder, QUndoGroup *undoGroup, QString profileName, const QPoint tracks, Render *render, MainWindow *parent) :
         QObject(parent),
         m_autosave(NULL),
         m_url(url),
-        m_zoom(7),
         m_startPos(0),
         m_render(render),
         m_commandStack(new QUndoStack(undoGroup)),
@@ -58,187 +58,177 @@ KdenliveDoc::KdenliveDoc(const KUrl &url, const KUrl &projectFolder, QUndoGroup
         m_projectFolder(projectFolder),
         m_documentLoadingStep(0.0),
         m_documentLoadingProgress(0),
-        m_abortLoading(false),
-        m_zoneStart(0),
-        m_zoneEnd(100)
+        m_abortLoading(false)
 {
     m_clipManager = new ClipManager(this);
     m_autoSaveTimer = new QTimer(this);
     m_autoSaveTimer->setSingleShot(true);
+    bool success = false;
+
+    // init default document properties
+    m_documentProperties["zoom"] = "7";
+    m_documentProperties["zonein"] = "0";
+    m_documentProperties["zoneout"] = "100";
+
     if (!url.isEmpty()) {
         QString tmpFile;
-        bool success = KIO::NetAccess::download(url.path(), tmpFile, parent);
-        if (success) {
+        success = KIO::NetAccess::download(url.path(), tmpFile, parent);
+        if (!success) // The file cannot be opened
+            KMessageBox::error(parent, KIO::NetAccess::lastErrorString());
+        else {
             QFile file(tmpFile);
             QString errorMsg;
             success = m_document.setContent(&file, false, &errorMsg);
             file.close();
-            if (success == false) {
-                // File is corrupted, warn user
+            KIO::NetAccess::removeTempFile(tmpFile);
+            if (!success) // It is corrupted
                 KMessageBox::error(parent, errorMsg);
-            }
-        } else KMessageBox::error(parent, KIO::NetAccess::lastErrorString());
-
-        if (success) {
-            QDomNode infoXmlNode = m_document.elementsByTagName("kdenlivedoc").at(0);
-            if (!infoXmlNode.isNull()) {
-                QDomElement infoXml = infoXmlNode.toElement();
-                double version = infoXml.attribute("version").toDouble();
-
-                // Upgrade old Kdenlive documents to current version
-                DocumentConvert converter(m_document);
-                if (!converter.doConvert(version, DOCUMENTVERSION)) {
-                    m_url.clear();
-                    m_document = createEmptyDocument(tracks.x(), tracks.y());
-                    setProfilePath(profileName);
-                } else {
+            else {
+                DocumentValidator validator(m_document);
+                success = validator.isProject();
+                if (!success) // It is not a project file
+                    parent->slotGotProgressInfo(i18n("File %1 is not a Kdenlive project file.", m_url.path()), 100);
+                else {
                     /*
-                     * read again <kdenlivedoc> and <mlt> to get all the new
-                     * stuff (convertDocument() can now do anything without breaking
-                     * document loading)
+                     * Validate the file against the current version (upgrade
+                     * and recover it if needed). It is NOT a passive operation
                      */
-                    setModified(converter.isModified());
-                    infoXmlNode = m_document.elementsByTagName("kdenlivedoc").at(0);
-                    infoXml = infoXmlNode.toElement();
-                    version = infoXml.attribute("version").toDouble();
-                    QDomNode mlt = m_document.elementsByTagName("mlt").at(0);
-
-                    QString profilePath = infoXml.attribute("profile");
-                    QString projectFolderPath = infoXml.attribute("projectfolder");
-                    if (!projectFolderPath.isEmpty()) m_projectFolder = KUrl(projectFolderPath);
-
-                    if (m_projectFolder.isEmpty() || !KIO::NetAccess::exists(m_projectFolder.path(), KIO::NetAccess::DestinationSide, parent)) {
-                        // Make sure the project folder is usable
-                        KMessageBox::information(parent, i18n("Document project folder is invalid, setting it to the default one: %1", KdenliveSettings::defaultprojectfolder()));
-                        m_projectFolder = KUrl(KdenliveSettings::defaultprojectfolder());
-                    }
-                    m_startPos = infoXml.attribute("position").toInt();
-                    m_zoom = infoXml.attribute("zoom", "7").toInt();
-                    m_zoneStart = infoXml.attribute("zonein", "0").toInt();
-                    m_zoneEnd = infoXml.attribute("zoneout", "100").toInt();
-                    setProfilePath(profilePath);
-
-                    // Build tracks
-                    QDomElement e;
-                    QDomNode tracksinfo = m_document.elementsByTagName("tracksinfo").at(0);
-                    TrackInfo projectTrack;
-                    if (!tracksinfo.isNull()) {
-                        QDomNodeList trackslist = tracksinfo.childNodes();
-                        int maxchild = trackslist.count();
-                        for (int k = 0; k < maxchild; k++) {
-                            e = trackslist.at(k).toElement();
-                            if (e.tagName() == "trackinfo") {
-                                if (e.attribute("type") == "audio") projectTrack.type = AUDIOTRACK;
-                                else projectTrack.type = VIDEOTRACK;
-                                projectTrack.isMute = e.attribute("mute").toInt();
-                                projectTrack.isBlind = e.attribute("blind").toInt();
-                                projectTrack.isLocked = e.attribute("locked").toInt();
-                                m_tracksList.append(projectTrack);
+                    // TODO: backup the document or alert the user?
+                    success = validator.validate(DOCUMENTVERSION);
+                    if (success) { // Let the validator handle error messages
+                        setModified(validator.isModified());
+                        QDomElement mlt = m_document.firstChildElement("mlt");
+                        QDomElement infoXml = mlt.firstChildElement("kdenlivedoc");
+
+                        profileName = infoXml.attribute("profile");
+                        m_projectFolder = infoXml.attribute("projectfolder");
+                        m_startPos = infoXml.attribute("position").toInt();
+
+                        QDomElement docproperties = infoXml.firstChildElement("documentproperties");
+                        QDomNamedNodeMap props = docproperties.attributes();
+                        for (int i = 0; i < props.count(); i++) {
+                            m_documentProperties.insert(props.item(i).nodeName(), props.item(i).nodeValue());
+                        }
+
+                        // Build tracks
+                        QDomElement e;
+                        QDomElement tracksinfo = infoXml.firstChildElement("tracksinfo");
+                        TrackInfo projectTrack;
+                        if (!tracksinfo.isNull()) {
+                            QDomNodeList trackslist = tracksinfo.childNodes();
+                            int maxchild = trackslist.count();
+                            for (int k = 0; k < maxchild; k++) {
+                                e = trackslist.at(k).toElement();
+                                if (e.tagName() == "trackinfo") {
+                                    if (e.attribute("type") == "audio") projectTrack.type = AUDIOTRACK;
+                                    else projectTrack.type = VIDEOTRACK;
+                                    projectTrack.isMute = e.attribute("mute").toInt();
+                                    projectTrack.isBlind = e.attribute("blind").toInt();
+                                    projectTrack.isLocked = e.attribute("locked").toInt();
+                                    m_tracksList.append(projectTrack);
+                                }
                             }
+                            mlt.removeChild(tracksinfo);
+                        }
+                        QDomNodeList producers = m_document.elementsByTagName("producer");
+                        QDomNodeList infoproducers = m_document.elementsByTagName("kdenlive_producer");
+                        if (checkDocumentClips(infoproducers) == false) m_abortLoading = true;
+                        const int max = producers.count();
+                        const int infomax = infoproducers.count();
+
+                        QDomNodeList folders = m_document.elementsByTagName("folder");
+                        for (int i = 0; i < folders.count(); i++) {
+                            e = folders.item(i).cloneNode().toElement();
+                            m_clipManager->addFolder(e.attribute("id"), e.attribute("name"));
                         }
-                        mlt.removeChild(tracksinfo);
-                    }
-                    QDomNodeList producers = m_document.elementsByTagName("producer");
-                    QDomNodeList infoproducers = m_document.elementsByTagName("kdenlive_producer");
-                    if (checkDocumentClips(infoproducers) == false) m_abortLoading = true;
-                    const int max = producers.count();
-                    const int infomax = infoproducers.count();
-
-                    QDomNodeList folders = m_document.elementsByTagName("folder");
-                    for (int i = 0; i < folders.count(); i++) {
-                        e = folders.item(i).cloneNode().toElement();
-                        m_clipManager->addFolder(e.attribute("id"), e.attribute("name"));
-                    }
 
-                    if (max > 0) {
-                        m_documentLoadingStep = 100.0 / (max + infomax + m_document.elementsByTagName("entry").count());
-                        parent->slotGotProgressInfo(i18n("Loading project clips"), (int) m_documentLoadingProgress);
-                    }
+                        if (max > 0) {
+                            m_documentLoadingStep = 100.0 / (max + infomax + m_document.elementsByTagName("entry").count());
+                            parent->slotGotProgressInfo(i18n("Loading project clips"), (int) m_documentLoadingProgress);
+                        }
 
 
-                    for (int i = 0; i < infomax && !m_abortLoading; i++) {
-                        e = infoproducers.item(i).cloneNode().toElement();
-                        if (m_documentLoadingStep > 0) {
-                            m_documentLoadingProgress += m_documentLoadingStep;
-                            parent->slotGotProgressInfo(QString(), (int) m_documentLoadingProgress);
-                            //qApp->processEvents();
-                        }
-                        QString prodId = e.attribute("id");
-                        if (!e.isNull() && prodId != "black" && !prodId.startsWith("slowmotion") && !m_abortLoading) {
-                            e.setTagName("producer");
-                            // Get MLT's original producer properties
-                            QDomElement orig;
-                            for (int j = 0; j < max; j++) {
-                                QDomElement o = producers.item(j).cloneNode().toElement();
-                                QString origId = o.attribute("id").section('_', 0, 0);
-                                if (origId == prodId) {
-                                    orig = o;
-                                    break;
+                        for (int i = 0; i < infomax && !m_abortLoading; i++) {
+                            e = infoproducers.item(i).cloneNode().toElement();
+                            if (m_documentLoadingStep > 0) {
+                                m_documentLoadingProgress += m_documentLoadingStep;
+                                parent->slotGotProgressInfo(QString(), (int) m_documentLoadingProgress);
+                                //qApp->processEvents();
+                            }
+                            QString prodId = e.attribute("id");
+                            if (!e.isNull() && prodId != "black" && !prodId.startsWith("slowmotion") && !m_abortLoading) {
+                                e.setTagName("producer");
+                                // Get MLT's original producer properties
+                                QDomElement orig;
+                                for (int j = 0; j < max; j++) {
+                                    QDomElement o = producers.item(j).cloneNode().toElement();
+                                    QString origId = o.attribute("id").section('_', 0, 0);
+                                    if (origId == prodId) {
+                                        orig = o;
+                                        break;
+                                    }
                                 }
+                                addClipInfo(e, orig, prodId);
+                                kDebug() << "// KDENLIVE PRODUCER: " << prodId;
                             }
-                            addClipInfo(e, orig, prodId);
-                            kDebug() << "// NLIVE PROD: " << prodId;
                         }
-                    }
-                    if (m_abortLoading) {
-                        //parent->slotGotProgressInfo(i18n("File %1 is not a Kdenlive project file."), 100);
-                        emit resetProjectList();
-                        m_startPos = 0;
-                        m_url = KUrl();
-                        m_tracksList.clear();
-                        kWarning() << "Aborted loading of: " << url.path();
-                        m_document = createEmptyDocument(KdenliveSettings::videotracks(), KdenliveSettings::audiotracks());
-                        setProfilePath(KdenliveSettings::default_profile());
-                        m_clipManager->clear();
-                    } else {
-                        QDomNode markers = m_document.elementsByTagName("markers").at(0);
-                        if (!markers.isNull()) {
-                            QDomNodeList markerslist = markers.childNodes();
-                            int maxchild = markerslist.count();
-                            for (int k = 0; k < maxchild; k++) {
-                                e = markerslist.at(k).toElement();
-                                if (e.tagName() == "marker") {
-                                    m_clipManager->getClipById(e.attribute("id"))->addSnapMarker(GenTime(e.attribute("time").toDouble()), e.attribute("comment"));
+                        if (m_abortLoading) {
+                            //parent->slotGotProgressInfo(i18n("File %1 is not a Kdenlive project file."), 100);
+                            emit resetProjectList();
+                            m_startPos = 0;
+                            m_url = KUrl();
+                            m_tracksList.clear();
+                            kWarning() << "Aborted loading of: " << url.path();
+                            m_document = createEmptyDocument(KdenliveSettings::videotracks(), KdenliveSettings::audiotracks());
+                            setProfilePath(KdenliveSettings::default_profile());
+                            m_clipManager->clear();
+                        } else {
+                            QDomElement markers = infoXml.firstChildElement("markers");
+                            if (!markers.isNull()) {
+                                QDomNodeList markerslist = markers.childNodes();
+                                int maxchild = markerslist.count();
+                                for (int k = 0; k < maxchild; k++) {
+                                    e = markerslist.at(k).toElement();
+                                    if (e.tagName() == "marker") {
+                                        m_clipManager->getClipById(e.attribute("id"))->addSnapMarker(GenTime(e.attribute("time").toDouble()), e.attribute("comment"));
+                                    }
                                 }
+                                infoXml.removeChild(markers);
                             }
-                            mlt.removeChild(markers);
+                            setProfilePath(profileName);
+                            kDebug() << "Reading file: " << url.path() << ", found clips: " << producers.count();
                         }
-                        m_document.removeChild(infoXmlNode);
-                        kDebug() << "Reading file: " << url.path() << ", found clips: " << producers.count();
                     }
                 }
-            } 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(KdenliveSettings::videotracks(), KdenliveSettings::audiotracks());
-                m_url = KUrl();
-                setProfilePath(KdenliveSettings::default_profile());
             }
-            KIO::NetAccess::removeTempFile(tmpFile);
-        } else {
-            parent->slotGotProgressInfo(i18n("File %1 is not a Kdenlive project file."), 100);
-            m_url = KUrl();
-            m_document = createEmptyDocument(KdenliveSettings::videotracks(), KdenliveSettings::audiotracks());
-            setProfilePath(KdenliveSettings::default_profile());
         }
-    } else {
+    } else setProfilePath(profileName);
+
+    // Something went wrong, or a new file was requested: create a new project
+    if (!success) {
+        m_url = KUrl();
         m_document = createEmptyDocument(tracks.x(), tracks.y());
-        setProfilePath(profileName);
     }
-    if (m_projectFolder.isEmpty()) m_projectFolder = KUrl(KdenliveSettings::defaultprojectfolder());
 
-    // make sure that the necessary folders exist
+    // Set the video profile (empty == default)
+
+    // Make sure the project folder is usable
+    if (m_projectFolder.isEmpty() || !KIO::NetAccess::exists(m_projectFolder.path(), KIO::NetAccess::DestinationSide, parent)) {
+        KMessageBox::information(parent, i18n("Document project folder is invalid, setting it to the default one: %1", KdenliveSettings::defaultprojectfolder()));
+        m_projectFolder = KUrl(KdenliveSettings::defaultprojectfolder());
+    }
+
+    // Make sure that the necessary folders exist
     KStandardDirs::makeDir(m_projectFolder.path() + "/titles/");
     KStandardDirs::makeDir(m_projectFolder.path() + "/thumbs/");
     KStandardDirs::makeDir(m_projectFolder.path() + "/ladspa/");
 
-    kDebug() << "KDEnlive document, init timecode: " << m_fps;
+    kDebug() << "Kdenlive document, init timecode: " << m_fps;
     if (m_fps == 30000.0 / 1001.0) m_timecode.setFormat(30, true);
     else m_timecode.setFormat((int) m_fps);
 
     //kDebug() << "// SETTING SCENE LIST:\n\n" << m_document.toString();
     connect(m_autoSaveTimer, SIGNAL(timeout()), this, SLOT(slotAutoSave()));
-
 }
 
 KdenliveDoc::~KdenliveDoc()
@@ -247,7 +237,7 @@ KdenliveDoc::~KdenliveDoc()
     delete m_clipManager;
     delete m_autoSaveTimer;
     if (m_autosave) {
-        m_autosave->remove();
+        if (!m_autosave->fileName().isEmpty()) m_autosave->remove();
         delete m_autosave;
     }
 }
@@ -371,6 +361,7 @@ void KdenliveDoc::syncGuides(QList <Guide *> guides)
         e.setAttribute("comment", guides.at(i)->label());
         guideNode.appendChild(e);
     }
+    setModified(true);
     emit guidesUpdated();
 }
 
@@ -401,42 +392,61 @@ void KdenliveDoc::slotAutoSave()
 
 void KdenliveDoc::setZoom(int factor)
 {
-    m_zoom = factor;
+    m_documentProperties["zoom"] = QString::number(factor);
 }
 
 int KdenliveDoc::zoom() const
 {
-    return m_zoom;
+    return m_documentProperties.value("zoom").toInt();
 }
 
 void KdenliveDoc::setZone(int start, int end)
 {
-    m_zoneStart = start;
-    m_zoneEnd = end;
+    m_documentProperties["zonein"] = QString::number(start);
+    m_documentProperties["zoneout"] = QString::number(end);
 }
 
 QPoint KdenliveDoc::zone() const
 {
-    return QPoint(m_zoneStart, m_zoneEnd);
+    return QPoint(m_documentProperties.value("zonein").toInt(), m_documentProperties.value("zoneout").toInt());
 }
 
 bool KdenliveDoc::saveSceneList(const QString &path, const QString &scene)
 {
     QDomDocument sceneList;
     sceneList.setContent(scene, true);
-    QDomNode mlt = sceneList.elementsByTagName("mlt").at(0);
+    QDomElement mlt = sceneList.firstChildElement("mlt");
     QDomElement addedXml = sceneList.createElement("kdenlivedoc");
     mlt.appendChild(addedXml);
 
     QDomElement markers = sceneList.createElement("markers");
-    addedXml.setAttribute("version", "0.82");
+    addedXml.setAttribute("version", DOCUMENTVERSION);
     addedXml.setAttribute("kdenliveversion", VERSION);
     addedXml.setAttribute("profile", profilePath());
     addedXml.setAttribute("position", m_render->seekPosition().frames(m_fps));
-    addedXml.setAttribute("zonein", m_zoneStart);
-    addedXml.setAttribute("zoneout", m_zoneEnd);
     addedXml.setAttribute("projectfolder", m_projectFolder.path());
-    addedXml.setAttribute("zoom", m_zoom);
+
+    QDomElement docproperties = sceneList.createElement("documentproperties");
+    QMapIterator<QString, QString> i(m_documentProperties);
+    while (i.hasNext()) {
+        i.next();
+        docproperties.setAttribute(i.key(), i.value());
+    }
+    addedXml.appendChild(docproperties);
+
+    // Add profile info
+    QDomElement profileinfo = sceneList.createElement("profileinfo");
+    profileinfo.setAttribute("description", m_profile.description);
+    profileinfo.setAttribute("frame_rate_num", m_profile.frame_rate_num);
+    profileinfo.setAttribute("frame_rate_den", m_profile.frame_rate_den);
+    profileinfo.setAttribute("width", m_profile.width);
+    profileinfo.setAttribute("height", m_profile.height);
+    profileinfo.setAttribute("progressive", m_profile.progressive);
+    profileinfo.setAttribute("sample_aspect_num", m_profile.sample_aspect_num);
+    profileinfo.setAttribute("sample_aspect_den", m_profile.sample_aspect_den);
+    profileinfo.setAttribute("display_aspect_num", m_profile.display_aspect_num);
+    profileinfo.setAttribute("display_aspect_den", m_profile.display_aspect_den);
+    addedXml.appendChild(profileinfo);
 
     // tracks info
     QDomElement tracksinfo = sceneList.createElement("tracksinfo");
@@ -571,12 +581,56 @@ void KdenliveDoc::setProfilePath(QString path)
     if (path.isEmpty()) path = KdenliveSettings::default_profile();
     if (path.isEmpty()) path = "dv_pal";
     m_profile = ProfilesDialog::getVideoProfile(path);
+    if (m_profile.path.isEmpty()) {
+        // Profile not found, use embedded profile
+        QDomElement profileInfo = m_document.elementsByTagName("profileinfo").at(0).toElement();
+        if (profileInfo.isNull()) {
+            KMessageBox::information(kapp->activeWindow(), i18n("Project profile was not found, using default profile."), i18n("Missing Profile"));
+            m_profile = ProfilesDialog::getVideoProfile(KdenliveSettings::default_profile());
+        } else {
+            m_profile.description = profileInfo.attribute("description");
+            m_profile.frame_rate_num = profileInfo.attribute("frame_rate_num").toInt();
+            m_profile.frame_rate_den = profileInfo.attribute("frame_rate_den").toInt();
+            m_profile.width = profileInfo.attribute("width").toInt();
+            m_profile.height = profileInfo.attribute("height").toInt();
+            m_profile.progressive = profileInfo.attribute("progressive").toInt();
+            m_profile.sample_aspect_num = profileInfo.attribute("sample_aspect_num").toInt();
+            m_profile.sample_aspect_den = profileInfo.attribute("sample_aspect_den").toInt();
+            m_profile.display_aspect_num = profileInfo.attribute("display_aspect_num").toInt();
+            m_profile.display_aspect_den = profileInfo.attribute("display_aspect_den").toInt();
+            QString existing = ProfilesDialog::existingProfile(m_profile);
+            if (!existing.isEmpty()) {
+                m_profile = ProfilesDialog::getVideoProfile(existing);
+                KMessageBox::information(kapp->activeWindow(), i18n("Project profile not found, replacing with existing one: %1", m_profile.description), i18n("Missing Profile"));
+            } else {
+                QString newDesc = m_profile.description;
+                bool ok = true;
+                while (ok && (newDesc.isEmpty() || ProfilesDialog::existingProfileDescription(newDesc))) {
+                    newDesc = QInputDialog::getText(kapp->activeWindow(), i18n("Existing Profile"), i18n("Your project uses an unknown profile.\nIt uses an existing profile name: %1.\nPlease choose a new name to save it", newDesc), QLineEdit::Normal, newDesc, &ok);
+                }
+                if (ok == false) {
+                    // User canceled, use default profile
+                    m_profile = ProfilesDialog::getVideoProfile(KdenliveSettings::default_profile());
+                } else {
+                    if (newDesc != m_profile.description) {
+                        // Profile description existed, was replaced by new one
+                        m_profile.description = newDesc;
+                    } else {
+                        KMessageBox::information(kapp->activeWindow(), i18n("Project profile was not found, it will be added to your system now."), i18n("Missing Profile"));
+                    }
+                    ProfilesDialog::saveProfile(m_profile);
+                }
+            }
+            setModified(true);
+        }
+    }
+
     KdenliveSettings::setProject_display_ratio((double) m_profile.display_aspect_num / m_profile.display_aspect_den);
     m_fps = (double) m_profile.frame_rate_num / m_profile.frame_rate_den;
     KdenliveSettings::setProject_fps(m_fps);
     m_width = m_profile.width;
     m_height = m_profile.height;
-    kDebug() << "KDEnnlive document, init timecode from path: " << path << ",  " << m_fps;
+    kDebug() << "Kdenlive document, init timecode from path: " << path << ",  " << m_fps;
     if (m_fps == 30000.0 / 1001.0) m_timecode.setFormat(30, true);
     else m_timecode.setFormat((int) m_fps);
 }
@@ -1002,22 +1056,41 @@ DocClipBase *KdenliveDoc::getBaseClip(const QString &clipId)
     return m_clipManager->getClipById(clipId);
 }
 
-void KdenliveDoc::slotCreateTextClip(QString /*group*/, const QString &/*groupId*/)
+void KdenliveDoc::slotCreateTextClip(QString group, const QString &groupId, const QString &templatePath)
 {
     QString titlesFolder = projectFolder().path() + "/titles/";
     KStandardDirs::makeDir(titlesFolder);
-    TitleWidget *dia_ui = new TitleWidget(KUrl(), titlesFolder, m_render, kapp->activeWindow());
+    TitleWidget *dia_ui = new TitleWidget(templatePath, titlesFolder, m_render, kapp->activeWindow());
     if (dia_ui->exec() == QDialog::Accepted) {
         QStringList titleInfo = TitleWidget::getFreeTitleInfo(projectFolder());
         QImage pix = dia_ui->renderedPixmap();
         pix.save(titleInfo.at(1));
         //dia_ui->saveTitle(path + ".kdenlivetitle");
-        m_clipManager->slotAddTextClipFile(titleInfo.at(0), titleInfo.at(1), dia_ui->xml().toString(), QString(), QString());
+        m_clipManager->slotAddTextClipFile(titleInfo.at(0), titleInfo.at(1), dia_ui->xml().toString(), group, groupId);
         setModified(true);
     }
     delete dia_ui;
 }
 
+void KdenliveDoc::slotCreateTextTemplateClip(QString group, const QString &groupId, KUrl path)
+{
+    QString titlesFolder = projectFolder().path() + "/titles/";
+    if (path.isEmpty()) {
+        path = KFileDialog::getOpenUrl(KUrl(titlesFolder), "*.kdenlivetitle", kapp->activeWindow(), i18n("Enter Template Path"));
+    }
+
+    if (path.isEmpty()) return;
+
+    QStringList titleInfo = TitleWidget::getFreeTitleInfo(projectFolder());
+
+    TitleWidget *dia_ui = new TitleWidget(path, titlesFolder, m_render, kapp->activeWindow());
+    QImage pix = dia_ui->renderedPixmap();
+    pix.save(titleInfo.at(1));
+    delete dia_ui;
+    m_clipManager->slotAddTextTemplateClip(titleInfo.at(0), titleInfo.at(1), path, group, groupId);
+    setModified(true);
+}
+
 int KdenliveDoc::tracksCount() const
 {
     return m_tracksList.count();
@@ -1123,6 +1196,15 @@ bool KdenliveDoc::checkDocumentClips(QDomNodeList infoproducers)
     return (d.exec() == QDialog::Accepted);
 }
 
+void KdenliveDoc::setDocumentProperty(const QString &name, const QString &value)
+{
+    m_documentProperties[name] = value;
+}
+
+const QString KdenliveDoc::getDocumentProperty(const QString &name) const
+{
+    return m_documentProperties.value(name);
+}
 
 #include "kdenlivedoc.moc"