]> git.sesse.net Git - kdenlive/blobdiff - src/kdenlivedoc.cpp
Display Jpeg exif data in clip properties metadata
[kdenlive] / src / kdenlivedoc.cpp
index d76aa59bb8390ac0c548a15bd72150abb585ef6e..2e0b13aca5df3ed8b5a4d12e90ed9d758113e10b 100644 (file)
 #include <QCryptographicHash>
 #include <QFile>
 #include <QInputDialog>
+#include <QDomImplementation>
 
 #include <mlt++/Mlt.h>
 
-const double DOCUMENTVERSION = 0.84;
+const double DOCUMENTVERSION = 0.85;
 
 KdenliveDoc::KdenliveDoc(const KUrl &url, const KUrl &projectFolder, QUndoGroup *undoGroup, QString profileName, const QPoint tracks, Render *render, MainWindow *parent) :
         QObject(parent),
@@ -55,8 +56,6 @@ KdenliveDoc::KdenliveDoc(const KUrl &url, const KUrl &projectFolder, QUndoGroup
         m_commandStack(new QUndoStack(undoGroup)),
         m_modified(false),
         m_projectFolder(projectFolder),
-        m_documentLoadingStep(0.0),
-        m_documentLoadingProgress(0),
         m_abortLoading(false)
 {
     m_clipManager = new ClipManager(this);
@@ -78,6 +77,8 @@ KdenliveDoc::KdenliveDoc(const KUrl &url, const KUrl &projectFolder, QUndoGroup
         else {
             QFile file(tmpFile);
             QString errorMsg;
+            QDomImplementation impl;
+            impl.setInvalidDataPolicy(QDomImplementation::DropInvalidChars);
             success = m_document.setContent(&file, false, &errorMsg);
             file.close();
             KIO::NetAccess::removeTempFile(tmpFile);
@@ -85,11 +86,13 @@ KdenliveDoc::KdenliveDoc(const KUrl &url, const KUrl &projectFolder, QUndoGroup
             if (!success) // It is corrupted
                 KMessageBox::error(parent, errorMsg);
             else {
+                parent->slotGotProgressInfo(i18n("Validating"), 0);
                 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 {
+                if (!success) {
+                    // It is not a project file
+                    parent->slotGotProgressInfo(i18n("File %1 is not a Kdenlive project file", m_url.path()), 0);
+                } else {
                     /*
                      * Validate the file against the current version (upgrade
                      * and recover it if needed). It is NOT a passive operation
@@ -97,6 +100,7 @@ KdenliveDoc::KdenliveDoc(const KUrl &url, const KUrl &projectFolder, QUndoGroup
                     // TODO: backup the document or alert the user?
                     success = validator.validate(DOCUMENTVERSION);
                     if (success) { // Let the validator handle error messages
+                        parent->slotGotProgressInfo(i18n("Loading"), 0);
                         QDomElement mlt = m_document.firstChildElement("mlt");
                         QDomElement infoXml = mlt.firstChildElement("kdenlivedoc");
 
@@ -130,29 +134,19 @@ KdenliveDoc::KdenliveDoc(const KUrl &url, const KUrl &projectFolder, QUndoGroup
                         }
                         QDomNodeList producers = m_document.elementsByTagName("producer");
                         QDomNodeList infoproducers = m_document.elementsByTagName("kdenlive_producer");
+                        parent->slotGotProgressInfo(i18n("Check missing clips"), 0);
                         if (checkDocumentClips(infoproducers) == false) m_abortLoading = true;
                         const int max = producers.count();
                         const int infomax = infoproducers.count();
 
-                        QDomNodeList folders = m_document.elementsByTagName("folder");
+                        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);
-                        }
-
-
                         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");
@@ -167,7 +161,6 @@ KdenliveDoc::KdenliveDoc(const KUrl &url, const KUrl &projectFolder, QUndoGroup
                                     }
                                 }
                                 addClipInfo(e, orig, prodId);
-                                kDebug() << "// KDENLIVE PRODUCER: " << prodId;
                             }
                         }
                         if (m_abortLoading) {
@@ -249,7 +242,7 @@ KdenliveDoc::~KdenliveDoc()
 
 int KdenliveDoc::setSceneList()
 {
-    m_render->resetProfile();
+    m_render->resetProfile(KdenliveSettings::current_profile());
     if (m_render->setSceneList(m_document.toString(), m_documentProperties.value("position").toInt()) == -1) {
         // INVALID MLT Consumer, something is wrong
         return -1;
@@ -257,7 +250,6 @@ int KdenliveDoc::setSceneList()
     m_documentProperties.remove("position");
     // m_document xml is now useless, clear it
     m_document.clear();
-    checkProjectClips();
     return 0;
 }
 
@@ -277,10 +269,16 @@ QDomDocument KdenliveDoc::createEmptyDocument(int videotracks, int audiotracks)
 
     m_tracksList.clear();
 
+    // Tracks are added «backwards», so we need to reverse the track numbering
+    // mbt 331: http://www.kdenlive.org/mantis/view.php?id=331
+    // Better default names for tracks: Audio 1 etc. instead of blank numbers
     for (int i = 0; i < audiotracks; i++) {
+        audioTrack.trackName = QString("Audio ") + QString::number(audiotracks - i);
         m_tracksList.append(audioTrack);
+
     }
     for (int i = 0; i < videotracks; i++) {
+        videoTrack.trackName = QString("Video ") + QString::number(videotracks - i);
         m_tracksList.append(videoTrack);
     }
     return createEmptyDocument(m_tracksList);
@@ -350,7 +348,7 @@ QDomDocument KdenliveDoc::createEmptyDocument(QList <TrackInfo> tracks)
 
     QDomElement blank0 = doc.createElement("entry");
     blank0.setAttribute("in", "0");
-    blank0.setAttribute("out", "0");
+    blank0.setAttribute("out", "1");
     blank0.setAttribute("producer", "black");
     playlist.appendChild(blank0);
 
@@ -367,13 +365,13 @@ QDomDocument KdenliveDoc::createEmptyDocument(QList <TrackInfo> tracks)
     track0.setAttribute("producer", "black_track");
     tractor.appendChild(track0);
 
-    // create audio tracks
+    // create audio and video tracks
     for (int i = 1; i < total; i++) {
         QDomElement track = doc.createElement("track");
         track.setAttribute("producer", "playlist" + QString::number(i));
-        if (tracks.at(i - 1).type == AUDIOTRACK)
+        if (tracks.at(i - 1).type == AUDIOTRACK) {
             track.setAttribute("hide", "video");
-        else if (tracks.at(i - 1).isBlind)
+        else if (tracks.at(i - 1).isBlind)
             track.setAttribute("hide", "video");
         if (tracks.at(i - 1).isMute)
             track.setAttribute("hide", "audio");
@@ -489,6 +487,11 @@ bool KdenliveDoc::saveSceneList(const QString &path, const QString &scene)
     QDomDocument sceneList;
     sceneList.setContent(scene, true);
     QDomElement mlt = sceneList.firstChildElement("mlt");
+    if (mlt.isNull() || !mlt.hasChildNodes()) {
+        //Make sure we don't save if scenelist is corrupted
+        KMessageBox::error(kapp->activeWindow(), i18n("Cannot write to file %1", path));
+        return false;
+    }
     QDomElement addedXml = sceneList.createElement("kdenlivedoc");
     mlt.appendChild(addedXml);
 
@@ -523,7 +526,7 @@ bool KdenliveDoc::saveSceneList(const QString &path, const QString &scene)
 
     // tracks info
     QDomElement tracksinfo = sceneList.createElement("tracksinfo");
-    foreach(const TrackInfo &info, m_tracksList) {
+    foreach(const TrackInfo & info, m_tracksList) {
         QDomElement trackinfo = sceneList.createElement("trackinfo");
         if (info.type == AUDIOTRACK) trackinfo.setAttribute("type", "audio");
         trackinfo.setAttribute("mute", info.isMute);
@@ -721,12 +724,6 @@ void KdenliveDoc::setThumbsProgress(const QString &message, int progress)
     emit progressInfo(message, progress);
 }
 
-void KdenliveDoc::loadingProgressed()
-{
-    m_documentLoadingProgress += m_documentLoadingStep;
-    emit progressInfo(QString(), (int) m_documentLoadingProgress);
-}
-
 QUndoStack *KdenliveDoc::commandStack()
 {
     return m_commandStack;
@@ -748,7 +745,6 @@ void KdenliveDoc::setRenderer(Render *render) {
 
 void KdenliveDoc::checkProjectClips()
 {
-    kDebug() << "+++++++++++++ + + + + CHK PCLIPS";
     if (m_render == NULL) return;
     m_clipManager->resetProducersList(m_render->producersList());
 }
@@ -887,10 +883,10 @@ void KdenliveDoc::addClip(QDomElement elem, QString clipId, bool createClipItem)
             int action = KMessageBox::No;
             if (!size.isEmpty() && !hash.isEmpty()) {
                 if (!m_searchFolder.isEmpty()) newpath = searchFileRecursively(m_searchFolder, size, hash);
-                else action = (KMessageBox::ButtonCode) KMessageBox::questionYesNoCancel(kapp->activeWindow(), i18n("Clip <b>%1</b><br>is invalid, what do you want to do?", path), i18n("File not found"), KGuiItem(i18n("Search automatically")), KGuiItem(i18n("Keep as placeholder")));
+                else action = (KMessageBox::ButtonCode) KMessageBox::questionYesNoCancel(kapp->activeWindow(), i18n("Clip <b>%1</b><br />is invalid, what do you want to do?", path), i18n("File not found"), KGuiItem(i18n("Search automatically")), KGuiItem(i18n("Keep as placeholder")));
             } else {
                 if (elem.attribute("type").toInt() == SLIDESHOW) {
-                    int res = KMessageBox::questionYesNoCancel(kapp->activeWindow(), i18n("Clip <b>%1</b><br>is invalid or missing, what do you want to do?", path), i18n("File not found"), KGuiItem(i18n("Search manually")), KGuiItem(i18n("Keep as placeholder")));
+                    int res = KMessageBox::questionYesNoCancel(kapp->activeWindow(), i18n("Clip <b>%1</b><br />is invalid or missing, what do you want to do?", path), i18n("File not found"), KGuiItem(i18n("Search manually")), KGuiItem(i18n("Keep as placeholder")));
                     if (res == KMessageBox::Yes)
                         newpath = KFileDialog::getExistingDirectory(KUrl("kfiledialog:///clipfolder"), kapp->activeWindow(), i18n("Looking for %1", path));
                     else {
@@ -898,7 +894,7 @@ void KdenliveDoc::addClip(QDomElement elem, QString clipId, bool createClipItem)
                         action = res;
                     }
                 } else {
-                    int res = KMessageBox::questionYesNoCancel(kapp->activeWindow(), i18n("Clip <b>%1</b><br>is invalid or missing, what do you want to do?", path), i18n("File not found"), KGuiItem(i18n("Search manually")), KGuiItem(i18n("Keep as placeholder")));
+                    int res = KMessageBox::questionYesNoCancel(kapp->activeWindow(), i18n("Clip <b>%1</b><br />is invalid or missing, what do you want to do?", path), i18n("File not found"), KGuiItem(i18n("Search manually")), KGuiItem(i18n("Keep as placeholder")));
                     if (res == KMessageBox::Yes)
                         newpath = KFileDialog::getOpenFileName(KUrl("kfiledialog:///clipfolder"), QString(), kapp->activeWindow(), i18n("Looking for %1", path));
                     else {
@@ -937,7 +933,6 @@ void KdenliveDoc::addClip(QDomElement elem, QString clipId, bool createClipItem)
     }
 }
 
-
 void KdenliveDoc::setNewClipResource(const QString &id, const QString &path)
 {
     QDomNodeList prods = m_document.elementsByTagName("producer");
@@ -972,7 +967,7 @@ QString KdenliveDoc::searchFileRecursively(const QDir &dir, const QString &match
                 * 1 MB = 1 second per 450 files (or faster)
                 * 10 MB = 9 seconds per 450 files (or faster)
                 */
-                if (file.size() > 1000000*2) {
+                if (file.size() > 1000000 * 2) {
                     fileData = file.read(1000000);
                     if (file.seek(file.size() - 1000000))
                         fileData.append(file.readAll());
@@ -1030,14 +1025,6 @@ void KdenliveDoc::addClipInfo(QDomElement elem, QDomElement orig, QString clipId
     }
 }
 
-void KdenliveDoc::deleteProjectClip(QStringList ids)
-{
-    for (int i = 0; i < ids.size(); ++i) {
-        emit deleteTimelineClip(ids.at(i));
-    }
-    m_clipManager->slotDeleteClips(ids);
-    setModified(true);
-}
 
 void KdenliveDoc::deleteClip(const QString &clipId)
 {
@@ -1047,7 +1034,7 @@ void KdenliveDoc::deleteClip(const QString &clipId)
 void KdenliveDoc::slotAddClipList(const KUrl::List urls, const QString group, const QString &groupId)
 {
     m_clipManager->slotAddClipList(urls, group, groupId);
-    emit selectLastAddedClip(QString::number(m_clipManager->lastClipId()));
+    //emit selectLastAddedClip(QString::number(m_clipManager->lastClipId()));
     setModified(true);
 }
 
@@ -1069,6 +1056,13 @@ DocClipBase *KdenliveDoc::getBaseClip(const QString &clipId)
     return m_clipManager->getClipById(clipId);
 }
 
+void KdenliveDoc::slotCreateXmlClip(const QString &name, const QDomElement xml, QString group, const QString &groupId)
+{
+    m_clipManager->slotAddXmlClipFile(name, xml, group, groupId);
+    setModified(true);
+    emit selectLastAddedClip(QString::number(m_clipManager->lastClipId()));
+}
+
 void KdenliveDoc::slotCreateColorClip(const QString &name, const QString &color, const QString &duration, QString group, const QString &groupId)
 {
     m_clipManager->slotAddColorClipFile(name, color, duration, group, groupId);
@@ -1089,7 +1083,7 @@ void KdenliveDoc::slotCreateTextClip(QString group, const QString &groupId, cons
     KStandardDirs::makeDir(titlesFolder);
     TitleWidget *dia_ui = new TitleWidget(templatePath, m_timecode, titlesFolder, m_render, kapp->activeWindow());
     if (dia_ui->exec() == QDialog::Accepted) {
-        m_clipManager->slotAddTextClipFile(i18n("Title clip"), dia_ui->duration(), dia_ui->xml().toString(), group, groupId);
+        m_clipManager->slotAddTextClipFile(i18n("Title clip"), dia_ui->outPoint(), dia_ui->xml().toString(), group, groupId);
         setModified(true);
         emit selectLastAddedClip(QString::number(m_clipManager->lastClipId()));
     }
@@ -1170,7 +1164,7 @@ QPoint KdenliveDoc::getTracksCount() const
 {
     int audio = 0;
     int video = 0;
-    foreach(const TrackInfo &info, m_tracksList) {
+    foreach(const TrackInfo & info, m_tracksList) {
         if (info.type == VIDEOTRACK) video++;
         else audio++;
     }
@@ -1195,36 +1189,39 @@ QString KdenliveDoc::getLadspaFile() const
 
 bool KdenliveDoc::checkDocumentClips(QDomNodeList infoproducers)
 {
-    int clipType;
-    QDomElement e;
-    QString id;
-    QString resource;
-    QList <QDomElement> missingClips;
-    for (int i = 0; i < infoproducers.count(); i++) {
-        e = infoproducers.item(i).toElement();
-        clipType = e.attribute("type").toInt();
-        if (clipType == COLOR) continue;
-        if (clipType == TEXT) {
-            //TODO: Check is clip template is missing (xmltemplate) or hash changed
-            continue;
-        }
-        id = e.attribute("id");
-        resource = e.attribute("resource");
-        if (clipType == SLIDESHOW) resource = KUrl(resource).directory();
-        if (!KIO::NetAccess::exists(KUrl(resource), KIO::NetAccess::SourceSide, 0)) {
-            // Missing clip found
-            missingClips.append(e);
-        } else {
-            // Check if the clip has changed
-            if (clipType != SLIDESHOW && e.hasAttribute("file_hash")) {
-                if (e.attribute("file_hash") != DocClipBase::getHash(e.attribute("resource")))
-                    e.removeAttribute("file_hash");
+    DocumentChecker d(infoproducers, m_document);
+    return (d.hasMissingClips() == false);
+
+    /*    int clipType;
+        QDomElement e;
+        QString id;
+        QString resource;
+        QList <QDomElement> missingClips;
+        for (int i = 0; i < infoproducers.count(); i++) {
+            e = infoproducers.item(i).toElement();
+            clipType = e.attribute("type").toInt();
+            if (clipType == COLOR) continue;
+            if (clipType == TEXT) {
+                //TODO: Check is clip template is missing (xmltemplate) or hash changed
+                continue;
+            }
+            id = e.attribute("id");
+            resource = e.attribute("resource");
+            if (clipType == SLIDESHOW) resource = KUrl(resource).directory();
+            if (!KIO::NetAccess::exists(KUrl(resource), KIO::NetAccess::SourceSide, 0)) {
+                // Missing clip found
+                missingClips.append(e);
+            } else {
+                // Check if the clip has changed
+                if (clipType != SLIDESHOW && e.hasAttribute("file_hash")) {
+                    if (e.attribute("file_hash") != DocClipBase::getHash(e.attribute("resource")))
+                        e.removeAttribute("file_hash");
+                }
             }
         }
-    }
-    if (missingClips.isEmpty()) return true;
-    DocumentChecker d(missingClips, m_document);
-    return (d.exec() == QDialog::Accepted);
+        if (missingClips.isEmpty()) return true;
+        DocumentChecker d(missingClips, m_document);
+        return (d.exec() == QDialog::Accepted);*/
 }
 
 void KdenliveDoc::setDocumentProperty(const QString &name, const QString &value)