]> git.sesse.net Git - kdenlive/blobdiff - src/documentvalidator.cpp
Keyframe widget: Make it possible to change the position of a keyframe with more...
[kdenlive] / src / documentvalidator.cpp
index 62c46352ff8d57b3a228474637bfa70b17ca48d6..e6e0be7edaab395330d09b06003bc9b6c87986d9 100644 (file)
@@ -36,36 +36,34 @@ DocumentValidator::DocumentValidator(QDomDocument doc):
 
 bool DocumentValidator::validate(const double currentVersion)
 {
-    // Check if we're validating a Kdenlive project
-    if (!isProject())
+    QDomElement mlt = m_doc.firstChildElement("mlt");
+    // At least the root element must be there
+    if (mlt.isNull())
         return false;
 
+    QDomElement kdenliveDoc = mlt.firstChildElement("kdenlivedoc");
+    // Check if we're validating a Kdenlive project
+    if (kdenliveDoc.isNull())
+        return false;
     // Upgrade the document to the latest version
-    QDomNode kdenlivedocNode = m_doc.elementsByTagName("kdenlivedoc").at(0);
-    QDomElement kdenlivedocElm = kdenlivedocNode.toElement();
-    if (!upgrade(kdenlivedocElm.attribute("version").toDouble(), currentVersion))
+    if (!upgrade(kdenliveDoc.attribute("version").toDouble(), currentVersion))
         return false;
 
     /*
      * Check the syntax (this will be replaced by XSD validation with Qt 4.6)
      * and correct some errors
      */
-    QDomNode mltNode = m_doc.elementsByTagName("mlt").at(0);
-    QDomElement mltElm = mltNode.toElement();
-    if (mltElm.isNull()) // At least the root element must be there
-        return false;
-    else {
+    {
         // Return (or create) the tractor
-        QDomNode tractorNode = m_doc.elementsByTagName("tractor").at(0);
-        QDomElement tractorElm = tractorNode.toElement();
-        if (tractorElm.isNull()) {
+        QDomElement tractor = mlt.firstChildElement("tractor");
+        if (tractor.isNull()) {
             m_modified = true;
-            tractorElm = m_doc.createElement("tractor");
-            tractorElm.setAttribute("global_feed", "1");
-            tractorElm.setAttribute("in", "0");
-            tractorElm.setAttribute("out", "-1");
-            tractorElm.setAttribute("id", "maintractor");
-            mltElm.appendChild(tractorElm);
+            tractor = m_doc.createElement("tractor");
+            tractor.setAttribute("global_feed", "1");
+            tractor.setAttribute("in", "0");
+            tractor.setAttribute("out", "-1");
+            tractor.setAttribute("id", "maintractor");
+            mlt.appendChild(tractor);
         }
 
         /*
@@ -74,36 +72,67 @@ bool DocumentValidator::validate(const double currentVersion)
          */
         QDomNodeList playlists = m_doc.elementsByTagName("playlist");
         int tracksMax = playlists.count() - 1; // Remove the black track
-        QDomNodeList tracks = m_doc.elementsByTagName("track");
+        QDomNodeList tracks = tractor.elementsByTagName("track");
         tracksMax = qMax(tracks.count() - 1, tracksMax);
-        QDomNodeList tracksinfo = m_doc.elementsByTagName("trackinfo");
+        QDomNodeList tracksinfo = kdenliveDoc.elementsByTagName("trackinfo");
         tracksMax = qMax(tracksinfo.count(), tracksMax);
         tracksMax = qMax(1, tracksMax); // Force existance of one track
         if (playlists.count() - 1 < tracksMax ||
-            tracks.count() - 1 < tracksMax ||
-            tracksinfo.count() < tracksMax) {
+                tracks.count() - 1 < tracksMax ||
+                tracksinfo.count() < tracksMax) {
+            kDebug() << "//// WARNING, PROJECT IS CORRUPTED, MISSING TRACK";
             m_modified = true;
             int difference;
+            // use the MLT tracks as reference
+            if (tracks.count() - 1 < tracksMax) {
+                // Looks like one MLT track is missing, remove the extra Kdenlive track if there is one.
+                if (tracksinfo.count() != tracks.count() - 1) {
+                    // The Kdenlive tracks are not ok, clear and rebuild them
+                    QDomNode tinfo = kdenliveDoc.elementsByTagName("tracksinfo").at(0);
+                    QDomNode tnode = tinfo.firstChild();
+                    while (!tnode.isNull()) {
+                        tinfo.removeChild(tnode);
+                        tnode = tinfo.firstChild();
+                    }
+
+                    for (int i = 1; i < tracks.count(); i++) {
+                        QString hide = tracks.at(i).toElement().attribute("hide");
+                        QDomElement newTrack = m_doc.createElement("trackinfo");
+                        if (hide == "video") {
+                            // audio track;
+                            newTrack.setAttribute("type", "audio");
+                            newTrack.setAttribute("blind", 1);
+                            newTrack.setAttribute("mute", 0);
+                            newTrack.setAttribute("lock", 0);
+                        } else {
+                            newTrack.setAttribute("blind", 0);
+                            newTrack.setAttribute("mute", 0);
+                            newTrack.setAttribute("lock", 0);
+                        }
+                        tinfo.appendChild(newTrack);
+                    }
+                }
+            }
+
             if (playlists.count() - 1 < tracksMax) {
                 difference = tracksMax - (playlists.count() - 1);
                 for (int i = 0; i < difference; ++i) {
                     QDomElement playlist = m_doc.createElement("playlist");
-                    mltElm.appendChild(playlist);
+                    mlt.appendChild(playlist);
                 }
             }
             if (tracks.count() - 1 < tracksMax) {
                 difference = tracksMax - (tracks.count() - 1);
                 for (int i = 0; i < difference; ++i) {
                     QDomElement track = m_doc.createElement("track");
-                    tractorElm.appendChild(track);
+                    tractor.appendChild(track);
                 }
             }
             if (tracksinfo.count() < tracksMax) {
-                QDomNode tracksinfoNode = m_doc.elementsByTagName("tracksinfo").at(0);
-                QDomElement tracksinfoElm = tracksinfoNode.toElement();
+                QDomElement tracksinfoElm = kdenliveDoc.firstChildElement("tracksinfo");
                 if (tracksinfoElm.isNull()) {
                     tracksinfoElm = m_doc.createElement("tracksinfo");
-                    kdenlivedocElm.appendChild(tracksinfoElm);
+                    kdenliveDoc.appendChild(tracksinfoElm);
                 }
                 difference = tracksMax - tracksinfo.count();
                 for (int i = 0; i < difference; ++i) {
@@ -128,14 +157,13 @@ bool DocumentValidator::upgrade(double version, const double currentVersion)
 
     // No conversion needed
     if (version == currentVersion) {
-        if (!m_doc.toString().contains("font-size")) // TODO: remove when currentVersion == 0.84
-            return true;
+        return true;
     }
 
     // The document is too new
     if (version > currentVersion) {
         kDebug() << "Unable to open document with version " << version;
-        KMessageBox::sorry(kapp->activeWindow(), i18n("This project type is unsupported (version %1) and can't be loaded.\nPlease consider upgrading you Kdenlive version.", version), i18n("Unable to open project"));
+        KMessageBox::sorry(kapp->activeWindow(), i18n("This project type is unsupported (version %1) and can't be loaded.\nPlease consider upgrading your Kdenlive version.", version), i18n("Unable to open project"));
         return false;
     }
 
@@ -149,6 +177,7 @@ bool DocumentValidator::upgrade(double version, const double currentVersion)
     // <kdenlivedoc />
     QDomNode infoXmlNode = m_doc.elementsByTagName("kdenlivedoc").at(0);
     QDomElement infoXml = infoXmlNode.toElement();
+    infoXml.setAttribute("upgraded", "1");
 
     if (version <= 0.6) {
         QDomElement infoXml_old = infoXmlNode.cloneNode(true).toElement(); // Needed for folders
@@ -194,61 +223,61 @@ bool DocumentValidator::upgrade(double version, const double currentVersion)
                 }
             }
         } else for (int i = 0; i < max; i++) {
-            QDomNode n = playlists.at(i);
-            westley.insertBefore(n, QDomNode());
-            QDomElement pl = n.toElement();
-            QDomElement track = m_doc.createElement("track");
-            QString trackType = pl.attribute("hide");
-            if (!trackType.isEmpty())
-                track.setAttribute("hide", trackType);
-            QString playlist_id =  pl.attribute("id");
-            if (playlist_id.isEmpty()) {
-                playlist_id = "black_track";
-                pl.setAttribute("id", playlist_id);
-            }
-            track.setAttribute("producer", playlist_id);
-            //tractor.appendChild(track);
+                QDomNode n = playlists.at(i);
+                westley.insertBefore(n, QDomNode());
+                QDomElement pl = n.toElement();
+                QDomElement track = m_doc.createElement("track");
+                QString trackType = pl.attribute("hide");
+                if (!trackType.isEmpty())
+                    track.setAttribute("hide", trackType);
+                QString playlist_id =  pl.attribute("id");
+                if (playlist_id.isEmpty()) {
+                    playlist_id = "black_track";
+                    pl.setAttribute("id", playlist_id);
+                }
+                track.setAttribute("producer", playlist_id);
+                //tractor.appendChild(track);
 #define KEEP_TRACK_ORDER 1
 #ifdef KEEP_TRACK_ORDER
-            tractor.insertAfter(track, QDomNode());
+                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);
+                // 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());
+                        }
                     }
-                    // Reach here, no insertion, insert last
-                    if (!inserted) {
-                        tractor.insertAfter(track, QDomNode());
-                    }
+                } else {
+                    kWarning() << "tractor was not a QDomElement";
+                    tractor.insertAfter(track, QDomNode());
                 }
-            } else {
-                kWarning() << "tractor was not a QDomElement";
-                tractor.insertAfter(track, QDomNode());
-            }
 #endif
-        }
+            }
         tractor.removeChild(multitrack);
 
         // audio track mixing transitions should not be added to track view, so add required attribute
@@ -675,40 +704,118 @@ bool DocumentValidator::upgrade(double version, const double currentVersion)
     if (version <= 0.83) {
         // Replace point size with pixel size in text titles
         if (m_doc.toString().contains("font-size")) {
+            KMessageBox::ButtonCode convert = KMessageBox::Continue;
             QDomNodeList kproducerNodes = m_doc.elementsByTagName("kdenlive_producer");
-            for (int i = 0; i < kproducerNodes.count(); ++i) {
+            for (int i = 0; i < kproducerNodes.count() && convert != KMessageBox::No; ++i) {
                 QDomElement kproducer = kproducerNodes.at(i).toElement();
                 if (kproducer.attribute("type").toInt() == TEXT) {
                     QDomDocument data;
                     data.setContent(kproducer.attribute("xmldata"));
                     QDomNodeList items = data.firstChild().childNodes();
-                    for (int j = 0; j < items.count(); ++j) {
+                    for (int j = 0; j < items.count() && convert != KMessageBox::No; ++j) {
                         if (items.at(j).attributes().namedItem("type").nodeValue() == "QGraphicsTextItem") {
                             QDomNamedNodeMap textProperties = items.at(j).namedItem("content").attributes();
-                            if (textProperties.namedItem("font-pixel-size").isNull()) {
-                                QFont font;
-                                font.setPointSize(textProperties.namedItem("font-size").nodeValue().toInt());
-                                QDomElement content = items.at(j).namedItem("content").toElement();
-                                content.setAttribute("font-pixel-size", QFontInfo(font).pixelSize());
-                                content.removeAttribute("font-size");
-                                kproducer.setAttribute("xmldata", data.toString());
-                                /*
-                                 * You may be tempted to delete the preview file
-                                 * to force its recreation: bad idea (see
-                                 * http://www.kdenlive.org/mantis/view.php?id=749)
-                                 */
+                            if (textProperties.namedItem("font-pixel-size").isNull() && !textProperties.namedItem("font-size").isNull()) {
+                                // Ask the user if he wants to convert
+                                if (convert != KMessageBox::Yes && convert != KMessageBox::No)
+                                    convert = (KMessageBox::ButtonCode)KMessageBox::warningYesNo(kapp->activeWindow(), i18n("Some of your text clips were saved with size in points, which means different sizes on different displays. Do you want to convert them to pixel size, making them portable? It is recommended you do this on the computer they were first created on, or you could have to adjust their size."), i18n("Update Text Clips"));
+                                if (convert == KMessageBox::Yes) {
+                                    QFont font;
+                                    font.setPointSize(textProperties.namedItem("font-size").nodeValue().toInt());
+                                    QDomElement content = items.at(j).namedItem("content").toElement();
+                                    content.setAttribute("font-pixel-size", QFontInfo(font).pixelSize());
+                                    content.removeAttribute("font-size");
+                                    kproducer.setAttribute("xmldata", data.toString());
+                                    /*
+                                     * You may be tempted to delete the preview file
+                                     * to force its recreation: bad idea (see
+                                     * http://www.kdenlive.org/mantis/view.php?id=749)
+                                     */
+                                }
                             }
                         }
                     }
                 }
             }
         }
+
+        // Fill the <documentproperties /> element
+        QDomElement docProperties = infoXml.firstChildElement("documentproperties");
+        if (docProperties.isNull()) {
+            docProperties = m_doc.createElement("documentproperties");
+            docProperties.setAttribute("zonein", infoXml.attribute("zonein"));
+            docProperties.setAttribute("zoneout", infoXml.attribute("zoneout"));
+            docProperties.setAttribute("zoom", infoXml.attribute("zoom"));
+            docProperties.setAttribute("position", infoXml.attribute("position"));
+            infoXml.appendChild(docProperties);
+        }
     }
 
+    if (version <= 0.84) {
+        // update the title clips to use the new MLT kdenlivetitle producer
+        QDomNodeList kproducerNodes = m_doc.elementsByTagName("kdenlive_producer");
+        for (int i = 0; i < kproducerNodes.count(); ++i) {
+            QDomElement kproducer = kproducerNodes.at(i).toElement();
+            if (kproducer.attribute("type").toInt() == TEXT) {
+                QString data = kproducer.attribute("xmldata");
+                QString datafile = kproducer.attribute("resource");
+                if (!datafile.endsWith(".kdenlivetitle")) {
+                    datafile = QString();
+                    kproducer.setAttribute("resource", QString());
+                }
+                QString id = kproducer.attribute("id");
+                QDomNodeList mltproducers = m_doc.elementsByTagName("producer");
+                bool foundData = false;
+                bool foundResource = false;
+                bool foundService = false;
+                for (int j = 0; j < mltproducers.count(); j++) {
+                    QDomElement wproducer = mltproducers.at(j).toElement();
+                    if (wproducer.attribute("id") == id) {
+                        QDomNodeList props = wproducer.childNodes();
+                        for (int k = 0; k < props.count(); k++) {
+                            if (props.at(k).toElement().attribute("name") == "xmldata") {
+                                props.at(k).firstChild().setNodeValue(data);
+                                foundData = true;
+                            } else if (props.at(k).toElement().attribute("name") == "mlt_service") {
+                                props.at(k).firstChild().setNodeValue("kdenlivetitle");
+                                foundService = true;
+                            } else if (props.at(k).toElement().attribute("name") == "resource") {
+                                props.at(k).firstChild().setNodeValue(datafile);
+                                foundResource = true;
+                            }
+                        }
+                        if (!foundData) {
+                            QDomElement e = m_doc.createElement("property");
+                            e.setAttribute("name", "xmldata");
+                            QDomText value = m_doc.createTextNode(data);
+                            e.appendChild(value);
+                            wproducer.appendChild(e);
+                        }
+                        if (!foundService) {
+                            QDomElement e = m_doc.createElement("property");
+                            e.setAttribute("name", "mlt_service");
+                            QDomText value = m_doc.createTextNode("kdenlivetitle");
+                            e.appendChild(value);
+                            wproducer.appendChild(e);
+                        }
+                        if (!foundResource) {
+                            QDomElement e = m_doc.createElement("property");
+                            e.setAttribute("name", "resource");
+                            QDomText value = m_doc.createTextNode(datafile);
+                            e.appendChild(value);
+                            wproducer.appendChild(e);
+                        }
+                        break;
+                    }
+                }
+            }
+        }
+    }
+
+
     // The document has been converted: mark it as modified
     infoXml.setAttribute("version", currentVersion);
     m_modified = true;
-
     return true;
 }