]> git.sesse.net Git - kdenlive/blobdiff - src/kdenlivedoc.cpp
Allow %width, %height and %dar as placeholders for rendering profiles:
[kdenlive] / src / kdenlivedoc.cpp
index e24df38947564477edc6bed731848f5b871723fe..5d7c6077d172ad583a3a26c0c5aeaa48f0cce0b0 100644 (file)
@@ -25,6 +25,8 @@
 #include <KIO/NetAccess>
 #include <KApplication>
 
+#include <QFile>
+
 #include <mlt++/Mlt.h>
 
 #include "kdenlivedoc.h"
@@ -56,10 +58,10 @@ KdenliveDoc::KdenliveDoc(const KUrl &url, const KUrl &projectFolder, QUndoGroup
                 m_zoom = infoXml.attribute("zoom", "7").toInt();
                 setProfilePath(profilePath);
                 double version = infoXml.attribute("version").toDouble();
-                if (version < 0.7) convertDocument(version);
+                if (version < 0.8) convertDocument(version);
                 else {
                     //delete all mlt producers and instead, use Kdenlive saved producers
-                    QDomNodeList prods = m_document.elementsByTagName("producer");
+                    /*QDomNodeList prods = m_document.elementsByTagName("producer");
                     int maxprod = prods.count();
                     int pos = 0;
                     for (int i = 0; i < maxprod; i++) {
@@ -68,19 +70,22 @@ KdenliveDoc::KdenliveDoc(const KUrl &url, const KUrl &projectFolder, QUndoGroup
                         if (prodId == "black" || prodId.startsWith("slowmotion"))
                             pos++;
                         else westley.removeChild(m);
-                    }
-                    prods = m_document.elementsByTagName("kdenlive_producer");
+                    }*/
+                    /*prods = m_document.elementsByTagName("kdenlive_producer");
                     maxprod = prods.count();
                     for (int i = 0; i < maxprod; i++) {
                         prods.at(0).toElement().setTagName("producer");
                         westley.insertBefore(prods.at(0), QDomNode());
-                    }
+                    }*/
                 }
                 QDomElement e;
                 QDomNodeList producers = m_document.elementsByTagName("producer");
+                QDomNodeList infoproducers = m_document.elementsByTagName("kdenlive_producer");
                 const int max = producers.count();
+                const int infomax = infoproducers.count();
+
                 if (max > 0) {
-                    m_documentLoadingStep = 100.0 / (max + m_document.elementsByTagName("entry").count());
+                    m_documentLoadingStep = 100.0 / (max + infomax + m_document.elementsByTagName("entry").count());
                     parent->slotGotProgressInfo(i18n("Loading project clips"), (int) m_documentLoadingProgress);
                 }
 
@@ -89,11 +94,27 @@ KdenliveDoc::KdenliveDoc(const KUrl &url, const KUrl &projectFolder, QUndoGroup
                     if (m_documentLoadingStep > 0) {
                         m_documentLoadingProgress += m_documentLoadingStep;
                         parent->slotGotProgressInfo(QString(), (int) m_documentLoadingProgress);
-                        qApp->processEvents();
+                        //qApp->processEvents();
                     }
                     QString prodId = e.attribute("id");
                     if (!e.isNull() && prodId != "black" && !prodId.startsWith("slowmotion")/*&& prodId.toInt() > 0*/) {
-                        addClip(e, prodId);
+                        // addClip(e, prodId, false);
+                        kDebug() << "// PROD: " << prodId;
+                    }
+                }
+
+                for (int i = 0; i < infomax; 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")) {
+                        e.setTagName("producer");
+                        addClipInfo(e, prodId);
+                        kDebug() << "// NLIVE PROD: " << prodId;
                     }
                 }
 
@@ -114,7 +135,7 @@ KdenliveDoc::KdenliveDoc(const KUrl &url, const KUrl &projectFolder, QUndoGroup
                 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();
+                kWarning() << "  NO KDENLIVE INFO FOUND IN FILE: " << url.path();
                 m_document = createEmptyDocument(tracks.x(), tracks.y());
                 setProfilePath(profileName);
             }
@@ -254,7 +275,9 @@ void KdenliveDoc::slotAutoSave() {
             kDebug() << "ERROR; CANNOT CREATE AUTOSAVE FILE";
         }
         kDebug() << "// AUTOSAVE FILE: " << m_autosave->fileName();
-        m_render->saveSceneList(m_autosave->fileName(), documentInfoXml());
+        QDomDocument doc;
+        doc.setContent(m_render->sceneList());
+        saveSceneList(m_autosave->fileName(), doc);
     }
 }
 
@@ -267,10 +290,17 @@ int KdenliveDoc::zoom() const {
 }
 
 void KdenliveDoc::convertDocument(double version) {
+    kDebug() << "Opening a document with version " << version;
     // Opening a old Kdenlive document
+    if (version == 0.7) {
+        kDebug() << "Unable to open document with version " << version;
+        // TODO: convert 0.7 files to the new document format.
+        return;
+    }
     QDomNode westley = m_document.elementsByTagName("westley").at(1);
     QDomNode tractor = m_document.elementsByTagName("tractor").at(0);
     QDomNode kdenlivedoc = m_document.elementsByTagName("kdenlivedoc").at(0);
+    QDomElement kdenlivedoc_old = kdenlivedoc.cloneNode(true).toElement(); // Needed for folders
     QDomNode multitrack = m_document.elementsByTagName("multitrack").at(0);
     QDomNodeList playlists = m_document.elementsByTagName("playlist");
 
@@ -297,7 +327,46 @@ void KdenliveDoc::convertDocument(double version) {
         }
         track.setAttribute("producer", playlist_id);
         //tractor.appendChild(track);
+#define KEEP_TRACK_ORDER 1
+#ifdef KEEP_TRACK_ORDER
         tractor.insertAfter(track, QDomNode());
+#else
+        // Insert the new track in an order that hopefully matches the 3 video, then 2 audio tracks of Kdenlive 0.7.0
+        // insertion sort - O( tracks*tracks )
+        // Note, this breaks _all_ transitions - but you can move them up and down afterwards.
+        QDomElement tractor_elem = tractor.toElement();
+        if (! tractor_elem.isNull()) {
+            QDomNodeList tracks = tractor_elem.elementsByTagName("track");
+            int size = tracks.size();
+            if (size == 0) {
+                tractor.insertAfter(track, QDomNode());
+            } else {
+                bool inserted = false;
+                for (int i = 0; i < size; ++i) {
+                    QDomElement track_elem = tracks.at(i).toElement();
+                    if (track_elem.isNull()) {
+                        tractor.insertAfter(track, QDomNode());
+                        inserted = true;
+                        break;
+                    } else {
+                        kDebug() << "playlist_id: " << playlist_id << " producer:" << track_elem.attribute("producer");
+                        if (playlist_id < track_elem.attribute("producer")) {
+                            tractor.insertBefore(track, track_elem);
+                            inserted = true;
+                            break;
+                        }
+                    }
+                }
+                // Reach here, no insertion, insert last
+                if (!inserted) {
+                    tractor.insertAfter(track, QDomNode());
+                }
+            }
+        } else {
+            kWarning() << "tractor was not a QDomElement";
+            tractor.insertAfter(track, QDomNode());
+        }
+#endif
     }
     tractor.removeChild(multitrack);
 
@@ -416,6 +485,7 @@ void KdenliveDoc::convertDocument(double version) {
     }
     // move producers to correct place, markers to a global list, fix clip descriptions
     QDomElement markers = m_document.createElement("markers");
+    // This will get the westley producers:
     producers = m_document.elementsByTagName("producer");
     max = producers.count();
     for (int i = 0; i < max; i++) {
@@ -444,7 +514,7 @@ void KdenliveDoc::convertDocument(double version) {
                         QString objectxml;
                         QDomElement ob = objects.at(k).toElement();
                         if (ob.attribute("type") == "3") {
-                            // text object
+                            // text object - all of this goes into "xmldata"...
                             QDomElement item = tdoc.createElement("item");
                             item.setAttribute("z-index", ob.attribute("z"));
                             item.setAttribute("type", "QGraphicsTextItem");
@@ -460,6 +530,9 @@ void KdenliveDoc::convertDocument(double version) {
                             QString col = ob.attribute("color");
                             QColor c(col);
                             content.setAttribute("font-color", colorToString(c));
+                            // todo: These fields are missing from the newly generated xmldata:
+                            // transform, startviewport, endviewport, background
+
                             QDomText conttxt = tdoc.createTextNode(ob.attribute("text"));
                             content.appendChild(conttxt);
                             item.appendChild(position);
@@ -488,12 +561,13 @@ void KdenliveDoc::convertDocument(double version) {
                         }
                     }
                     prod.setAttribute("xmldata", tdoc.toString());
-                    QStringList titleInfo = TitleWidget::getFreeTitleInfo(projectFolder());
-                    prod.setAttribute("titlename", titleInfo.at(0));
-                    prod.setAttribute("resource", titleInfo.at(1));
+                    // mbd todo: This clearly does not work, as every title gets the same name - trying to leave it empty
+                    // QStringList titleInfo = TitleWidget::getFreeTitleInfo(projectFolder());
+                    // prod.setAttribute("titlename", titleInfo.at(0));
+                    // prod.setAttribute("resource", titleInfo.at(1));
                     //kDebug()<<"TITLE DATA:\n"<<tdoc.toString();
                     prod.removeChild(m);
-                }
+                } // End conversion of title clips.
 
             } else if (m.isText()) {
                 QString comment = m.nodeValue();
@@ -505,13 +579,98 @@ void KdenliveDoc::convertDocument(double version) {
         }
         int duration = prod.attribute("duration").toInt();
         if (duration > 0) prod.setAttribute("out", QString::number(duration));
+        // The clip goes back in, but text clips should not go back in, at least not modified
         westley.insertBefore(prod, QDomNode());
 
     }
 
     QDomNode westley0 = m_document.elementsByTagName("westley").at(0);
     if (!markers.firstChild().isNull()) westley0.appendChild(markers);
+
+
+    // Convert as much of the kdenlivedoc as possible. Use the producer in westley
+    // First, remove the old stuff from westley, and add a new empty one
+    // Also, track the max id in order to use it for the adding of groups/folders
+    int max_kproducer_id = 0;
     westley0.removeChild(kdenlivedoc);
+    QDomElement kdenlivedoc_new = m_document.createElement("kdenlivedoc");
+    kdenlivedoc_new.setAttribute("profile", profile);
+    // Add all the producers that has a ressource in westley
+    QDomElement westley_element = westley0.toElement();
+    if (westley_element.isNull()) {
+        kWarning() << "westley0 element in document was not a QDomElement - unable to add producers to new kdenlivedoc";
+    } else {
+        QDomNodeList wproducers = westley_element.elementsByTagName("producer");
+        int kmax = wproducers.count();
+        for (int i = 0; i < kmax; i++) {
+            QDomElement wproducer = wproducers.at(i).toElement();
+            if (wproducer.isNull()) {
+                kWarning() << "Found producer in westley0, that was not a QDomElement";
+            } else {
+                // We have to do slightly different things, depending on the type
+                kDebug() << "Converting producer element with type " << wproducer.attribute("type");
+                if (wproducer.attribute("type").toInt() == TEXT) {
+                    kDebug() << "Found TEXT element in producer" << endl;
+                    QDomElement kproducer = wproducer.cloneNode(true).toElement();
+                    kproducer.setTagName("kdenlive_producer");
+                    kdenlivedoc_new.appendChild(kproducer);
+                    // todo: Perhaps needs some more changes here to "frequency", aspect ratio as a float, frame_size, channels, and later, ressource and title name
+                } else {
+                    QDomElement kproducer = m_document.createElement("kdenlive_producer");
+                    kproducer.setAttribute("id", wproducer.attribute("id"));
+                    kproducer.setAttribute("description", wproducer.attribute("description"));
+                    kproducer.setAttribute("resource", wproducer.attribute("resource"));
+                    kproducer.setAttribute("type", wproducer.attribute("type"));
+                    kdenlivedoc_new.appendChild(kproducer);
+                }
+                if (wproducer.attribute("id").toInt() > max_kproducer_id) {
+                    max_kproducer_id = wproducer.attribute("id").toInt();
+                }
+            }
+        }
+    }
+#define LOOKUP_FOLDER 1
+#ifdef LOOKUP_FOLDER
+    // Look through all the folder elements of the old doc, for each folder, for each producer,
+    // get the id, look it up in the new doc, set the groupname and groupid
+    // Note, this does not work at the moment - at least one folders shows up missing, and clips with no folder
+    // does not show up.
+    //    QDomElement kdenlivedoc_old = kdenlivedoc.toElement();
+    if (!kdenlivedoc_old.isNull()) {
+        QDomNodeList folders = kdenlivedoc_old.elementsByTagName("folder");
+        int fsize = folders.size();
+        int groupId = max_kproducer_id + 1; // Start at +1 of max id of the kdenlive_producers
+        for (int i = 0; i < fsize; ++i) {
+            QDomElement folder = folders.at(i).toElement();
+            if (!folder.isNull()) {
+                QString groupName = folder.attribute("name");
+                kDebug() << "groupName: " << groupName << " with groupId: " << groupId;
+                QDomNodeList fproducers = folder.elementsByTagName("producer");
+                int psize = fproducers.size();
+                for (int j = 0; j < psize; ++j) {
+                    QDomElement fproducer = fproducers.at(j).toElement();
+                    if (!fproducer.isNull()) {
+                        QString id = fproducer.attribute("id");
+                        // This is not very effective, but compared to loading the clips, its a breeze
+                        QDomNodeList kdenlive_producers = kdenlivedoc_new.elementsByTagName("kdenlive_producer");
+                        int kpsize = kdenlive_producers.size();
+                        for (int k = 0; k < kpsize; ++k) {
+                            QDomElement kproducer = kdenlive_producers.at(k).toElement(); // Its an element for sure
+                            if (id == kproducer.attribute("id")) {
+                                // We do not check that it already is part of a folder
+                                kproducer.setAttribute("groupid", groupId);
+                                kproducer.setAttribute("groupname", groupName);
+                                break;
+                            }
+                        }
+                    }
+                }
+                ++groupId;
+            }
+        }
+    }
+#endif
+    westley0.appendChild(kdenlivedoc_new);
 
     QDomNodeList elements = westley.childNodes();
     max = elements.count();
@@ -523,7 +682,17 @@ void KdenliveDoc::convertDocument(double version) {
     westley0.removeChild(westley);
 
     //kDebug() << "/////////////////  CONVERTED DOC:";
-    //kDebug() << m_document.toString();
+    // kDebug() << m_document.toString();
+    /*
+    QFile file( "converted.kdenlive" );
+    if ( file.open( QIODevice::WriteOnly ) ) {
+      QTextStream stream( &file );
+      stream << m_document.toString();
+      file.close();
+    } else {
+      kDebug() << "Unable to dump file to converted.kdenlive";
+    }
+    */
     //kDebug() << "/////////////////  END CONVERTED DOC:";
 }
 
@@ -533,6 +702,49 @@ QString KdenliveDoc::colorToString(const QColor& c) {
     return ret;
 }
 
+bool KdenliveDoc::saveSceneList(const QString &path, QDomDocument sceneList) {
+    QDomNode wes = sceneList.elementsByTagName("westley").at(0);
+
+    QDomElement addedXml = sceneList.createElement("kdenlivedoc");
+    QDomElement markers = sceneList.createElement("markers");
+    addedXml.setAttribute("version", "0.8");
+    addedXml.setAttribute("profile", profilePath());
+    addedXml.setAttribute("position", m_render->seekPosition().frames(m_fps));
+    addedXml.setAttribute("zoom", m_zoom);
+
+    QDomElement e;
+    QList <DocClipBase*> list = m_clipManager->documentClipList();
+    for (int i = 0; i < list.count(); i++) {
+        e = list.at(i)->toXML();
+        e.setTagName("kdenlive_producer");
+        addedXml.appendChild(sceneList.importNode(e, true));
+        QList < CommentedTime > marks = list.at(i)->commentedSnapMarkers();
+        for (int j = 0; j < marks.count(); j++) {
+            QDomElement marker = sceneList.createElement("marker");
+            marker.setAttribute("time", marks.at(j).time().ms() / 1000);
+            marker.setAttribute("comment", marks.at(j).comment());
+            marker.setAttribute("id", e.attribute("id"));
+            markers.appendChild(marker);
+        }
+    }
+    addedXml.appendChild(markers);
+    if (!m_guidesXml.isNull()) addedXml.appendChild(sceneList.importNode(m_guidesXml, true));
+
+    wes.appendChild(addedXml);
+    //wes.appendChild(doc.importNode(kdenliveData, true));
+
+    QFile file(path);
+    if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
+        kWarning() << "//////  ERROR writing to file: " << path;
+        KMessageBox::error(kapp->activeWindow(), i18n("Cannot write to file %1", path));
+        return false;
+    }
+    QTextStream out(&file);
+    out << sceneList.toString();
+    file.close();
+    return true;
+}
+
 QDomElement KdenliveDoc::documentInfoXml() {
     QDomDocument doc;
     QDomElement e;
@@ -572,12 +784,6 @@ KUrl KdenliveDoc::projectFolder() const {
     return m_projectFolder;
 }
 
-QString KdenliveDoc::getDocumentStandard() {
-    //WARNING: this way to tell the video standard is a bit hackish...
-    if (m_profile.description.contains("pal", Qt::CaseInsensitive) || m_profile.description.contains("25", Qt::CaseInsensitive) || m_profile.description.contains("50", Qt::CaseInsensitive)) return "PAL";
-    return "NTSC";
-}
-
 QString KdenliveDoc::profilePath() const {
     return m_profile.path;
 }
@@ -620,7 +826,7 @@ void KdenliveDoc::setRenderer(Render *render) {
     if (m_render) return;
     m_render = render;
     emit progressInfo(i18n("Loading playlist..."), 0);
-    qApp->processEvents();
+    //qApp->processEvents();
     if (m_render) {
         m_render->setSceneList(m_document.toString(), m_startPos);
         checkProjectClips();
@@ -632,13 +838,17 @@ void KdenliveDoc::checkProjectClips() {
     if (m_render == NULL) return;
     QList <Mlt::Producer *> prods = m_render->producersList();
     QString id ;
+    QString prodId ;
+    QString prodTrack ;
     for (int i = 0; i < prods.count(); i++) {
         id = prods.at(i)->get("id");
-        DocClipBase *clip = m_clipManager->getClipById(id);
-        if (clip && clip->producer() == NULL) {
-            clip->setProducer(prods.at(i));
-        }
-        if (clip->clipType() == TEXT && !QFile::exists(clip->fileURL().path())) {
+        prodId = id.section('_', 0, 0);
+        prodTrack = id.section('_', 1, 1);
+        kDebug() << "CHECK PRO CLIP, ID: " << id;
+        DocClipBase *clip = m_clipManager->getClipById(prodId);
+        if (clip) clip->setProducer(prods.at(i));
+        kDebug() << "CHECK PRO CLIP, ID: " << id << " DONE";
+        if (clip && clip->clipType() == TEXT && !QFile::exists(clip->fileURL().path())) {
             // regenerate text clip image if required
             kDebug() << "// TITLE: " << clip->getProperty("titlename") << " Preview file: " << clip->getProperty("resource") << " DOES NOT EXIST";
             QString titlename = clip->getProperty("titlename");
@@ -787,11 +997,35 @@ QString KdenliveDoc::description() const {
         return m_url.fileName() + " / " + m_profile.description;
 }
 
-void KdenliveDoc::addClip(const QDomElement &elem, const QString &clipId) {
-    DocClipBase *clip = new DocClipBase(m_clipManager, elem, clipId);
-    kDebug() << "/////////  DOCUM, CREATING NEW CLIP, ID:" << clipId << ", PAR ID:" << elem.attribute("groupid");
-    m_clipManager->addClip(clip);
-    emit addProjectClip(clip);
+void KdenliveDoc::addClip(QDomElement elem, QString clipId, bool createClipItem) {
+    const QString producerId = clipId.section('_', 0, 0);
+    int subtrack = clipId.section('_', 1, 1).toInt();
+    DocClipBase *clip = m_clipManager->getClipById(producerId);
+    if (clip == NULL) {
+        elem.setAttribute("id", producerId);
+        clip = new DocClipBase(m_clipManager, elem, producerId);
+        m_clipManager->addClip(clip);
+    }
+    if (createClipItem) emit addProjectClip(clip);
+}
+
+void KdenliveDoc::addClipInfo(QDomElement elem, QString clipId) {
+    DocClipBase *clip = m_clipManager->getClipById(clipId);
+    if (clip == NULL) {
+        addClip(elem, clipId);
+    } else {
+        QMap <QString, QString> properties;
+        QDomNamedNodeMap attributes = elem.attributes();
+        QString attrname;
+        for (unsigned int i = 0; i < attributes.count(); i++) {
+            attrname = attributes.item(i).nodeName();
+            if (attrname != "resource")
+                properties.insert(attrname, attributes.item(i).nodeValue());
+            kDebug() << attrname << " = " << attributes.item(i).nodeValue();
+        }
+        clip->setProperties(properties);
+        emit addProjectClip(clip);
+    }
 }
 
 void KdenliveDoc::addFolder(const QString foldername, const QString &clipId, bool edit) {