]> git.sesse.net Git - kdenlive/blob - src/documentvalidator.cpp
clean up reindentation
[kdenlive] / src / documentvalidator.cpp
1 /***************************************************************************
2  *   Copyright (C) 2007 by Jean-Baptiste Mardelle (jb@kdenlive.org)        *
3  *                                                                         *
4  *   This program is free software; you can redistribute it and/or modify  *
5  *   it under the terms of the GNU General Public License as published by  *
6  *   the Free Software Foundation; either version 2 of the License, or     *
7  *   (at your option) any later version.                                   *
8  *                                                                         *
9  *   This program is distributed in the hope that it will be useful,       *
10  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
11  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
12  *   GNU General Public License for more details.                          *
13  *                                                                         *
14  *   You should have received a copy of the GNU General Public License     *
15  *   along with this program; if not, write to the                         *
16  *   Free Software Foundation, Inc.,                                       *
17  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA          *
18  ***************************************************************************/
19
20
21 #include "documentvalidator.h"
22 #include "definitions.h"
23
24 #include <KDebug>
25 #include <KMessageBox>
26 #include <KApplication>
27 #include <KLocale>
28
29 #include <QFile>
30 #include <QColor>
31
32 DocumentValidator::DocumentValidator(QDomDocument doc):
33         m_doc(doc),
34         m_modified(false)
35 {}
36
37 bool DocumentValidator::validate(const double currentVersion)
38 {
39     // Check if we're validating a Kdenlive project
40     if (!isProject())
41         return false;
42
43     // Upgrade the document to the latest version
44     QDomNode kdenlivedocNode = m_doc.elementsByTagName("kdenlivedoc").at(0);
45     QDomElement kdenlivedocElm = kdenlivedocNode.toElement();
46     if (!upgrade(kdenlivedocElm.attribute("version").toDouble(), currentVersion))
47         return false;
48
49     /*
50      * Check the syntax (this will be replaced by XSD validation with Qt 4.6)
51      * and correct some errors
52      */
53     QDomNode mltNode = m_doc.elementsByTagName("mlt").at(0);
54     QDomElement mltElm = mltNode.toElement();
55     if (mltElm.isNull()) // At least the root element must be there
56         return false;
57     else {
58         // Return (or create) the tractor
59         QDomNode tractorNode = m_doc.elementsByTagName("tractor").at(0);
60         QDomElement tractorElm = tractorNode.toElement();
61         if (tractorElm.isNull()) {
62             m_modified = true;
63             tractorElm = m_doc.createElement("tractor");
64             tractorElm.setAttribute("global_feed", "1");
65             tractorElm.setAttribute("in", "0");
66             tractorElm.setAttribute("out", "-1");
67             tractorElm.setAttribute("id", "maintractor");
68             mltElm.appendChild(tractorElm);
69         }
70
71         /*
72          * Make sure at least one track exists, and they're equal in number to
73          * to the maximum between MLT and Kdenlive playlists and tracks
74          */
75         QDomNodeList playlists = m_doc.elementsByTagName("playlist");
76         int tracksMax = playlists.count() - 1; // Remove the black track
77         QDomNodeList tracks = m_doc.elementsByTagName("track");
78         tracksMax = qMax(tracks.count() - 1, tracksMax);
79         QDomNodeList tracksinfo = m_doc.elementsByTagName("trackinfo");
80         tracksMax = qMax(tracksinfo.count(), tracksMax);
81         tracksMax = qMax(1, tracksMax); // Force existance of one track
82         if (playlists.count() - 1 < tracksMax ||
83                 tracks.count() - 1 < tracksMax ||
84                 tracksinfo.count() < tracksMax) {
85             m_modified = true;
86             int difference;
87             if (playlists.count() - 1 < tracksMax) {
88                 difference = tracksMax - (playlists.count() - 1);
89                 for (int i = 0; i < difference; ++i) {
90                     QDomElement playlist = m_doc.createElement("playlist");
91                     mltElm.appendChild(playlist);
92                 }
93             }
94             if (tracks.count() - 1 < tracksMax) {
95                 difference = tracksMax - (tracks.count() - 1);
96                 for (int i = 0; i < difference; ++i) {
97                     QDomElement track = m_doc.createElement("track");
98                     tractorElm.appendChild(track);
99                 }
100             }
101             if (tracksinfo.count() < tracksMax) {
102                 QDomNode tracksinfoNode = m_doc.elementsByTagName("tracksinfo").at(0);
103                 QDomElement tracksinfoElm = tracksinfoNode.toElement();
104                 if (tracksinfoElm.isNull()) {
105                     tracksinfoElm = m_doc.createElement("tracksinfo");
106                     kdenlivedocElm.appendChild(tracksinfoElm);
107                 }
108                 difference = tracksMax - tracksinfo.count();
109                 for (int i = 0; i < difference; ++i) {
110                     QDomElement trackinfo = m_doc.createElement("trackinfo");
111                     trackinfo.setAttribute("mute", "0");
112                     trackinfo.setAttribute("locked", "0");
113                     tracksinfoElm.appendChild(trackinfo);
114                 }
115             }
116         }
117
118         // TODO: check the tracks references
119         // TODO: check internal mix transitions
120     }
121
122     return true;
123 }
124
125 bool DocumentValidator::upgrade(double version, const double currentVersion)
126 {
127     kDebug() << "Opening a document with version " << version;
128
129     // No conversion needed
130     if (version == currentVersion) {
131         if (!m_doc.toString().contains("font-size")) // TODO: remove when currentVersion == 0.84
132             return true;
133     }
134
135     // The document is too new
136     if (version > currentVersion) {
137         kDebug() << "Unable to open document with version " << version;
138         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"));
139         return false;
140     }
141
142     // Unsupported document versions
143     if (version == 0.5 || version == 0.7) {
144         kDebug() << "Unable to open document with version " << version;
145         KMessageBox::sorry(kapp->activeWindow(), i18n("This project type is unsupported (version %1) and can't be loaded.", version), i18n("Unable to open project"));
146         return false;
147     }
148
149     // <kdenlivedoc />
150     QDomNode infoXmlNode = m_doc.elementsByTagName("kdenlivedoc").at(0);
151     QDomElement infoXml = infoXmlNode.toElement();
152
153     if (version <= 0.6) {
154         QDomElement infoXml_old = infoXmlNode.cloneNode(true).toElement(); // Needed for folders
155         QDomNode westley = m_doc.elementsByTagName("westley").at(1);
156         QDomNode tractor = m_doc.elementsByTagName("tractor").at(0);
157         QDomNode multitrack = m_doc.elementsByTagName("multitrack").at(0);
158         QDomNodeList playlists = m_doc.elementsByTagName("playlist");
159
160         QDomNode props = m_doc.elementsByTagName("properties").at(0).toElement();
161         QString profile = props.toElement().attribute("videoprofile");
162         int startPos = props.toElement().attribute("timeline_position").toInt();
163         if (profile == "dv_wide")
164             profile = "dv_pal_wide";
165
166         // move playlists outside of tractor and add the tracks instead
167         int max = playlists.count();
168         if (westley.isNull()) {
169             westley = m_doc.createElement("westley");
170             m_doc.documentElement().appendChild(westley);
171         }
172         if (tractor.isNull()) {
173             kDebug() << "// NO MLT PLAYLIST, building empty one";
174             QDomElement blank_tractor = m_doc.createElement("tractor");
175             westley.appendChild(blank_tractor);
176             QDomElement blank_playlist = m_doc.createElement("playlist");
177             blank_playlist.setAttribute("id", "black_track");
178             westley.insertBefore(blank_playlist, QDomNode());
179             QDomElement blank_track = m_doc.createElement("track");
180             blank_track.setAttribute("producer", "black_track");
181             blank_tractor.appendChild(blank_track);
182
183             QDomNodeList kdenlivetracks = m_doc.elementsByTagName("kdenlivetrack");
184             for (int i = 0; i < kdenlivetracks.count(); i++) {
185                 blank_playlist = m_doc.createElement("playlist");
186                 blank_playlist.setAttribute("id", "playlist" + QString::number(i));
187                 westley.insertBefore(blank_playlist, QDomNode());
188                 blank_track = m_doc.createElement("track");
189                 blank_track.setAttribute("producer", "playlist" + QString::number(i));
190                 blank_tractor.appendChild(blank_track);
191                 if (kdenlivetracks.at(i).toElement().attribute("cliptype") == "Sound") {
192                     blank_playlist.setAttribute("hide", "video");
193                     blank_track.setAttribute("hide", "video");
194                 }
195             }
196         } else for (int i = 0; i < max; i++) {
197                 QDomNode n = playlists.at(i);
198                 westley.insertBefore(n, QDomNode());
199                 QDomElement pl = n.toElement();
200                 QDomElement track = m_doc.createElement("track");
201                 QString trackType = pl.attribute("hide");
202                 if (!trackType.isEmpty())
203                     track.setAttribute("hide", trackType);
204                 QString playlist_id =  pl.attribute("id");
205                 if (playlist_id.isEmpty()) {
206                     playlist_id = "black_track";
207                     pl.setAttribute("id", playlist_id);
208                 }
209                 track.setAttribute("producer", playlist_id);
210                 //tractor.appendChild(track);
211 #define KEEP_TRACK_ORDER 1
212 #ifdef KEEP_TRACK_ORDER
213                 tractor.insertAfter(track, QDomNode());
214 #else
215                 // Insert the new track in an order that hopefully matches the 3 video, then 2 audio tracks of Kdenlive 0.7.0
216                 // insertion sort - O( tracks*tracks )
217                 // Note, this breaks _all_ transitions - but you can move them up and down afterwards.
218                 QDomElement tractor_elem = tractor.toElement();
219                 if (! tractor_elem.isNull()) {
220                     QDomNodeList tracks = tractor_elem.elementsByTagName("track");
221                     int size = tracks.size();
222                     if (size == 0) {
223                         tractor.insertAfter(track, QDomNode());
224                     } else {
225                         bool inserted = false;
226                         for (int i = 0; i < size; ++i) {
227                             QDomElement track_elem = tracks.at(i).toElement();
228                             if (track_elem.isNull()) {
229                                 tractor.insertAfter(track, QDomNode());
230                                 inserted = true;
231                                 break;
232                             } else {
233                                 kDebug() << "playlist_id: " << playlist_id << " producer:" << track_elem.attribute("producer");
234                                 if (playlist_id < track_elem.attribute("producer")) {
235                                     tractor.insertBefore(track, track_elem);
236                                     inserted = true;
237                                     break;
238                                 }
239                             }
240                         }
241                         // Reach here, no insertion, insert last
242                         if (!inserted) {
243                             tractor.insertAfter(track, QDomNode());
244                         }
245                     }
246                 } else {
247                     kWarning() << "tractor was not a QDomElement";
248                     tractor.insertAfter(track, QDomNode());
249                 }
250 #endif
251             }
252         tractor.removeChild(multitrack);
253
254         // audio track mixing transitions should not be added to track view, so add required attribute
255         QDomNodeList transitions = m_doc.elementsByTagName("transition");
256         max = transitions.count();
257         for (int i = 0; i < max; i++) {
258             QDomElement tr = transitions.at(i).toElement();
259             if (tr.attribute("combine") == "1" && tr.attribute("mlt_service") == "mix") {
260                 QDomElement property = m_doc.createElement("property");
261                 property.setAttribute("name", "internal_added");
262                 QDomText value = m_doc.createTextNode("237");
263                 property.appendChild(value);
264                 tr.appendChild(property);
265                 property = m_doc.createElement("property");
266                 property.setAttribute("name", "mlt_service");
267                 value = m_doc.createTextNode("mix");
268                 property.appendChild(value);
269                 tr.appendChild(property);
270             } else {
271                 // convert transition
272                 QDomNamedNodeMap attrs = tr.attributes();
273                 for (int j = 0; j < attrs.count(); j++) {
274                     QString attrName = attrs.item(j).nodeName();
275                     if (attrName != "in" && attrName != "out" && attrName != "id") {
276                         QDomElement property = m_doc.createElement("property");
277                         property.setAttribute("name", attrName);
278                         QDomText value = m_doc.createTextNode(attrs.item(j).nodeValue());
279                         property.appendChild(value);
280                         tr.appendChild(property);
281                     }
282                 }
283             }
284         }
285
286         // move transitions after tracks
287         for (int i = 0; i < max; i++) {
288             tractor.insertAfter(transitions.at(0), QDomNode());
289         }
290
291         // Fix filters format
292         QDomNodeList entries = m_doc.elementsByTagName("entry");
293         max = entries.count();
294         for (int i = 0; i < max; i++) {
295             QString last_id;
296             int effectix = 0;
297             QDomNode m = entries.at(i).firstChild();
298             while (!m.isNull()) {
299                 if (m.toElement().tagName() == "filter") {
300                     QDomElement filt = m.toElement();
301                     QDomNamedNodeMap attrs = filt.attributes();
302                     QString current_id = filt.attribute("kdenlive_id");
303                     if (current_id != last_id) {
304                         effectix++;
305                         last_id = current_id;
306                     }
307                     QDomElement e = m_doc.createElement("property");
308                     e.setAttribute("name", "kdenlive_ix");
309                     QDomText value = m_doc.createTextNode(QString::number(effectix));
310                     e.appendChild(value);
311                     filt.appendChild(e);
312                     for (int j = 0; j < attrs.count(); j++) {
313                         QDomAttr a = attrs.item(j).toAttr();
314                         if (!a.isNull()) {
315                             kDebug() << " FILTER; adding :" << a.name() << ":" << a.value();
316                             QDomElement e = m_doc.createElement("property");
317                             e.setAttribute("name", a.name());
318                             QDomText value = m_doc.createTextNode(a.value());
319                             e.appendChild(value);
320                             filt.appendChild(e);
321
322                         }
323                     }
324                 }
325                 m = m.nextSibling();
326             }
327         }
328
329         /*
330             QDomNodeList filters = m_doc.elementsByTagName("filter");
331             max = filters.count();
332             QString last_id;
333             int effectix = 0;
334             for (int i = 0; i < max; i++) {
335                 QDomElement filt = filters.at(i).toElement();
336                 QDomNamedNodeMap attrs = filt.attributes();
337         QString current_id = filt.attribute("kdenlive_id");
338         if (current_id != last_id) {
339             effectix++;
340             last_id = current_id;
341         }
342         QDomElement e = m_doc.createElement("property");
343                 e.setAttribute("name", "kdenlive_ix");
344                 QDomText value = m_doc.createTextNode(QString::number(1));
345                 e.appendChild(value);
346                 filt.appendChild(e);
347                 for (int j = 0; j < attrs.count(); j++) {
348                     QDomAttr a = attrs.item(j).toAttr();
349                     if (!a.isNull()) {
350                         kDebug() << " FILTER; adding :" << a.name() << ":" << a.value();
351                         QDomElement e = m_doc.createElement("property");
352                         e.setAttribute("name", a.name());
353                         QDomText value = m_doc.createTextNode(a.value());
354                         e.appendChild(value);
355                         filt.appendChild(e);
356                     }
357                 }
358             }*/
359
360         // fix slowmotion
361         QDomNodeList producers = westley.toElement().elementsByTagName("producer");
362         max = producers.count();
363         for (int i = 0; i < max; i++) {
364             QDomElement prod = producers.at(i).toElement();
365             if (prod.attribute("mlt_service") == "framebuffer") {
366                 QString slowmotionprod = prod.attribute("resource");
367                 slowmotionprod.replace(':', '?');
368                 kDebug() << "// FOUND WRONG SLOWMO, new: " << slowmotionprod;
369                 prod.setAttribute("resource", slowmotionprod);
370             }
371         }
372         // move producers to correct place, markers to a global list, fix clip descriptions
373         QDomElement markers = m_doc.createElement("markers");
374         // This will get the xml producers:
375         producers = m_doc.elementsByTagName("producer");
376         max = producers.count();
377         for (int i = 0; i < max; i++) {
378             QDomElement prod = producers.at(0).toElement();
379             // add resource also as a property (to allow path correction in setNewResource())
380             // TODO: will it work with slowmotion? needs testing
381             /*if (!prod.attribute("resource").isEmpty()) {
382                 QDomElement prop_resource = m_doc.createElement("property");
383                 prop_resource.setAttribute("name", "resource");
384                 QDomText resource = m_doc.createTextNode(prod.attribute("resource"));
385                 prop_resource.appendChild(resource);
386                 prod.appendChild(prop_resource);
387             }*/
388             QDomNode m = prod.firstChild();
389             if (!m.isNull()) {
390                 if (m.toElement().tagName() == "markers") {
391                     QDomNodeList prodchilds = m.childNodes();
392                     int maxchild = prodchilds.count();
393                     for (int k = 0; k < maxchild; k++) {
394                         QDomElement mark = prodchilds.at(0).toElement();
395                         mark.setAttribute("id", prod.attribute("id"));
396                         markers.insertAfter(mark, QDomNode());
397                     }
398                     prod.removeChild(m);
399                 } else if (prod.attribute("type").toInt() == TEXT) {
400                     // convert title clip
401                     if (m.toElement().tagName() == "textclip") {
402                         QDomDocument tdoc;
403                         QDomElement titleclip = m.toElement();
404                         QDomElement title = tdoc.createElement("kdenlivetitle");
405                         tdoc.appendChild(title);
406                         QDomNodeList objects = titleclip.childNodes();
407                         int maxchild = objects.count();
408                         for (int k = 0; k < maxchild; k++) {
409                             QString objectxml;
410                             QDomElement ob = objects.at(k).toElement();
411                             if (ob.attribute("type") == "3") {
412                                 // text object - all of this goes into "xmldata"...
413                                 QDomElement item = tdoc.createElement("item");
414                                 item.setAttribute("z-index", ob.attribute("z"));
415                                 item.setAttribute("type", "QGraphicsTextItem");
416                                 QDomElement position = tdoc.createElement("position");
417                                 position.setAttribute("x", ob.attribute("x"));
418                                 position.setAttribute("y", ob.attribute("y"));
419                                 QDomElement content = tdoc.createElement("content");
420                                 content.setAttribute("font", ob.attribute("font_family"));
421                                 content.setAttribute("font-size", ob.attribute("font_size"));
422                                 content.setAttribute("font-bold", ob.attribute("bold"));
423                                 content.setAttribute("font-italic", ob.attribute("italic"));
424                                 content.setAttribute("font-underline", ob.attribute("underline"));
425                                 QString col = ob.attribute("color");
426                                 QColor c(col);
427                                 content.setAttribute("font-color", colorToString(c));
428                                 // todo: These fields are missing from the newly generated xmldata:
429                                 // transform, startviewport, endviewport, background
430
431                                 QDomText conttxt = tdoc.createTextNode(ob.attribute("text"));
432                                 content.appendChild(conttxt);
433                                 item.appendChild(position);
434                                 item.appendChild(content);
435                                 title.appendChild(item);
436                             } else if (ob.attribute("type") == "5") {
437                                 // rectangle object
438                                 QDomElement item = tdoc.createElement("item");
439                                 item.setAttribute("z-index", ob.attribute("z"));
440                                 item.setAttribute("type", "QGraphicsRectItem");
441                                 QDomElement position = tdoc.createElement("position");
442                                 position.setAttribute("x", ob.attribute("x"));
443                                 position.setAttribute("y", ob.attribute("y"));
444                                 QDomElement content = tdoc.createElement("content");
445                                 QString col = ob.attribute("color");
446                                 QColor c(col);
447                                 content.setAttribute("brushcolor", colorToString(c));
448                                 QString rect = "0,0,";
449                                 rect.append(ob.attribute("width"));
450                                 rect.append(",");
451                                 rect.append(ob.attribute("height"));
452                                 content.setAttribute("rect", rect);
453                                 item.appendChild(position);
454                                 item.appendChild(content);
455                                 title.appendChild(item);
456                             }
457                         }
458                         prod.setAttribute("xmldata", tdoc.toString());
459                         // mbd todo: This clearly does not work, as every title gets the same name - trying to leave it empty
460                         // QStringList titleInfo = TitleWidget::getFreeTitleInfo(projectFolder());
461                         // prod.setAttribute("titlename", titleInfo.at(0));
462                         // prod.setAttribute("resource", titleInfo.at(1));
463                         //kDebug()<<"TITLE DATA:\n"<<tdoc.toString();
464                         prod.removeChild(m);
465                     } // End conversion of title clips.
466
467                 } else if (m.isText()) {
468                     QString comment = m.nodeValue();
469                     if (!comment.isEmpty()) {
470                         prod.setAttribute("description", comment);
471                     }
472                     prod.removeChild(m);
473                 }
474             }
475             int duration = prod.attribute("duration").toInt();
476             if (duration > 0) prod.setAttribute("out", QString::number(duration));
477             // The clip goes back in, but text clips should not go back in, at least not modified
478             westley.insertBefore(prod, QDomNode());
479         }
480
481         QDomNode westley0 = m_doc.elementsByTagName("westley").at(0);
482         if (!markers.firstChild().isNull()) westley0.appendChild(markers);
483
484         /*
485          * Convert as much of the kdenlivedoc as possible. Use the producer in
486          * westley. First, remove the old stuff from westley, and add a new
487          * empty one. Also, track the max id in order to use it for the adding
488          * of groups/folders
489          */
490         int max_kproducer_id = 0;
491         westley0.removeChild(infoXmlNode);
492         QDomElement infoXml_new = m_doc.createElement("kdenlivedoc");
493         infoXml_new.setAttribute("profile", profile);
494         infoXml.setAttribute("position", startPos);
495
496         // Add all the producers that has a resource in westley
497         QDomElement westley_element = westley0.toElement();
498         if (westley_element.isNull()) {
499             kWarning() << "westley0 element in document was not a QDomElement - unable to add producers to new kdenlivedoc";
500         } else {
501             QDomNodeList wproducers = westley_element.elementsByTagName("producer");
502             int kmax = wproducers.count();
503             for (int i = 0; i < kmax; i++) {
504                 QDomElement wproducer = wproducers.at(i).toElement();
505                 if (wproducer.isNull()) {
506                     kWarning() << "Found producer in westley0, that was not a QDomElement";
507                     continue;
508                 }
509                 if (wproducer.attribute("id") == "black") continue;
510                 // We have to do slightly different things, depending on the type
511                 kDebug() << "Converting producer element with type" << wproducer.attribute("type");
512                 if (wproducer.attribute("type").toInt() == TEXT) {
513                     kDebug() << "Found TEXT element in producer" << endl;
514                     QDomElement kproducer = wproducer.cloneNode(true).toElement();
515                     kproducer.setTagName("kdenlive_producer");
516                     infoXml_new.appendChild(kproducer);
517                     /*
518                      * TODO: Perhaps needs some more changes here to
519                      * "frequency", aspect ratio as a float, frame_size,
520                      * channels, and later, resource and title name
521                      */
522                 } else {
523                     QDomElement kproducer = m_doc.createElement("kdenlive_producer");
524                     kproducer.setAttribute("id", wproducer.attribute("id"));
525                     if (!wproducer.attribute("description").isEmpty())
526                         kproducer.setAttribute("description", wproducer.attribute("description"));
527                     kproducer.setAttribute("resource", wproducer.attribute("resource"));
528                     kproducer.setAttribute("type", wproducer.attribute("type"));
529                     // Testing fix for 358
530                     if (!wproducer.attribute("aspect_ratio").isEmpty()) {
531                         kproducer.setAttribute("aspect_ratio", wproducer.attribute("aspect_ratio"));
532                     }
533                     if (!wproducer.attribute("source_fps").isEmpty()) {
534                         kproducer.setAttribute("fps", wproducer.attribute("source_fps"));
535                     }
536                     if (!wproducer.attribute("length").isEmpty()) {
537                         kproducer.setAttribute("duration", wproducer.attribute("length"));
538                     }
539                     infoXml_new.appendChild(kproducer);
540                 }
541                 if (wproducer.attribute("id").toInt() > max_kproducer_id) {
542                     max_kproducer_id = wproducer.attribute("id").toInt();
543                 }
544             }
545         }
546 #define LOOKUP_FOLDER 1
547 #ifdef LOOKUP_FOLDER
548         /*
549          * Look through all the folder elements of the old doc, for each folder,
550          * for each producer, get the id, look it up in the new doc, set the
551          * groupname and groupid. Note, this does not work at the moment - at
552          * least one folder shows up missing, and clips with no folder does not
553          * show up.
554          */
555         //QDomElement infoXml_old = infoXmlNode.toElement();
556         if (!infoXml_old.isNull()) {
557             QDomNodeList folders = infoXml_old.elementsByTagName("folder");
558             int fsize = folders.size();
559             int groupId = max_kproducer_id + 1; // Start at +1 of max id of the kdenlive_producers
560             for (int i = 0; i < fsize; ++i) {
561                 QDomElement folder = folders.at(i).toElement();
562                 if (!folder.isNull()) {
563                     QString groupName = folder.attribute("name");
564                     kDebug() << "groupName: " << groupName << " with groupId: " << groupId;
565                     QDomNodeList fproducers = folder.elementsByTagName("producer");
566                     int psize = fproducers.size();
567                     for (int j = 0; j < psize; ++j) {
568                         QDomElement fproducer = fproducers.at(j).toElement();
569                         if (!fproducer.isNull()) {
570                             QString id = fproducer.attribute("id");
571                             // This is not very effective, but compared to loading the clips, its a breeze
572                             QDomNodeList kdenlive_producers = infoXml_new.elementsByTagName("kdenlive_producer");
573                             int kpsize = kdenlive_producers.size();
574                             for (int k = 0; k < kpsize; ++k) {
575                                 QDomElement kproducer = kdenlive_producers.at(k).toElement(); // Its an element for sure
576                                 if (id == kproducer.attribute("id")) {
577                                     // We do not check that it already is part of a folder
578                                     kproducer.setAttribute("groupid", groupId);
579                                     kproducer.setAttribute("groupname", groupName);
580                                     break;
581                                 }
582                             }
583                         }
584                     }
585                     ++groupId;
586                 }
587             }
588         }
589 #endif
590         QDomNodeList elements = westley.childNodes();
591         max = elements.count();
592         for (int i = 0; i < max; i++) {
593             QDomElement prod = elements.at(0).toElement();
594             westley0.insertAfter(prod, QDomNode());
595         }
596
597         westley0.appendChild(infoXml_new);
598
599         westley0.removeChild(westley);
600
601         // adds <avfile /> information to <kdenlive_producer />
602         QDomNodeList kproducers = m_doc.elementsByTagName("kdenlive_producer");
603         QDomNodeList avfiles = infoXml_old.elementsByTagName("avfile");
604         kDebug() << "found" << avfiles.count() << "<avfile />s and" << kproducers.count() << "<kdenlive_producer />s";
605         for (int i = 0; i < avfiles.count(); ++i) {
606             QDomElement avfile = avfiles.at(i).toElement();
607             QDomElement kproducer;
608             if (avfile.isNull())
609                 kWarning() << "found an <avfile /> that is not a QDomElement";
610             else {
611                 QString id = avfile.attribute("id");
612                 // this is horrible, must be rewritten, it's just for test
613                 for (int j = 0; j < kproducers.count(); ++j) {
614                     //kDebug() << "checking <kdenlive_producer /> with id" << kproducers.at(j).toElement().attribute("id");
615                     if (kproducers.at(j).toElement().attribute("id") == id) {
616                         kproducer = kproducers.at(j).toElement();
617                         break;
618                     }
619                 }
620                 if (kproducer == QDomElement())
621                     kWarning() << "no match for <avfile /> with id =" << id;
622                 else {
623                     //kDebug() << "ready to set additional <avfile />'s attributes (id =" << id << ")";
624                     kproducer.setAttribute("channels", avfile.attribute("channels"));
625                     kproducer.setAttribute("duration", avfile.attribute("duration"));
626                     kproducer.setAttribute("frame_size", avfile.attribute("width") + 'x' + avfile.attribute("height"));
627                     kproducer.setAttribute("frequency", avfile.attribute("frequency"));
628                     if (kproducer.attribute("description").isEmpty() && !avfile.attribute("description").isEmpty())
629                         kproducer.setAttribute("description", avfile.attribute("description"));
630                 }
631             }
632         }
633         infoXml = infoXml_new;
634     }
635
636     if (version <= 0.81) {
637         // Add the tracks information
638         QString tracksOrder = infoXml.attribute("tracks");
639         if (tracksOrder.isEmpty()) {
640             QDomNodeList tracks = m_doc.elementsByTagName("track");
641             for (int i = 0; i < tracks.count(); i++) {
642                 QDomElement track = tracks.at(i).toElement();
643                 if (track.attribute("producer") != "black_track") {
644                     if (track.attribute("hide") == "video")
645                         tracksOrder.append('a');
646                     else
647                         tracksOrder.append('v');
648                 }
649             }
650         }
651         QDomElement tracksinfo = m_doc.createElement("tracksinfo");
652         for (int i = 0; i < tracksOrder.size(); i++) {
653             QDomElement trackinfo = m_doc.createElement("trackinfo");
654             if (tracksOrder.data()[i] == 'a') {
655                 trackinfo.setAttribute("type", "audio");
656                 trackinfo.setAttribute("blind", true);
657             } else
658                 trackinfo.setAttribute("blind", false);
659             trackinfo.setAttribute("mute", false);
660             trackinfo.setAttribute("locked", false);
661             tracksinfo.appendChild(trackinfo);
662         }
663         infoXml.appendChild(tracksinfo);
664     }
665
666     if (version <= 0.82) {
667         // Convert <westley />s in <mlt />s (MLT extreme makeover)
668         QDomNodeList westleyNodes = m_doc.elementsByTagName("westley");
669         for (int i = 0; i < westleyNodes.count(); i++) {
670             QDomElement westley = westleyNodes.at(i).toElement();
671             westley.setTagName("mlt");
672         }
673     }
674
675     if (version <= 0.83) {
676         // Replace point size with pixel size in text titles
677         if (m_doc.toString().contains("font-size")) {
678             QDomNodeList kproducerNodes = m_doc.elementsByTagName("kdenlive_producer");
679             for (int i = 0; i < kproducerNodes.count(); ++i) {
680                 QDomElement kproducer = kproducerNodes.at(i).toElement();
681                 if (kproducer.attribute("type").toInt() == TEXT) {
682                     QDomDocument data;
683                     data.setContent(kproducer.attribute("xmldata"));
684                     QDomNodeList items = data.firstChild().childNodes();
685                     for (int j = 0; j < items.count(); ++j) {
686                         if (items.at(j).attributes().namedItem("type").nodeValue() == "QGraphicsTextItem") {
687                             QDomNamedNodeMap textProperties = items.at(j).namedItem("content").attributes();
688                             if (textProperties.namedItem("font-pixel-size").isNull()) {
689                                 QFont font;
690                                 font.setPointSize(textProperties.namedItem("font-size").nodeValue().toInt());
691                                 QDomElement content = items.at(j).namedItem("content").toElement();
692                                 content.setAttribute("font-pixel-size", QFontInfo(font).pixelSize());
693                                 content.removeAttribute("font-size");
694                                 kproducer.setAttribute("xmldata", data.toString());
695                                 /*
696                                  * You may be tempted to delete the preview file
697                                  * to force its recreation: bad idea (see
698                                  * http://www.kdenlive.org/mantis/view.php?id=749)
699                                  */
700                             }
701                         }
702                     }
703                 }
704             }
705         }
706     }
707
708     // The document has been converted: mark it as modified
709     infoXml.setAttribute("version", currentVersion);
710     m_modified = true;
711
712     return true;
713 }
714
715 QString DocumentValidator::colorToString(const QColor& c)
716 {
717     QString ret = "%1,%2,%3,%4";
718     ret = ret.arg(c.red()).arg(c.green()).arg(c.blue()).arg(c.alpha());
719     return ret;
720 }
721
722 bool DocumentValidator::isProject() const
723 {
724     QDomNode infoXmlNode = m_doc.elementsByTagName("kdenlivedoc").at(0);
725     return !infoXmlNode.isNull();
726 }
727
728 bool DocumentValidator::isModified() const
729 {
730     return m_modified;
731 }