documentvalidator.cpp 117 KB
Newer Older
Alberto Villa's avatar
Alberto Villa committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
/***************************************************************************
 *   Copyright (C) 2007 by Jean-Baptiste Mardelle (jb@kdenlive.org)        *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA          *
 ***************************************************************************/

Vincent Pinon's avatar
Vincent Pinon committed
20 21
#include "documentvalidator.h"

Alberto Villa's avatar
Alberto Villa committed
22
#include "definitions.h"
Vincent Pinon's avatar
Vincent Pinon committed
23
#include "effectslist/initeffects.h"
Till Theato's avatar
Till Theato committed
24
#include "mainwindow.h"
25
#include "core.h"
26
#include "mltcontroller/bincontroller.h"
Alberto Villa's avatar
Alberto Villa committed
27

Laurent Montel's avatar
Laurent Montel committed
28
#include "kdenlive_debug.h"
Alberto Villa's avatar
Alberto Villa committed
29
#include <KMessageBox>
30
#include <klocalizedstring.h>
Alberto Villa's avatar
Alberto Villa committed
31

32
#include <QFile>
Alberto Villa's avatar
Alberto Villa committed
33
#include <QColor>
34
#include <QString>
Till Theato's avatar
Till Theato committed
35 36
#include <QDir>
#include <QScriptEngine>
37

38 39
#include <mlt++/Mlt.h>

40
#include <locale>
41 42 43 44
#ifdef Q_OS_MAC
#include <xlocale.h>
#endif

45
#include <QStandardPaths>
46

47
DocumentValidator::DocumentValidator(const QDomDocument &doc, const QUrl &documentUrl):
Laurent Montel's avatar
Laurent Montel committed
48 49 50
    m_doc(doc),
    m_url(documentUrl),
    m_modified(false)
Alberto Villa's avatar
Alberto Villa committed
51 52 53 54
{}

bool DocumentValidator::validate(const double currentVersion)
{
55
    QDomElement mlt = m_doc.firstChildElement(QStringLiteral("mlt"));
56
    // At least the root element must be there
Laurent Montel's avatar
Laurent Montel committed
57
    if (mlt.isNull()) {
Alberto Villa's avatar
Alberto Villa committed
58
        return false;
Laurent Montel's avatar
Laurent Montel committed
59
    }
Alberto Villa's avatar
Alberto Villa committed
60

61 62 63
    QDomElement kdenliveDoc = mlt.firstChildElement(QStringLiteral("kdenlivedoc"));
    QString rootDir = mlt.attribute(QStringLiteral("root"));
    if (rootDir == QLatin1String("$CURRENTPATH")) {
64
        // The document was extracted from a Kdenlive archived project, fix root directory
65
        QString playlist = m_doc.toString();
66
        playlist.replace(QLatin1String("$CURRENTPATH"), m_url.adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash).toLocalFile());
67
        m_doc.setContent(playlist);
68 69
        mlt = m_doc.firstChildElement(QStringLiteral("mlt"));
        kdenliveDoc = mlt.firstChildElement(QStringLiteral("kdenlivedoc"));
70 71
    } else if (rootDir.isEmpty()) {
        mlt.setAttribute(QStringLiteral("root"), m_url.adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash).toLocalFile());
72
    }
73 74

    // Previous MLT / Kdenlive versions used C locale by default
75
    QLocale documentLocale = QLocale::c();
76

77
    if (mlt.hasAttribute(QStringLiteral("LC_NUMERIC"))) {
78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
        // Check document numeric separator (added in Kdenlive 16.12.1
        QDomElement main_playlist = mlt.firstChildElement(QStringLiteral("playlist"));
        QDomNodeList props = main_playlist.elementsByTagName(QStringLiteral("property"));
        QChar numericalSeparator;
        for (int i = 0; i < props.count(); ++i) {
            QDomNode n = props.at(i);
            if (n.toElement().attribute(QStringLiteral("name")) == QLatin1String("kdenlive:docproperties.decimalPoint")) {
                QString sep = n.firstChild().nodeValue();
                if (!sep.isEmpty()) {
                    numericalSeparator = sep.at(0);
                }
                break;
            }
        }
        bool error = false;
        if (!numericalSeparator.isNull() && numericalSeparator != QLocale().decimalPoint()) {
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
94
            qCDebug(KDENLIVE_LOG)<<" * ** LOCALE CHANGE REQUIRED: "<<numericalSeparator <<"!="<< QLocale().decimalPoint()<<" / "<<QLocale::system().decimalPoint();
95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117
            // Change locale to match document
            QString requestedLocale = mlt.attribute(QStringLiteral("LC_NUMERIC"));
            documentLocale = QLocale(requestedLocale);
#ifdef Q_OS_WIN
            // Most locales don't work on windows, so use C whenever possible
            if (numericalSeparator == QLatin1Char('.')) {
#else
            if (numericalSeparator != documentLocale.decimalPoint() && numericalSeparator == QLatin1Char('.')) {
#endif
                requestedLocale = QStringLiteral("C");
                documentLocale = QLocale::c();
            }
#ifdef Q_OS_MAC
            setlocale(LC_NUMERIC_MASK, requestedLocale.toUtf8().constData());
#elifdef Q_OS_WIN
            std::locale::global(std::locale(requestedLocale.toUtf8().constData()));
#else
            setlocale(LC_NUMERIC, requestedLocale.toUtf8().constData());
#endif
            if (numericalSeparator != documentLocale.decimalPoint()) {
                // Parse installed locales to find one matching
                const QList<QLocale> list = QLocale::matchingLocales(QLocale::AnyLanguage, QLocale().script(), QLocale::AnyCountry);
                QLocale matching;
118
                for (const QLocale &loc : list) {
119 120
                    if (loc.decimalPoint() == numericalSeparator) {
                        matching = loc;
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
121
                        qCDebug(KDENLIVE_LOG)<<"Warning, document locale: "<<mlt.attribute(QStringLiteral("LC_NUMERIC"))<<" is not available, using: "<<loc.name();
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
122
#ifndef Q_OS_MAC
123
                        setlocale(LC_NUMERIC, loc.name().toUtf8().constData());
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
124
#else
125
                        setlocale(LC_NUMERIC_MASK, loc.name().toUtf8().constData());
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
126
#endif
127 128 129 130 131 132 133 134
                        documentLocale = matching;
                        break;
                    }
                }
                error = numericalSeparator != documentLocale.decimalPoint();
            }
        } else if (numericalSeparator.isNull()) {
            // Change locale to match document
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
135
#ifndef Q_OS_MAC
136
            const QString newloc = QString::fromLatin1(setlocale(LC_NUMERIC, mlt.attribute(QStringLiteral("LC_NUMERIC")).toUtf8().constData()));
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
137
#else
138
            const QString newloc = setlocale(LC_NUMERIC_MASK, mlt.attribute("LC_NUMERIC").toUtf8().constData());
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
139
#endif
140 141 142 143 144 145 146
            documentLocale = QLocale(mlt.attribute(QStringLiteral("LC_NUMERIC")));
            error = newloc.isEmpty();
        } else {
            // Document separator matching system separator
            documentLocale = QLocale();
        }
        if (error) {
147
            // Requested locale not available, ask for install
Laurent Montel's avatar
Laurent Montel committed
148
            KMessageBox::sorry(QApplication::activeWindow(), i18n("The document was created in \"%1\" locale, which is not installed on your system. Please install that language pack. Until then, Kdenlive might not be able to correctly open the document.", mlt.attribute(QStringLiteral("LC_NUMERIC"))));
149
        }
Laurent Montel's avatar
Laurent Montel committed
150

151 152
        // Make sure Qt locale and C++ locale have the same numeric separator, might not be the case
        // With some locales since C++ and Qt use a different database for locales
153 154
        // localeconv()->decimal_point does not give reliable results on Windows
#ifndef Q_OS_WIN
155
        char *separator = localeconv()->decimal_point;
156
        if (QString::fromUtf8(separator) != QString(documentLocale.decimalPoint())) {
Laurent Montel's avatar
Laurent Montel committed
157
        KMessageBox::sorry(QApplication::activeWindow(), i18n("There is a locale conflict on your system. The document uses locale %1 which uses a \"%2\" as numeric separator (in system libraries) but Qt expects \"%3\". You might not be able to correctly open the project.", mlt.attribute(QStringLiteral("LC_NUMERIC")), documentLocale.decimalPoint(), separator));
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
158
            //qDebug()<<"------\n!!! system locale is not similar to Qt's locale... be prepared for bugs!!!\n------";
159
            // HACK: There is a locale conflict, so set locale to at least have correct decimal point
Laurent Montel's avatar
Laurent Montel committed
160 161 162 163 164
            if (strncmp(separator, ".", 1) == 0) {
                documentLocale = QLocale::c();
            } else if (strncmp(separator, ",", 1) == 0) {
                documentLocale = QLocale(QStringLiteral("fr_FR.UTF-8"));
            }
165
        }
166
#endif
167
    }
168
    documentLocale.setNumberOptions(QLocale::OmitGroupSeparator);
169 170
    if (documentLocale.decimalPoint() != QLocale().decimalPoint()) {
        // If loading an older MLT file without LC_NUMERIC, set locale to C which was previously the default
171
        if (!mlt.hasAttribute(QStringLiteral("LC_NUMERIC"))) {
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
172 173 174 175 176
#ifndef Q_OS_MAC
            setlocale(LC_NUMERIC, "C");
#else
            setlocale(LC_NUMERIC_MASK, "C");
#endif
177
	}
178
        QLocale::setDefault(documentLocale);
179 180 181
        if (documentLocale.decimalPoint() != QLocale().decimalPoint()) {
            KMessageBox::sorry(QApplication::activeWindow(), i18n("There is a locale conflict. The document uses a \"%1\" as numeric separator, but your computer is configured to use \"%2\". Change your computer settings or you might not be able to correctly open the project.", documentLocale.decimalPoint(), QLocale().decimalPoint()));
        }
182
        // locale conversion might need to be redone
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
183
#ifndef Q_OS_MAC
184
        initEffects::parseEffectFiles(pCore->getMltRepository(), QString::fromLatin1(setlocale(LC_NUMERIC, nullptr)));
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
185
#else
186
        initEffects::parseEffectFiles(pCore->getMltRepository(), QString::fromLatin1(setlocale(LC_NUMERIC_MASK, nullptr)));
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
187
#endif
188
    }
189
    double version = -1;
190
    if (kdenliveDoc.isNull() || !kdenliveDoc.hasAttribute(QStringLiteral("version"))) {
191
        // Newer Kdenlive document version
192 193
        QDomElement main = mlt.firstChildElement(QStringLiteral("playlist"));
        version = EffectsList::property(main, QStringLiteral("kdenlive:docproperties.version")).toDouble();
Laurent Montel's avatar
Laurent Montel committed
194
    } else {
195
        bool ok;
196
        version = documentLocale.toDouble(kdenliveDoc.attribute(QStringLiteral("version")), &ok);
197 198
        if (!ok) {
            // Could not parse version number, there is probably a conflict in decimal separator
199 200
            QLocale tempLocale = QLocale(mlt.attribute(QStringLiteral("LC_NUMERIC")));
            version = tempLocale.toDouble(kdenliveDoc.attribute(QStringLiteral("version")), &ok);
Laurent Montel's avatar
Laurent Montel committed
201 202 203
            if (!ok) {
                version = kdenliveDoc.attribute(QStringLiteral("version")).toDouble(&ok);
            }
204 205
            if (!ok) {
                // Last try: replace comma with a dot
206
                QString versionString = kdenliveDoc.attribute(QStringLiteral("version"));
Laurent Montel's avatar
Laurent Montel committed
207
                if (versionString.contains(QLatin1Char(','))) {
Laurent Montel's avatar
Laurent Montel committed
208
                    versionString.replace(QLatin1Char(','), QLatin1Char('.'));
Laurent Montel's avatar
Laurent Montel committed
209
                }
210
                version = versionString.toDouble(&ok);
Laurent Montel's avatar
Laurent Montel committed
211 212 213
                if (!ok) {
                    qCDebug(KDENLIVE_LOG) << "// CANNOT PARSE VERSION NUMBER, ERROR!";
                }
214 215
            }
        }
216
    }
Alberto Villa's avatar
Alberto Villa committed
217
    // Upgrade the document to the latest version
Laurent Montel's avatar
Laurent Montel committed
218
    if (!upgrade(version, currentVersion)) {
Alberto Villa's avatar
Alberto Villa committed
219
        return false;
Laurent Montel's avatar
Laurent Montel committed
220
    }
Alberto Villa's avatar
Alberto Villa committed
221

222 223
    checkOrphanedProducers();

224
    return true;
225
    /*
226 227
    // Check the syntax (this will be replaced by XSD validation with Qt 4.6)
    // and correct some errors
228
    {
Alberto Villa's avatar
Alberto Villa committed
229
        // Return (or create) the tractor
230 231
        QDomElement tractor = mlt.firstChildElement("tractor");
        if (tractor.isNull()) {
Alberto Villa's avatar
Alberto Villa committed
232
            m_modified = true;
233 234 235 236 237 238
            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);
Alberto Villa's avatar
Alberto Villa committed
239 240
        }

241 242 243
        // Make sure at least one track exists, and they're equal in number to
        // to the maximum between MLT and Kdenlive playlists and tracks
        //
244 245 246
        // In older Kdenlive project files, one playlist is not a real track (the black track), we have: track count = playlist count- 1
        // In newer Qt5 Kdenlive, the Bin playlist should not appear as a track. So we should have: track count = playlist count- 2
        int trackOffset = 1;
Alberto Villa's avatar
Alberto Villa committed
247
        QDomNodeList playlists = m_doc.elementsByTagName("playlist");
Laurent Montel's avatar
Laurent Montel committed
248 249 250 251 252 253
    // Remove "main bin" playlist that simply holds the bin's clips and is not a real playlist
    for (int i = 0; i < playlists.count(); ++i) {
        QString playlistId = playlists.at(i).toElement().attribute("id");
        if (playlistId == BinController::binPlaylistId()) {
        // remove pseudo-playlist
        //playlists.at(i).parentNode().removeChild(playlists.at(i));
254
                trackOffset = 2;
Laurent Montel's avatar
Laurent Montel committed
255 256 257 258
        break;
        }
    }

259
        int tracksMax = playlists.count() - trackOffset; // Remove the black track and bin track
260
        QDomNodeList tracks = tractor.elementsByTagName("track");
Alberto Villa's avatar
Alberto Villa committed
261
        tracksMax = qMax(tracks.count() - 1, tracksMax);
262
        QDomNodeList tracksinfo = kdenliveDoc.elementsByTagName("trackinfo");
Alberto Villa's avatar
Alberto Villa committed
263
        tracksMax = qMax(tracksinfo.count(), tracksMax);
Yuri Chornoivan's avatar
Yuri Chornoivan committed
264
        tracksMax = qMax(1, tracksMax); // Force existence of one track
265 266
        if (playlists.count() - trackOffset < tracksMax ||
                tracks.count() < tracksMax ||
Alberto Villa's avatar
Alberto Villa committed
267
                tracksinfo.count() < tracksMax) {
Laurent Montel's avatar
Laurent Montel committed
268
            qCDebug(KDENLIVE_LOG) << "//// WARNING, PROJECT IS CORRUPTED, MISSING TRACK";
Alberto Villa's avatar
Alberto Villa committed
269 270
            m_modified = true;
            int difference;
271 272 273 274 275
            // 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
Jean-Baptiste Mardelle's avatar
Cleanup  
Jean-Baptiste Mardelle committed
276
                    QDomNode tinfo = kdenliveDoc.firstChildElement("tracksinfo");
277 278 279 280 281 282
                    QDomNode tnode = tinfo.firstChild();
                    while (!tnode.isNull()) {
                        tinfo.removeChild(tnode);
                        tnode = tinfo.firstChild();
                    }

283
                    for (int i = 1; i < tracks.count(); ++i) {
284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301
                        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);
                    }
                }
            }

Alberto Villa's avatar
Alberto Villa committed
302 303 304 305
            if (playlists.count() - 1 < tracksMax) {
                difference = tracksMax - (playlists.count() - 1);
                for (int i = 0; i < difference; ++i) {
                    QDomElement playlist = m_doc.createElement("playlist");
306
                    mlt.appendChild(playlist);
Alberto Villa's avatar
Alberto Villa committed
307 308 309 310 311 312
                }
            }
            if (tracks.count() - 1 < tracksMax) {
                difference = tracksMax - (tracks.count() - 1);
                for (int i = 0; i < difference; ++i) {
                    QDomElement track = m_doc.createElement("track");
313
                    tractor.appendChild(track);
Alberto Villa's avatar
Alberto Villa committed
314 315 316
                }
            }
            if (tracksinfo.count() < tracksMax) {
317
                QDomElement tracksinfoElm = kdenliveDoc.firstChildElement("tracksinfo");
Alberto Villa's avatar
Alberto Villa committed
318 319
                if (tracksinfoElm.isNull()) {
                    tracksinfoElm = m_doc.createElement("tracksinfo");
320
                    kdenliveDoc.appendChild(tracksinfoElm);
Alberto Villa's avatar
Alberto Villa committed
321 322 323 324 325 326 327 328 329
                }
                difference = tracksMax - tracksinfo.count();
                for (int i = 0; i < difference; ++i) {
                    QDomElement trackinfo = m_doc.createElement("trackinfo");
                    trackinfo.setAttribute("mute", "0");
                    trackinfo.setAttribute("locked", "0");
                    tracksinfoElm.appendChild(trackinfo);
                }
            }
330
        }
Alberto Villa's avatar
Alberto Villa committed
331 332 333 334
        // TODO: check the tracks references
        // TODO: check internal mix transitions
    }

Till Theato's avatar
Till Theato committed
335 336
    updateEffects();

Alberto Villa's avatar
Alberto Villa committed
337
    return true;
338
    */
Alberto Villa's avatar
Alberto Villa committed
339 340 341 342
}

bool DocumentValidator::upgrade(double version, const double currentVersion)
{
Laurent Montel's avatar
Laurent Montel committed
343
    qCDebug(KDENLIVE_LOG) << "Opening a document with version " << version << " / " << currentVersion;
Alberto Villa's avatar
Alberto Villa committed
344 345

    // No conversion needed
346
    if (version == currentVersion) {
347
        return true;
348
    }
Alberto Villa's avatar
Alberto Villa committed
349 350 351

    // The document is too new
    if (version > currentVersion) {
Laurent Montel's avatar
Laurent Montel committed
352
        //qCDebug(KDENLIVE_LOG) << "Unable to open document with version " << version;
353
        KMessageBox::sorry(QApplication::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"));
Alberto Villa's avatar
Alberto Villa committed
354 355 356 357 358
        return false;
    }

    // Unsupported document versions
    if (version == 0.5 || version == 0.7) {
Laurent Montel's avatar
Laurent Montel committed
359
        //qCDebug(KDENLIVE_LOG) << "Unable to open document with version " << version;
360
        KMessageBox::sorry(QApplication::activeWindow(), i18n("This project type is unsupported (version %1) and can't be loaded.", version), i18n("Unable to open project"));
Alberto Villa's avatar
Alberto Villa committed
361 362
        return false;
    }
363

364
    // <kdenlivedoc />
365 366
    QDomNode infoXmlNode;
    QDomElement infoXml;
367
    QDomNodeList docs = m_doc.elementsByTagName(QStringLiteral("kdenlivedoc"));
368
    if (!docs.isEmpty()) {
369
        infoXmlNode = m_doc.elementsByTagName(QStringLiteral("kdenlivedoc")).at(0);
370
        infoXml = infoXmlNode.toElement();
371
        infoXml.setAttribute(QStringLiteral("upgraded"), QStringLiteral("1"));
372
    }
373
    m_doc.documentElement().setAttribute(QStringLiteral("upgraded"), QStringLiteral("1"));
374

Alberto Villa's avatar
Alberto Villa committed
375 376
    if (version <= 0.6) {
        QDomElement infoXml_old = infoXmlNode.cloneNode(true).toElement(); // Needed for folders
377 378 379 380
        QDomNode westley = m_doc.elementsByTagName(QStringLiteral("westley")).at(1);
        QDomNode tractor = m_doc.elementsByTagName(QStringLiteral("tractor")).at(0);
        QDomNode multitrack = m_doc.elementsByTagName(QStringLiteral("multitrack")).at(0);
        QDomNodeList playlists = m_doc.elementsByTagName(QStringLiteral("playlist"));
Alberto Villa's avatar
Alberto Villa committed
381

382 383 384
        QDomNode props = m_doc.elementsByTagName(QStringLiteral("properties")).at(0).toElement();
        QString profile = props.toElement().attribute(QStringLiteral("videoprofile"));
        int startPos = props.toElement().attribute(QStringLiteral("timeline_position")).toInt();
Laurent Montel's avatar
Laurent Montel committed
385
        if (profile == QLatin1String("dv_wide")) {
386
            profile = QStringLiteral("dv_pal_wide");
Laurent Montel's avatar
Laurent Montel committed
387
        }
Alberto Villa's avatar
Alberto Villa committed
388 389 390 391

        // move playlists outside of tractor and add the tracks instead
        int max = playlists.count();
        if (westley.isNull()) {
392
            westley = m_doc.createElement(QStringLiteral("westley"));
Alberto Villa's avatar
Alberto Villa committed
393 394 395
            m_doc.documentElement().appendChild(westley);
        }
        if (tractor.isNull()) {
Laurent Montel's avatar
Laurent Montel committed
396
            //qCDebug(KDENLIVE_LOG) << "// NO MLT PLAYLIST, building empty one";
397
            QDomElement blank_tractor = m_doc.createElement(QStringLiteral("tractor"));
Alberto Villa's avatar
Alberto Villa committed
398
            westley.appendChild(blank_tractor);
399 400
            QDomElement blank_playlist = m_doc.createElement(QStringLiteral("playlist"));
            blank_playlist.setAttribute(QStringLiteral("id"), QStringLiteral("black_track"));
Alberto Villa's avatar
Alberto Villa committed
401
            westley.insertBefore(blank_playlist, QDomNode());
402 403
            QDomElement blank_track = m_doc.createElement(QStringLiteral("track"));
            blank_track.setAttribute(QStringLiteral("producer"), QStringLiteral("black_track"));
Alberto Villa's avatar
Alberto Villa committed
404 405
            blank_tractor.appendChild(blank_track);

406
            QDomNodeList kdenlivetracks = m_doc.elementsByTagName(QStringLiteral("kdenlivetrack"));
407
            for (int i = 0; i < kdenlivetracks.count(); ++i) {
408
                blank_playlist = m_doc.createElement(QStringLiteral("playlist"));
Laurent Montel's avatar
Laurent Montel committed
409
                blank_playlist.setAttribute(QStringLiteral("id"), QStringLiteral("playlist") + QString::number(i));
Alberto Villa's avatar
Alberto Villa committed
410
                westley.insertBefore(blank_playlist, QDomNode());
411
                blank_track = m_doc.createElement(QStringLiteral("track"));
Laurent Montel's avatar
Laurent Montel committed
412
                blank_track.setAttribute(QStringLiteral("producer"), QStringLiteral("playlist") + QString::number(i));
Alberto Villa's avatar
Alberto Villa committed
413
                blank_tractor.appendChild(blank_track);
414 415 416
                if (kdenlivetracks.at(i).toElement().attribute(QStringLiteral("cliptype")) == QLatin1String("Sound")) {
                    blank_playlist.setAttribute(QStringLiteral("hide"), QStringLiteral("video"));
                    blank_track.setAttribute(QStringLiteral("hide"), QStringLiteral("video"));
Alberto Villa's avatar
Alberto Villa committed
417 418
                }
            }
419
        } else for (int i = 0; i < max; ++i) {
Alberto Villa's avatar
Alberto Villa committed
420 421 422
                QDomNode n = playlists.at(i);
                westley.insertBefore(n, QDomNode());
                QDomElement pl = n.toElement();
423 424
                QDomElement track = m_doc.createElement(QStringLiteral("track"));
                QString trackType = pl.attribute(QStringLiteral("hide"));
Laurent Montel's avatar
Laurent Montel committed
425
                if (!trackType.isEmpty()) {
426
                    track.setAttribute(QStringLiteral("hide"), trackType);
Laurent Montel's avatar
Laurent Montel committed
427
                }
428
                QString playlist_id =  pl.attribute(QStringLiteral("id"));
Alberto Villa's avatar
Alberto Villa committed
429
                if (playlist_id.isEmpty()) {
430 431
                    playlist_id = QStringLiteral("black_track");
                    pl.setAttribute(QStringLiteral("id"), playlist_id);
Alberto Villa's avatar
Alberto Villa committed
432
                }
433
                track.setAttribute(QStringLiteral("producer"), playlist_id);
Alberto Villa's avatar
Alberto Villa committed
434
                //tractor.appendChild(track);
Alberto Villa's avatar
Alberto Villa committed
435 436
#define KEEP_TRACK_ORDER 1
#ifdef KEEP_TRACK_ORDER
Alberto Villa's avatar
Alberto Villa committed
437
                tractor.insertAfter(track, QDomNode());
Alberto Villa's avatar
Alberto Villa committed
438
#else
Alberto Villa's avatar
Alberto Villa committed
439 440 441 442 443 444 445 446 447 448 449 450 451 452 453
                // 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());
Alberto Villa's avatar
Alberto Villa committed
454 455
                                inserted = true;
                                break;
Alberto Villa's avatar
Alberto Villa committed
456
                            } else {
Laurent Montel's avatar
Laurent Montel committed
457
                                //qCDebug(KDENLIVE_LOG) << "playlist_id: " << playlist_id << " producer:" << track_elem.attribute("producer");
Alberto Villa's avatar
Alberto Villa committed
458 459 460 461 462
                                if (playlist_id < track_elem.attribute("producer")) {
                                    tractor.insertBefore(track, track_elem);
                                    inserted = true;
                                    break;
                                }
Alberto Villa's avatar
Alberto Villa committed
463 464
                            }
                        }
Alberto Villa's avatar
Alberto Villa committed
465 466 467 468
                        // Reach here, no insertion, insert last
                        if (!inserted) {
                            tractor.insertAfter(track, QDomNode());
                        }
Alberto Villa's avatar
Alberto Villa committed
469
                    }
Alberto Villa's avatar
Alberto Villa committed
470
                } else {
Laurent Montel's avatar
Laurent Montel committed
471
                    qCWarning(KDENLIVE_LOG) << "tractor was not a QDomElement";
Alberto Villa's avatar
Alberto Villa committed
472
                    tractor.insertAfter(track, QDomNode());
Alberto Villa's avatar
Alberto Villa committed
473 474
                }
#endif
Alberto Villa's avatar
Alberto Villa committed
475
            }
Alberto Villa's avatar
Alberto Villa committed
476 477 478
        tractor.removeChild(multitrack);

        // audio track mixing transitions should not be added to track view, so add required attribute
479
        QDomNodeList transitions = m_doc.elementsByTagName(QStringLiteral("transition"));
Alberto Villa's avatar
Alberto Villa committed
480
        max = transitions.count();
481
        for (int i = 0; i < max; ++i) {
Alberto Villa's avatar
Alberto Villa committed
482
            QDomElement tr = transitions.at(i).toElement();
483 484 485 486
            if (tr.attribute(QStringLiteral("combine")) == QLatin1String("1") && tr.attribute(QStringLiteral("mlt_service")) == QLatin1String("mix")) {
                QDomElement property = m_doc.createElement(QStringLiteral("property"));
                property.setAttribute(QStringLiteral("name"), QStringLiteral("internal_added"));
                QDomText value = m_doc.createTextNode(QStringLiteral("237"));
Alberto Villa's avatar
Alberto Villa committed
487 488
                property.appendChild(value);
                tr.appendChild(property);
489 490 491
                property = m_doc.createElement(QStringLiteral("property"));
                property.setAttribute(QStringLiteral("name"), QStringLiteral("mlt_service"));
                value = m_doc.createTextNode(QStringLiteral("mix"));
Alberto Villa's avatar
Alberto Villa committed
492 493 494 495 496
                property.appendChild(value);
                tr.appendChild(property);
            } else {
                // convert transition
                QDomNamedNodeMap attrs = tr.attributes();
497
                for (int j = 0; j < attrs.count(); ++j) {
Alberto Villa's avatar
Alberto Villa committed
498
                    QString attrName = attrs.item(j).nodeName();
499 500 501
                    if (attrName != QLatin1String("in") && attrName != QLatin1String("out") && attrName != QLatin1String("id")) {
                        QDomElement property = m_doc.createElement(QStringLiteral("property"));
                        property.setAttribute(QStringLiteral("name"), attrName);
Alberto Villa's avatar
Alberto Villa committed
502 503 504 505 506 507 508 509 510
                        QDomText value = m_doc.createTextNode(attrs.item(j).nodeValue());
                        property.appendChild(value);
                        tr.appendChild(property);
                    }
                }
            }
        }

        // move transitions after tracks
511
        for (int i = 0; i < max; ++i) {
Alberto Villa's avatar
Alberto Villa committed
512 513 514 515
            tractor.insertAfter(transitions.at(0), QDomNode());
        }

        // Fix filters format
516
        QDomNodeList entries = m_doc.elementsByTagName(QStringLiteral("entry"));
Alberto Villa's avatar
Alberto Villa committed
517
        max = entries.count();
518
        for (int i = 0; i < max; ++i) {
Alberto Villa's avatar
Alberto Villa committed
519 520 521 522
            QString last_id;
            int effectix = 0;
            QDomNode m = entries.at(i).firstChild();
            while (!m.isNull()) {
523
                if (m.toElement().tagName() == QLatin1String("filter")) {
Alberto Villa's avatar
Alberto Villa committed
524 525
                    QDomElement filt = m.toElement();
                    QDomNamedNodeMap attrs = filt.attributes();
526
                    QString current_id = filt.attribute(QStringLiteral("kdenlive_id"));
Alberto Villa's avatar
Alberto Villa committed
527 528 529 530
                    if (current_id != last_id) {
                        effectix++;
                        last_id = current_id;
                    }
531 532
                    QDomElement e = m_doc.createElement(QStringLiteral("property"));
                    e.setAttribute(QStringLiteral("name"), QStringLiteral("kdenlive_ix"));
Alberto Villa's avatar
Alberto Villa committed
533 534 535
                    QDomText value = m_doc.createTextNode(QString::number(effectix));
                    e.appendChild(value);
                    filt.appendChild(e);
536
                    for (int j = 0; j < attrs.count(); ++j) {
Alberto Villa's avatar
Alberto Villa committed
537 538
                        QDomAttr a = attrs.item(j).toAttr();
                        if (!a.isNull()) {
Laurent Montel's avatar
Laurent Montel committed
539
                            //qCDebug(KDENLIVE_LOG) << " FILTER; adding :" << a.name() << ':' << a.value();
540 541 542 543 544
                            auto property = m_doc.createElement(QStringLiteral("property"));
                            property.setAttribute(QStringLiteral("name"), a.name());
                            auto property_value = m_doc.createTextNode(a.value());
                            property.appendChild(property_value);
                            filt.appendChild(property);
Alberto Villa's avatar
Alberto Villa committed
545 546 547 548 549 550 551 552 553 554 555 556 557

                        }
                    }
                }
                m = m.nextSibling();
            }
        }

        /*
            QDomNodeList filters = m_doc.elementsByTagName("filter");
            max = filters.count();
            QString last_id;
            int effectix = 0;
558
            for (int i = 0; i < max; ++i) {
Alberto Villa's avatar
Alberto Villa committed
559 560 561 562 563 564 565 566 567 568 569 570
                QDomElement filt = filters.at(i).toElement();
                QDomNamedNodeMap attrs = filt.attributes();
        QString current_id = filt.attribute("kdenlive_id");
        if (current_id != last_id) {
            effectix++;
            last_id = current_id;
        }
        QDomElement e = m_doc.createElement("property");
                e.setAttribute("name", "kdenlive_ix");
                QDomText value = m_doc.createTextNode(QString::number(1));
                e.appendChild(value);
                filt.appendChild(e);
571
                for (int j = 0; j < attrs.count(); ++j) {
Alberto Villa's avatar
Alberto Villa committed
572 573
                    QDomAttr a = attrs.item(j).toAttr();
                    if (!a.isNull()) {
Laurent Montel's avatar
Laurent Montel committed
574
                        //qCDebug(KDENLIVE_LOG) << " FILTER; adding :" << a.name() << ':' << a.value();
Alberto Villa's avatar
Alberto Villa committed
575 576 577 578 579 580 581 582 583 584
                        QDomElement e = m_doc.createElement("property");
                        e.setAttribute("name", a.name());
                        QDomText value = m_doc.createTextNode(a.value());
                        e.appendChild(value);
                        filt.appendChild(e);
                    }
                }
            }*/

        // fix slowmotion
585
        QDomNodeList producers = westley.toElement().elementsByTagName(QStringLiteral("producer"));
Alberto Villa's avatar
Alberto Villa committed
586
        max = producers.count();
587
        for (int i = 0; i < max; ++i) {
Alberto Villa's avatar
Alberto Villa committed
588
            QDomElement prod = producers.at(i).toElement();
589 590
            if (prod.attribute(QStringLiteral("mlt_service")) == QLatin1String("framebuffer")) {
                QString slowmotionprod = prod.attribute(QStringLiteral("resource"));
Laurent Montel's avatar
Laurent Montel committed
591
                slowmotionprod.replace(QLatin1Char(':'), QLatin1Char('?'));
Laurent Montel's avatar
Laurent Montel committed
592
                //qCDebug(KDENLIVE_LOG) << "// FOUND WRONG SLOWMO, new: " << slowmotionprod;
593
                prod.setAttribute(QStringLiteral("resource"), slowmotionprod);
Alberto Villa's avatar
Alberto Villa committed
594 595 596
            }
        }
        // move producers to correct place, markers to a global list, fix clip descriptions
597
        QDomElement markers = m_doc.createElement(QStringLiteral("markers"));
Alberto Villa's avatar
Alberto Villa committed
598
        // This will get the xml producers:
599
        producers = m_doc.elementsByTagName(QStringLiteral("producer"));
Alberto Villa's avatar
Alberto Villa committed
600
        max = producers.count();
601
        for (int i = 0; i < max; ++i) {
Alberto Villa's avatar
Alberto Villa committed
602 603 604 605 606 607 608 609 610 611 612 613
            QDomElement prod = producers.at(0).toElement();
            // add resource also as a property (to allow path correction in setNewResource())
            // TODO: will it work with slowmotion? needs testing
            /*if (!prod.attribute("resource").isEmpty()) {
                QDomElement prop_resource = m_doc.createElement("property");
                prop_resource.setAttribute("name", "resource");
                QDomText resource = m_doc.createTextNode(prod.attribute("resource"));
                prop_resource.appendChild(resource);
                prod.appendChild(prop_resource);
            }*/
            QDomNode m = prod.firstChild();
            if (!m.isNull()) {
614
                if (m.toElement().tagName() == QLatin1String("markers")) {
Alberto Villa's avatar
Alberto Villa committed
615 616
                    QDomNodeList prodchilds = m.childNodes();
                    int maxchild = prodchilds.count();
617
                    for (int k = 0; k < maxchild; ++k) {
Alberto Villa's avatar
Alberto Villa committed
618
                        QDomElement mark = prodchilds.at(0).toElement();
619
                        mark.setAttribute(QStringLiteral("id"), prod.attribute(QStringLiteral("id")));
Alberto Villa's avatar
Alberto Villa committed
620 621 622
                        markers.insertAfter(mark, QDomNode());
                    }
                    prod.removeChild(m);
623
                } else if (prod.attribute(QStringLiteral("type")).toInt() == Text) {
Alberto Villa's avatar
Alberto Villa committed
624
                    // convert title clip
625
                    if (m.toElement().tagName() == QLatin1String("textclip")) {
Alberto Villa's avatar
Alberto Villa committed
626 627
                        QDomDocument tdoc;
                        QDomElement titleclip = m.toElement();
628
                        QDomElement title = tdoc.createElement(QStringLiteral("kdenlivetitle"));
Alberto Villa's avatar
Alberto Villa committed
629 630 631
                        tdoc.appendChild(title);
                        QDomNodeList objects = titleclip.childNodes();
                        int maxchild = objects.count();
632
                        for (int k = 0; k < maxchild; ++k) {
Alberto Villa's avatar
Alberto Villa committed
633
                            QDomElement ob = objects.at(k).toElement();
634
                            if (ob.attribute(QStringLiteral("type")) == QLatin1String("3")) {
Alberto Villa's avatar
Alberto Villa committed
635
                                // text object - all of this goes into "xmldata"...
636 637 638 639 640 641 642 643 644 645 646 647 648
                                QDomElement item = tdoc.createElement(QStringLiteral("item"));
                                item.setAttribute(QStringLiteral("z-index"), ob.attribute(QStringLiteral("z")));
                                item.setAttribute(QStringLiteral("type"), QStringLiteral("QGraphicsTextItem"));
                                QDomElement position = tdoc.createElement(QStringLiteral("position"));
                                position.setAttribute(QStringLiteral("x"), ob.attribute(QStringLiteral("x")));
                                position.setAttribute(QStringLiteral("y"), ob.attribute(QStringLiteral("y")));
                                QDomElement content = tdoc.createElement(QStringLiteral("content"));
                                content.setAttribute(QStringLiteral("font"), ob.attribute(QStringLiteral("font_family")));
                                content.setAttribute(QStringLiteral("font-size"), ob.attribute(QStringLiteral("font_size")));
                                content.setAttribute(QStringLiteral("font-bold"), ob.attribute(QStringLiteral("bold")));
                                content.setAttribute(QStringLiteral("font-italic"), ob.attribute(QStringLiteral("italic")));
                                content.setAttribute(QStringLiteral("font-underline"), ob.attribute(QStringLiteral("underline")));
                                QString col = ob.attribute(QStringLiteral("color"));
Alberto Villa's avatar
Alberto Villa committed
649
                                QColor c(col);
650
                                content.setAttribute(QStringLiteral("font-color"), colorToString(c));
Alberto Villa's avatar
Alberto Villa committed
651 652 653
                                // todo: These fields are missing from the newly generated xmldata:
                                // transform, startviewport, endviewport, background

654
                                QDomText conttxt = tdoc.createTextNode(ob.attribute(QStringLiteral("text")));
Alberto Villa's avatar
Alberto Villa committed
655 656 657 658
                                content.appendChild(conttxt);
                                item.appendChild(position);
                                item.appendChild(content);
                                title.appendChild(item);
659
                            } else if (ob.attribute(QStringLiteral("type")) == QLatin1String("5")) {
Alberto Villa's avatar
Alberto Villa committed
660
                                // rectangle object
661 662 663 664 665 666 667 668
                                QDomElement item = tdoc.createElement(QStringLiteral("item"));
                                item.setAttribute(QStringLiteral("z-index"), ob.attribute(QStringLiteral("z")));
                                item.setAttribute(QStringLiteral("type"), QStringLiteral("QGraphicsRectItem"));
                                QDomElement position = tdoc.createElement(QStringLiteral("position"));
                                position.setAttribute(QStringLiteral("x"), ob.attribute(QStringLiteral("x")));
                                position.setAttribute(QStringLiteral("y"), ob.attribute(QStringLiteral("y")));
                                QDomElement content = tdoc.createElement(QStringLiteral("content"));
                                QString col = ob.attribute(QStringLiteral("color"));
Alberto Villa's avatar
Alberto Villa committed
669
                                QColor c(col);
670 671 672
                                content.setAttribute(QStringLiteral("brushcolor"), colorToString(c));
                                QString rect = QStringLiteral("0,0,");
                                rect.append(ob.attribute(QStringLiteral("width")));
Laurent Montel's avatar
Laurent Montel committed
673
                                rect.append(QLatin1String(","));
674 675
                                rect.append(ob.attribute(QStringLiteral("height")));
                                content.setAttribute(QStringLiteral("rect"), rect);
Alberto Villa's avatar
Alberto Villa committed
676 677 678 679 680
                                item.appendChild(position);
                                item.appendChild(content);
                                title.appendChild(item);
                            }
                        }
681
                        prod.setAttribute(QStringLiteral("xmldata"), tdoc.toString());
Alberto Villa's avatar
Alberto Villa committed
682 683 684 685
                        // 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));
Laurent Montel's avatar
Laurent Montel committed
686
                        ////qCDebug(KDENLIVE_LOG)<<"TITLE DATA:\n"<<tdoc.toString();
Alberto Villa's avatar
Alberto Villa committed
687 688 689 690 691 692
                        prod.removeChild(m);
                    } // End conversion of title clips.

                } else if (m.isText()) {
                    QString comment = m.nodeValue();
                    if (!comment.isEmpty()) {
693
                        prod.setAttribute(QStringLiteral("description"), comment);
Alberto Villa's avatar
Alberto Villa committed
694 695 696 697
                    }
                    prod.removeChild(m);
                }
            }
698
            int duration = prod.attribute(QStringLiteral("duration")).toInt();
Laurent Montel's avatar
Laurent Montel committed
699 700 701
            if (duration > 0) {
                prod.setAttribute(QStringLiteral("out"), QString::number(duration));
            }
Alberto Villa's avatar
Alberto Villa committed
702 703 704 705
            // The clip goes back in, but text clips should not go back in, at least not modified
            westley.insertBefore(prod, QDomNode());
        }

706
        QDomNode westley0 = m_doc.elementsByTagName(QStringLiteral("westley")).at(0);
Laurent Montel's avatar
Laurent Montel committed
707 708 709
        if (!markers.firstChild().isNull()) {
            westley0.appendChild(markers);
        }
Alberto Villa's avatar
Alberto Villa committed
710 711 712 713 714 715 716 717 718

        /*
         * 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(infoXmlNode);
719 720 721
        QDomElement infoXml_new = m_doc.createElement(QStringLiteral("kdenlivedoc"));
        infoXml_new.setAttribute(QStringLiteral("profile"), profile);
        infoXml.setAttribute(QStringLiteral("position"), startPos);
Alberto Villa's avatar
Alberto Villa committed
722 723 724 725

        // Add all the producers that has a resource in westley
        QDomElement westley_element = westley0.toElement();
        if (westley_element.isNull()) {
Laurent Montel's avatar
Laurent Montel committed
726
            qCWarning(KDENLIVE_LOG) << "westley0 element in document was not a QDomElement - unable to add producers to new kdenlivedoc";
Alberto Villa's avatar
Alberto Villa committed
727
        } else {
728
            QDomNodeList wproducers = westley_element.elementsByTagName(QStringLiteral("producer"));
Alberto Villa's avatar
Alberto Villa committed
729
            int kmax = wproducers.count();
730
            for (int i = 0; i < kmax; ++i) {
Alberto Villa's avatar
Alberto Villa committed
731 732
                QDomElement wproducer = wproducers.at(i).toElement();
                if (wproducer.isNull()) {
Laurent Montel's avatar
Laurent Montel committed
733
                    qCWarning(KDENLIVE_LOG) << "Found producer in westley0, that was not a QDomElement";
Alberto Villa's avatar
Alberto Villa committed
734 735
                    continue;
                }
Laurent Montel's avatar
Laurent Montel committed
736 737 738
                if (wproducer.attribute(QStringLiteral("id")) == QLatin1String("black")) {
                    continue;
                }
Alberto Villa's avatar
Alberto Villa committed
739
                // We have to do slightly different things, depending on the type
Laurent Montel's avatar
Laurent Montel committed
740
                //qCDebug(KDENLIVE_LOG) << "Converting producer element with type" << wproducer.attribute("type");
741
                if (wproducer.attribute(QStringLiteral("type")).toInt() == Text) {
Laurent Montel's avatar
Laurent Montel committed
742
                    //qCDebug(KDENLIVE_LOG) << "Found TEXT element in producer" << endl;
Alberto Villa's avatar
Alberto Villa committed
743
                    QDomElement kproducer = wproducer.cloneNode(true).toElement();
744
                    kproducer.setTagName(QStringLiteral("kdenlive_producer"));
Alberto Villa's avatar
Alberto Villa committed
745 746 747 748 749 750 751
                    infoXml_new.appendChild(kproducer);
                    /*
                     * TODO: Perhaps needs some more changes here to
                     * "frequency", aspect ratio as a float, frame_size,
                     * channels, and later, resource and title name
                     */
                } else {
752 753
                    QDomElement kproducer = m_doc.createElement(QStringLiteral("kdenlive_producer"));
                    kproducer.setAttribute(QStringLiteral("id"), wproducer.attribute(QStringLiteral("id")));
Laurent Montel's avatar
Laurent Montel committed
754
                    if (!wproducer.attribute(QStringLiteral("description")).isEmpty()) {
755
                        kproducer.setAttribute(QStringLiteral("description"), wproducer.attribute(QStringLiteral("description")));
Laurent Montel's avatar
Laurent Montel committed
756
                    }
757 758
                    kproducer.setAttribute(QStringLiteral("resource"), wproducer.attribute(QStringLiteral("resource")));
                    kproducer.setAttribute(QStringLiteral("type"), wproducer.attribute(QStringLiteral("type")));
Alberto Villa's avatar
Alberto Villa committed
759
                    // Testing fix for 358
760 761
                    if (!wproducer.attribute(QStringLiteral("aspect_ratio")).isEmpty()) {
                        kproducer.setAttribute(QStringLiteral("aspect_ratio"), wproducer.attribute(QStringLiteral("aspect_ratio")));
Alberto Villa's avatar
Alberto Villa committed
762
                    }
763 764
                    if (!wproducer.attribute(QStringLiteral("source_fps")).isEmpty()) {
                        kproducer.setAttribute(QStringLiteral("fps"), wproducer.attribute(QStringLiteral("source_fps")));
Alberto Villa's avatar
Alberto Villa committed
765
                    }
766 767
                    if (!wproducer.attribute(QStringLiteral("length")).isEmpty()) {
                        kproducer.setAttribute(QStringLiteral("duration"), wproducer.attribute(QStringLiteral("length")));
Alberto Villa's avatar
Alberto Villa committed
768 769 770
                    }
                    infoXml_new.appendChild(kproducer);
                }
771 772
                if (wproducer.attribute(QStringLiteral("id")).toInt() > max_kproducer_id) {
                    max_kproducer_id = wproducer.attribute(QStringLiteral("id")).toInt();
Alberto Villa's avatar
Alberto Villa committed
773 774 775 776 777 778 779 780 781 782 783 784 785 786
                }
            }
        }
#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 folder shows up missing, and clips with no folder does not
         * show up.
         */
        //QDomElement infoXml_old = infoXmlNode.toElement();
        if (!infoXml_old.isNull()) {
787
            QDomNodeList folders = infoXml_old.elementsByTagName(QStringLiteral("folder"));
Alberto Villa's avatar
Alberto Villa committed
788 789 790 791 792
            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()) {
793
                    QString groupName = folder.attribute(QStringLiteral("name"));
Laurent Montel's avatar
Laurent Montel committed
794
                    //qCDebug(KDENLIVE_LOG) << "groupName: " << groupName << " with groupId: " << groupId;
795
                    QDomNodeList fproducers = folder.elementsByTagName(QStringLiteral("producer"));
Alberto Villa's avatar
Alberto Villa committed
796 797 798 799
                    int psize = fproducers.size();
                    for (int j = 0; j < psize; ++j) {
                        QDomElement fproducer = fproducers.at(j).toElement();
                        if (!fproducer.isNull()) {
800
                            QString id = fproducer.attribute(QStringLiteral("id"));
Alberto Villa's avatar
Alberto Villa committed
801
                            // This is not very effective, but compared to loading the clips, its a breeze
802
                            QDomNodeList kdenlive_producers = infoXml_new.elementsByTagName(QStringLiteral("kdenlive_producer"));
Alberto Villa's avatar
Alberto Villa committed
803 804 805
                            int kpsize = kdenlive_producers.size();
                            for (int k = 0; k < kpsize; ++k) {
                                QDomElement kproducer = kdenlive_producers.at(k).toElement(); // Its an element for sure
806
                                if (id == kproducer.attribute(QStringLiteral("id"))) {
Alberto Villa's avatar
Alberto Villa committed
807
                                    // We do not check that it already is part of a folder
808 809
                                    kproducer.setAttribute(QStringLiteral("groupid"), groupId);
                                    kproducer.setAttribute(QStringLiteral("groupname"), groupName);
Alberto Villa's avatar
Alberto Villa committed
810 811 812 813 814 815 816 817 818 819 820 821
                                    break;
                                }
                            }
                        }
                    }
                    ++groupId;
                }
            }
        }
#endif
        QDomNodeList elements = westley.childNodes();
        max = elements.count();
822
        for (int i = 0; i < max; ++i) {
Alberto Villa's avatar
Alberto Villa committed
823 824 825 826 827 828 829 830 831
            QDomElement prod = elements.at(0).toElement();
            westley0.insertAfter(prod, QDomNode());
        }

        westley0.appendChild(infoXml_new);

        westley0.removeChild(westley);

        // adds <avfile /> information to <kdenlive_producer />
832 833
        QDomNodeList kproducers = m_doc.elementsByTagName(QStringLiteral("kdenlive_producer"));
        QDomNodeList avfiles = infoXml_old.elementsByTagName(QStringLiteral("avfile"));
Laurent Montel's avatar
Laurent Montel committed
834
        //qCDebug(KDENLIVE_LOG) << "found" << avfiles.count() << "<avfile />s and" << kproducers.count() << "<kdenlive_producer />s";
Alberto Villa's avatar
Alberto Villa committed
835 836 837
        for (int i = 0; i < avfiles.count(); ++i) {
            QDomElement avfile = avfiles.at(i).toElement();
            QDomElement kproducer;
Laurent Montel's avatar
Laurent Montel committed
838
            if (avfile.isNull()) {
Laurent Montel's avatar
Laurent Montel committed
839
                qCWarning(KDENLIVE_LOG) << "found an <avfile /> that is not a QDomElement";
Laurent Montel's avatar
Laurent Montel committed
840
            } else {
841
                QString id = avfile.attribute(QStringLiteral("id"));
Alberto Villa's avatar
Alberto Villa committed
842 843
                // this is horrible, must be rewritten, it's just for test
                for (int j = 0; j < kproducers.count(); ++j) {
Laurent Montel's avatar
Laurent Montel committed
844
                    ////qCDebug(KDENLIVE_LOG) << "checking <kdenlive_producer /> with id" << kproducers.at(j).toElement().attribute("id");
845
                    if (kproducers.at(j).toElement().attribute(QStringLiteral("id")) == id) {
Alberto Villa's avatar
Alberto Villa committed
846 847 848 849
                        kproducer = kproducers.at(j).toElement();
                        break;
                    }
                }
Laurent Montel's avatar
Laurent Montel committed
850
                if (kproducer == QDomElement()) {
Laurent Montel's avatar
Laurent Montel committed
851
                    qCWarning(KDENLIVE_LOG) << "no match for <avfile /> with id =" << id;
Laurent Montel's avatar
Laurent Montel committed
852
                } else {
Laurent Montel's avatar
Laurent Montel committed
853
                    ////qCDebug(KDENLIVE_LOG) << "ready to set additional <avfile />'s attributes (id =" << id << ')';
854 855
                    kproducer.setAttribute(QStringLiteral("channels"), avfile.attribute(QStringLiteral("channels")));
                    kproducer.setAttribute(QStringLiteral("duration"), avfile.attribute(QStringLiteral("duration")));
Laurent Montel's avatar
Laurent Montel committed
856
                    kproducer.setAttribute(QStringLiteral("frame_size"), avfile.attribute(QStringLiteral("width")) + QLatin1Char('x') + avfile.attribute(QStringLiteral("height")));
857
                    kproducer.setAttribute(QStringLiteral("frequency"), avfile.attribute(QStringLiteral("frequency")));
Laurent Montel's avatar
Laurent Montel committed
858
                    if (kproducer.attribute(QStringLiteral("description")).isEmpty() && !avfile.attribute(QStringLiteral("description")).isEmpty()) {
859
                        kproducer.setAttribute(QStringLiteral("description"), avfile.attribute(QStringLiteral("description")));
Laurent Montel's avatar
Laurent Montel committed
860
                    }
Alberto Villa's avatar
Alberto Villa committed
861 862 863 864 865 866 867 868
                }
            }
        }
        infoXml = infoXml_new;
    }

    if (version <= 0.81) {
        // Add the tracks information
869
        QString tracksOrder = infoXml.attribute(QStringLiteral("tracks"));
Alberto Villa's avatar
Alberto Villa committed
870
        if (tracksOrder.isEmpty()) {
871
            QDomNodeList tracks = m_doc.elementsByTagName(QStringLiteral("track"));
872
            for (int i = 0; i < tracks.count(); ++i) {
Alberto Villa's avatar
Alberto Villa committed
873
                QDomElement track = tracks.at(i).toElement();
874
                if (track.attribute(QStringLiteral("producer")) != QLatin1String("black_track")) {
Laurent Montel's avatar
Laurent Montel committed
875
                    if (track.attribute(QStringLiteral("hide")) == QLatin1String("video")) {
Laurent Montel's avatar
Laurent Montel committed
876
                        tracksOrder.append(QLatin1Char('a'));
Laurent Montel's avatar
Laurent Montel committed
877
                    } else {
Laurent Montel's avatar
Laurent Montel committed
878
                        tracksOrder.append(QLatin1Char('v'));
Laurent Montel's avatar
Laurent Montel committed
879
                    }
Alberto Villa's avatar
Alberto Villa committed
880 881 882
                }
            }
        }
883
        QDomElement tracksinfo = m_doc.createElement(QStringLiteral("tracksinfo"));
884
        for (int i = 0; i < tracksOrder.size(); ++i) {
885
            QDomElement trackinfo = m_doc.createElement(QStringLiteral("trackinfo"));
Laurent Montel's avatar
Laurent Montel committed
886
            if (tracksOrder.data()[i] == QLatin1Char('a')) {
887 888
                trackinfo.setAttribute(QStringLiteral("type"), QStringLiteral("audio"));
                trackinfo.setAttribute(QStringLiteral("blind"), true);
Laurent Montel's avatar
Laurent Montel committed
889
            } else {
890
                trackinfo.setAttribute(QStringLiteral("blind"), false);
Laurent Montel's avatar
Laurent Montel committed
891
            }
892 893
            trackinfo.setAttribute(QStringLiteral("mute"), false);
            trackinfo.setAttribute(QStringLiteral("locked"), false);
Alberto Villa's avatar
Alberto Villa committed
894 895 896 897 898 899 900
            tracksinfo.appendChild(trackinfo);
        }
        infoXml.appendChild(tracksinfo);
    }

    if (version <= 0.82) {
        // Convert <westley />s in <mlt />s (MLT extreme makeover)
901
        QDomNodeList westleyNodes = m_doc.elementsByTagName(QStringLiteral("westley"));
902
        for (int i = 0; i < westleyNodes.count(); ++i) {
Alberto Villa's avatar
Alberto Villa committed
903
            QDomElement westley = westleyNodes.at(i).toElement();
904
            westley.setTagName(QStringLiteral("mlt"));
Alberto Villa's avatar
Alberto Villa committed
905 906 907
        }
    }

908 909
    if (version <= 0.83) {
        // Replace point size with pixel size in text titles
910
        if (m_doc.toString().contains(QStringLiteral("font-size"))) {
911
            KMessageBox::ButtonCode convert = KMessageBox::Continue;
912
            QDomNodeList kproducerNodes = m_doc.elementsByTagName(QStringLiteral("kdenlive_producer"));
913
            for (int i = 0; i < kproducerNodes.count() && convert != KMessageBox::No; ++i) {
914
                QDomElement kproducer = kproducerNodes.at(i).toElement();
915
                if (kproducer.attribute(QStringLiteral("type")).toInt() == Text) {
916
                    QDomDocument data;
917
                    data.setContent(kproducer.attribute(QStringLiteral("xmldata")));
918
                    QDomNodeList items = data.firstChild().childNodes();
919
                    for (int j = 0; j < items.count() && convert != KMessageBox::No; ++j) {
920 921 922
                        if (items.at(j).attributes().namedItem(QStringLiteral("type")).nodeValue() == QLatin1String("QGraphicsTextItem")) {
                            QDomNamedNodeMap textProperties = items.at(j).namedItem(QStringLiteral("content")).attributes();
                            if (textProperties.namedItem(QStringLiteral("font-pixel-size")).isNull() && !textProperties.namedItem(QStringLiteral("font-size")).isNull()) {
923
                                // Ask the user if he wants to convert
Laurent Montel's avatar
Laurent Montel committed
924
                                if (convert != KMessageBox::Yes && convert != KMessageBox::No) {
925
                                    convert = (KMessageBox::ButtonCode)KMessageBox::warningYesNo(QApplication::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"));
Laurent Montel's avatar
Laurent Montel committed
926
                                }
927 928
                                if (convert == KMessageBox::Yes) {
                                    QFont font;
929 930 931 932 933
                                    font.setPointSize(textProperties.namedItem(QStringLiteral("font-size")).nodeValue().toInt());
                                    QDomElement content = items.at(j).namedItem(QStringLiteral("content")).toElement();
                                    content.setAttribute(QStringLiteral("font-pixel-size"), QFontInfo(font).pixelSize());
                                    content.removeAttribute(QStringLiteral("font-size"));
                                    kproducer.setAttribute(QStringLiteral("xmldata"), data.toString());
934 935 936 937 938 939
                                    /*
                                     * 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)
                                     */
                                }
940 941 942 943 944 945