]> git.sesse.net Git - kdenlive/blob - src/documentvalidator.cpp
Fix compilation warnings
[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         return true;
132     }
133
134     // The document is too new
135     if (version > currentVersion) {
136         kDebug() << "Unable to open document with version " << version;
137         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"));
138         return false;
139     }
140
141     // Unsupported document versions
142     if (version == 0.5 || version == 0.7) {
143         kDebug() << "Unable to open document with version " << version;
144         KMessageBox::sorry(kapp->activeWindow(), i18n("This project type is unsupported (version %1) and can't be loaded.", version), i18n("Unable to open project"));
145         return false;
146     }
147
148     // <kdenlivedoc />
149     QDomNode infoXmlNode = m_doc.elementsByTagName("kdenlivedoc").at(0);
150     QDomElement infoXml = infoXmlNode.toElement();
151
152     if (version <= 0.6) {
153         QDomElement infoXml_old = infoXmlNode.cloneNode(true).toElement(); // Needed for folders
154         QDomNode westley = m_doc.elementsByTagName("westley").at(1);
155         QDomNode tractor = m_doc.elementsByTagName("tractor").at(0);
156         QDomNode multitrack = m_doc.elementsByTagName("multitrack").at(0);
157         QDomNodeList playlists = m_doc.elementsByTagName("playlist");
158
159         QDomNode props = m_doc.elementsByTagName("properties").at(0).toElement();
160         QString profile = props.toElement().attribute("videoprofile");
161         int startPos = props.toElement().attribute("timeline_position").toInt();
162         if (profile == "dv_wide")
163             profile = "dv_pal_wide";
164
165         // move playlists outside of tractor and add the tracks instead
166         int max = playlists.count();
167         if (westley.isNull()) {
168             westley = m_doc.createElement("westley");
169             m_doc.documentElement().appendChild(westley);
170         }
171         if (tractor.isNull()) {
172             kDebug() << "// NO MLT PLAYLIST, building empty one";
173             QDomElement blank_tractor = m_doc.createElement("tractor");
174             westley.appendChild(blank_tractor);
175             QDomElement blank_playlist = m_doc.createElement("playlist");
176             blank_playlist.setAttribute("id", "black_track");
177             westley.insertBefore(blank_playlist, QDomNode());
178             QDomElement blank_track = m_doc.createElement("track");
179             blank_track.setAttribute("producer", "black_track");
180             blank_tractor.appendChild(blank_track);
181
182             QDomNodeList kdenlivetracks = m_doc.elementsByTagName("kdenlivetrack");
183             for (int i = 0; i < kdenlivetracks.count(); i++) {
184                 blank_playlist = m_doc.createElement("playlist");
185                 blank_playlist.setAttribute("id", "playlist" + QString::number(i));
186                 westley.insertBefore(blank_playlist, QDomNode());
187                 blank_track = m_doc.createElement("track");
188                 blank_track.setAttribute("producer", "playlist" + QString::number(i));
189                 blank_tractor.appendChild(blank_track);
190                 if (kdenlivetracks.at(i).toElement().attribute("cliptype") == "Sound") {
191                     blank_playlist.setAttribute("hide", "video");
192                     blank_track.setAttribute("hide", "video");
193                 }
194             }
195         } else for (int i = 0; i < max; i++) {
196                 QDomNode n = playlists.at(i);
197                 westley.insertBefore(n, QDomNode());
198                 QDomElement pl = n.toElement();
199                 QDomElement track = m_doc.createElement("track");
200                 QString trackType = pl.attribute("hide");
201                 if (!trackType.isEmpty())
202                     track.setAttribute("hide", trackType);
203                 QString playlist_id =  pl.attribute("id");
204                 if (playlist_id.isEmpty()) {
205                     playlist_id = "black_track";
206                     pl.setAttribute("id", playlist_id);
207                 }
208                 track.setAttribute("producer", playlist_id);
209                 //tractor.appendChild(track);
210 #define KEEP_TRACK_ORDER 1
211 #ifdef KEEP_TRACK_ORDER
212                 tractor.insertAfter(track, QDomNode());
213 #else
214                 // Insert the new track in an order that hopefully matches the 3 video, then 2 audio tracks of Kdenlive 0.7.0
215                 // insertion sort - O( tracks*tracks )
216                 // Note, this breaks _all_ transitions - but you can move them up and down afterwards.
217                 QDomElement tractor_elem = tractor.toElement();
218                 if (! tractor_elem.isNull()) {
219                     QDomNodeList tracks = tractor_elem.elementsByTagName("track");
220                     int size = tracks.size();
221                     if (size == 0) {
222                         tractor.insertAfter(track, QDomNode());
223                     } else {
224                         bool inserted = false;
225                         for (int i = 0; i < size; ++i) {
226                             QDomElement track_elem = tracks.at(i).toElement();
227                             if (track_elem.isNull()) {
228                                 tractor.insertAfter(track, QDomNode());
229                                 inserted = true;
230                                 break;
231                             } else {
232                                 kDebug() << "playlist_id: " << playlist_id << " producer:" << track_elem.attribute("producer");
233                                 if (playlist_id < track_elem.attribute("producer")) {
234                                     tractor.insertBefore(track, track_elem);
235                                     inserted = true;
236                                     break;
237                                 }
238                             }
239                         }
240                         // Reach here, no insertion, insert last
241                         if (!inserted) {
242                             tractor.insertAfter(track, QDomNode());
243                         }
244                     }
245                 } else {
246                     kWarning() << "tractor was not a QDomElement";
247                     tractor.insertAfter(track, QDomNode());
248                 }
249 #endif
250             }
251         tractor.removeChild(multitrack);
252
253         // audio track mixing transitions should not be added to track view, so add required attribute
254         QDomNodeList transitions = m_doc.elementsByTagName("transition");
255         max = transitions.count();
256         for (int i = 0; i < max; i++) {
257             QDomElement tr = transitions.at(i).toElement();
258             if (tr.attribute("combine") == "1" && tr.attribute("mlt_service") == "mix") {
259                 QDomElement property = m_doc.createElement("property");
260                 property.setAttribute("name", "internal_added");
261                 QDomText value = m_doc.createTextNode("237");
262                 property.appendChild(value);
263                 tr.appendChild(property);
264                 property = m_doc.createElement("property");
265                 property.setAttribute("name", "mlt_service");
266                 value = m_doc.createTextNode("mix");
267                 property.appendChild(value);
268                 tr.appendChild(property);
269             } else {
270                 // convert transition
271                 QDomNamedNodeMap attrs = tr.attributes();
272                 for (int j = 0; j < attrs.count(); j++) {
273                     QString attrName = attrs.item(j).nodeName();
274                     if (attrName != "in" && attrName != "out" && attrName != "id") {
275                         QDomElement property = m_doc.createElement("property");
276                         property.setAttribute("name", attrName);
277                         QDomText value = m_doc.createTextNode(attrs.item(j).nodeValue());
278                         property.appendChild(value);
279                         tr.appendChild(property);
280                     }
281                 }
282             }
283         }
284
285         // move transitions after tracks
286         for (int i = 0; i < max; i++) {
287             tractor.insertAfter(transitions.at(0), QDomNode());
288         }
289
290         // Fix filters format
291         QDomNodeList entries = m_doc.elementsByTagName("entry");
292         max = entries.count();
293         for (int i = 0; i < max; i++) {
294             QString last_id;
295             int effectix = 0;
296             QDomNode m = entries.at(i).firstChild();
297             while (!m.isNull()) {
298                 if (m.toElement().tagName() == "filter") {
299                     QDomElement filt = m.toElement();
300                     QDomNamedNodeMap attrs = filt.attributes();
301                     QString current_id = filt.attribute("kdenlive_id");
302                     if (current_id != last_id) {
303                         effectix++;
304                         last_id = current_id;
305                     }
306                     QDomElement e = m_doc.createElement("property");
307                     e.setAttribute("name", "kdenlive_ix");
308                     QDomText value = m_doc.createTextNode(QString::number(effectix));
309                     e.appendChild(value);
310                     filt.appendChild(e);
311                     for (int j = 0; j < attrs.count(); j++) {
312                         QDomAttr a = attrs.item(j).toAttr();
313                         if (!a.isNull()) {
314                             kDebug() << " FILTER; adding :" << a.name() << ":" << a.value();
315                             QDomElement e = m_doc.createElement("property");
316                             e.setAttribute("name", a.name());
317                             QDomText value = m_doc.createTextNode(a.value());
318                             e.appendChild(value);
319                             filt.appendChild(e);
320
321                         }
322                     }
323                 }
324                 m = m.nextSibling();
325             }
326         }
327
328         /*
329             QDomNodeList filters = m_doc.elementsByTagName("filter");
330             max = filters.count();
331             QString last_id;
332             int effectix = 0;
333             for (int i = 0; i < max; i++) {
334                 QDomElement filt = filters.at(i).toElement();
335                 QDomNamedNodeMap attrs = filt.attributes();
336         QString current_id = filt.attribute("kdenlive_id");
337         if (current_id != last_id) {
338             effectix++;
339             last_id = current_id;
340         }
341         QDomElement e = m_doc.createElement("property");
342                 e.setAttribute("name", "kdenlive_ix");
343                 QDomText value = m_doc.createTextNode(QString::number(1));
344                 e.appendChild(value);
345                 filt.appendChild(e);
346                 for (int j = 0; j < attrs.count(); j++) {
347                     QDomAttr a = attrs.item(j).toAttr();
348                     if (!a.isNull()) {
349                         kDebug() << " FILTER; adding :" << a.name() << ":" << a.value();
350                         QDomElement e = m_doc.createElement("property");
351                         e.setAttribute("name", a.name());
352                         QDomText value = m_doc.createTextNode(a.value());
353                         e.appendChild(value);
354                         filt.appendChild(e);
355                     }
356                 }
357             }*/
358
359         // fix slowmotion
360         QDomNodeList producers = westley.toElement().elementsByTagName("producer");
361         max = producers.count();
362         for (int i = 0; i < max; i++) {
363             QDomElement prod = producers.at(i).toElement();
364             if (prod.attribute("mlt_service") == "framebuffer") {
365                 QString slowmotionprod = prod.attribute("resource");
366                 slowmotionprod.replace(':', '?');
367                 kDebug() << "// FOUND WRONG SLOWMO, new: " << slowmotionprod;
368                 prod.setAttribute("resource", slowmotionprod);
369             }
370         }
371         // move producers to correct place, markers to a global list, fix clip descriptions
372         QDomElement markers = m_doc.createElement("markers");
373         // This will get the xml producers:
374         producers = m_doc.elementsByTagName("producer");
375         max = producers.count();
376         for (int i = 0; i < max; i++) {
377             QDomElement prod = producers.at(0).toElement();
378             // add resource also as a property (to allow path correction in setNewResource())
379             // TODO: will it work with slowmotion? needs testing
380             /*if (!prod.attribute("resource").isEmpty()) {
381                 QDomElement prop_resource = m_doc.createElement("property");
382                 prop_resource.setAttribute("name", "resource");
383                 QDomText resource = m_doc.createTextNode(prod.attribute("resource"));
384                 prop_resource.appendChild(resource);
385                 prod.appendChild(prop_resource);
386             }*/
387             QDomNode m = prod.firstChild();
388             if (!m.isNull()) {
389                 if (m.toElement().tagName() == "markers") {
390                     QDomNodeList prodchilds = m.childNodes();
391                     int maxchild = prodchilds.count();
392                     for (int k = 0; k < maxchild; k++) {
393                         QDomElement mark = prodchilds.at(0).toElement();
394                         mark.setAttribute("id", prod.attribute("id"));
395                         markers.insertAfter(mark, QDomNode());
396                     }
397                     prod.removeChild(m);
398                 } else if (prod.attribute("type").toInt() == TEXT) {
399                     // convert title clip
400                     if (m.toElement().tagName() == "textclip") {
401                         QDomDocument tdoc;
402                         QDomElement titleclip = m.toElement();
403                         QDomElement title = tdoc.createElement("kdenlivetitle");
404                         tdoc.appendChild(title);
405                         QDomNodeList objects = titleclip.childNodes();
406                         int maxchild = objects.count();
407                         for (int k = 0; k < maxchild; k++) {
408                             QString objectxml;
409                             QDomElement ob = objects.at(k).toElement();
410                             if (ob.attribute("type") == "3") {
411                                 // text object - all of this goes into "xmldata"...
412                                 QDomElement item = tdoc.createElement("item");
413                                 item.setAttribute("z-index", ob.attribute("z"));
414                                 item.setAttribute("type", "QGraphicsTextItem");
415                                 QDomElement position = tdoc.createElement("position");
416                                 position.setAttribute("x", ob.attribute("x"));
417                                 position.setAttribute("y", ob.attribute("y"));
418                                 QDomElement content = tdoc.createElement("content");
419                                 content.setAttribute("font", ob.attribute("font_family"));
420                                 content.setAttribute("font-size", ob.attribute("font_size"));
421                                 content.setAttribute("font-bold", ob.attribute("bold"));
422                                 content.setAttribute("font-italic", ob.attribute("italic"));
423                                 content.setAttribute("font-underline", ob.attribute("underline"));
424                                 QString col = ob.attribute("color");
425                                 QColor c(col);
426                                 content.setAttribute("font-color", colorToString(c));
427                                 // todo: These fields are missing from the newly generated xmldata:
428                                 // transform, startviewport, endviewport, background
429
430                                 QDomText conttxt = tdoc.createTextNode(ob.attribute("text"));
431                                 content.appendChild(conttxt);
432                                 item.appendChild(position);
433                                 item.appendChild(content);
434                                 title.appendChild(item);
435                             } else if (ob.attribute("type") == "5") {
436                                 // rectangle object
437                                 QDomElement item = tdoc.createElement("item");
438                                 item.setAttribute("z-index", ob.attribute("z"));
439                                 item.setAttribute("type", "QGraphicsRectItem");
440                                 QDomElement position = tdoc.createElement("position");
441                                 position.setAttribute("x", ob.attribute("x"));
442                                 position.setAttribute("y", ob.attribute("y"));
443                                 QDomElement content = tdoc.createElement("content");
444                                 QString col = ob.attribute("color");
445                                 QColor c(col);
446                                 content.setAttribute("brushcolor", colorToString(c));
447                                 QString rect = "0,0,";
448                                 rect.append(ob.attribute("width"));
449                                 rect.append(",");
450                                 rect.append(ob.attribute("height"));
451                                 content.setAttribute("rect", rect);
452                                 item.appendChild(position);
453                                 item.appendChild(content);
454                                 title.appendChild(item);
455                             }
456                         }
457                         prod.setAttribute("xmldata", tdoc.toString());
458                         // mbd todo: This clearly does not work, as every title gets the same name - trying to leave it empty
459                         // QStringList titleInfo = TitleWidget::getFreeTitleInfo(projectFolder());
460                         // prod.setAttribute("titlename", titleInfo.at(0));
461                         // prod.setAttribute("resource", titleInfo.at(1));
462                         //kDebug()<<"TITLE DATA:\n"<<tdoc.toString();
463                         prod.removeChild(m);
464                     } // End conversion of title clips.
465
466                 } else if (m.isText()) {
467                     QString comment = m.nodeValue();
468                     if (!comment.isEmpty()) {
469                         prod.setAttribute("description", comment);
470                     }
471                     prod.removeChild(m);
472                 }
473             }
474             int duration = prod.attribute("duration").toInt();
475             if (duration > 0) prod.setAttribute("out", QString::number(duration));
476             // The clip goes back in, but text clips should not go back in, at least not modified
477             westley.insertBefore(prod, QDomNode());
478         }
479
480         QDomNode westley0 = m_doc.elementsByTagName("westley").at(0);
481         if (!markers.firstChild().isNull()) westley0.appendChild(markers);
482
483         /*
484          * Convert as much of the kdenlivedoc as possible. Use the producer in
485          * westley. First, remove the old stuff from westley, and add a new
486          * empty one. Also, track the max id in order to use it for the adding
487          * of groups/folders
488          */
489         int max_kproducer_id = 0;
490         westley0.removeChild(infoXmlNode);
491         QDomElement infoXml_new = m_doc.createElement("kdenlivedoc");
492         infoXml_new.setAttribute("profile", profile);
493         infoXml.setAttribute("position", startPos);
494
495         // Add all the producers that has a resource in westley
496         QDomElement westley_element = westley0.toElement();
497         if (westley_element.isNull()) {
498             kWarning() << "westley0 element in document was not a QDomElement - unable to add producers to new kdenlivedoc";
499         } else {
500             QDomNodeList wproducers = westley_element.elementsByTagName("producer");
501             int kmax = wproducers.count();
502             for (int i = 0; i < kmax; i++) {
503                 QDomElement wproducer = wproducers.at(i).toElement();
504                 if (wproducer.isNull()) {
505                     kWarning() << "Found producer in westley0, that was not a QDomElement";
506                     continue;
507                 }
508                 if (wproducer.attribute("id") == "black") continue;
509                 // We have to do slightly different things, depending on the type
510                 kDebug() << "Converting producer element with type" << wproducer.attribute("type");
511                 if (wproducer.attribute("type").toInt() == TEXT) {
512                     kDebug() << "Found TEXT element in producer" << endl;
513                     QDomElement kproducer = wproducer.cloneNode(true).toElement();
514                     kproducer.setTagName("kdenlive_producer");
515                     infoXml_new.appendChild(kproducer);
516                     /*
517                      * TODO: Perhaps needs some more changes here to
518                      * "frequency", aspect ratio as a float, frame_size,
519                      * channels, and later, resource and title name
520                      */
521                 } else {
522                     QDomElement kproducer = m_doc.createElement("kdenlive_producer");
523                     kproducer.setAttribute("id", wproducer.attribute("id"));
524                     if (!wproducer.attribute("description").isEmpty())
525                         kproducer.setAttribute("description", wproducer.attribute("description"));
526                     kproducer.setAttribute("resource", wproducer.attribute("resource"));
527                     kproducer.setAttribute("type", wproducer.attribute("type"));
528                     // Testing fix for 358
529                     if (!wproducer.attribute("aspect_ratio").isEmpty()) {
530                         kproducer.setAttribute("aspect_ratio", wproducer.attribute("aspect_ratio"));
531                     }
532                     if (!wproducer.attribute("source_fps").isEmpty()) {
533                         kproducer.setAttribute("fps", wproducer.attribute("source_fps"));
534                     }
535                     if (!wproducer.attribute("length").isEmpty()) {
536                         kproducer.setAttribute("duration", wproducer.attribute("length"));
537                     }
538                     infoXml_new.appendChild(kproducer);
539                 }
540                 if (wproducer.attribute("id").toInt() > max_kproducer_id) {
541                     max_kproducer_id = wproducer.attribute("id").toInt();
542                 }
543             }
544         }
545 #define LOOKUP_FOLDER 1
546 #ifdef LOOKUP_FOLDER
547         /*
548          * Look through all the folder elements of the old doc, for each folder,
549          * for each producer, get the id, look it up in the new doc, set the
550          * groupname and groupid. Note, this does not work at the moment - at
551          * least one folder shows up missing, and clips with no folder does not
552          * show up.
553          */
554         //QDomElement infoXml_old = infoXmlNode.toElement();
555         if (!infoXml_old.isNull()) {
556             QDomNodeList folders = infoXml_old.elementsByTagName("folder");
557             int fsize = folders.size();
558             int groupId = max_kproducer_id + 1; // Start at +1 of max id of the kdenlive_producers
559             for (int i = 0; i < fsize; ++i) {
560                 QDomElement folder = folders.at(i).toElement();
561                 if (!folder.isNull()) {
562                     QString groupName = folder.attribute("name");
563                     kDebug() << "groupName: " << groupName << " with groupId: " << groupId;
564                     QDomNodeList fproducers = folder.elementsByTagName("producer");
565                     int psize = fproducers.size();
566                     for (int j = 0; j < psize; ++j) {
567                         QDomElement fproducer = fproducers.at(j).toElement();
568                         if (!fproducer.isNull()) {
569                             QString id = fproducer.attribute("id");
570                             // This is not very effective, but compared to loading the clips, its a breeze
571                             QDomNodeList kdenlive_producers = infoXml_new.elementsByTagName("kdenlive_producer");
572                             int kpsize = kdenlive_producers.size();
573                             for (int k = 0; k < kpsize; ++k) {
574                                 QDomElement kproducer = kdenlive_producers.at(k).toElement(); // Its an element for sure
575                                 if (id == kproducer.attribute("id")) {
576                                     // We do not check that it already is part of a folder
577                                     kproducer.setAttribute("groupid", groupId);
578                                     kproducer.setAttribute("groupname", groupName);
579                                     break;
580                                 }
581                             }
582                         }
583                     }
584                     ++groupId;
585                 }
586             }
587         }
588 #endif
589         QDomNodeList elements = westley.childNodes();
590         max = elements.count();
591         for (int i = 0; i < max; i++) {
592             QDomElement prod = elements.at(0).toElement();
593             westley0.insertAfter(prod, QDomNode());
594         }
595
596         westley0.appendChild(infoXml_new);
597
598         westley0.removeChild(westley);
599
600         // adds <avfile /> information to <kdenlive_producer />
601         QDomNodeList kproducers = m_doc.elementsByTagName("kdenlive_producer");
602         QDomNodeList avfiles = infoXml_old.elementsByTagName("avfile");
603         kDebug() << "found" << avfiles.count() << "<avfile />s and" << kproducers.count() << "<kdenlive_producer />s";
604         for (int i = 0; i < avfiles.count(); ++i) {
605             QDomElement avfile = avfiles.at(i).toElement();
606             QDomElement kproducer;
607             if (avfile.isNull())
608                 kWarning() << "found an <avfile /> that is not a QDomElement";
609             else {
610                 QString id = avfile.attribute("id");
611                 // this is horrible, must be rewritten, it's just for test
612                 for (int j = 0; j < kproducers.count(); ++j) {
613                     //kDebug() << "checking <kdenlive_producer /> with id" << kproducers.at(j).toElement().attribute("id");
614                     if (kproducers.at(j).toElement().attribute("id") == id) {
615                         kproducer = kproducers.at(j).toElement();
616                         break;
617                     }
618                 }
619                 if (kproducer == QDomElement())
620                     kWarning() << "no match for <avfile /> with id =" << id;
621                 else {
622                     //kDebug() << "ready to set additional <avfile />'s attributes (id =" << id << ")";
623                     kproducer.setAttribute("channels", avfile.attribute("channels"));
624                     kproducer.setAttribute("duration", avfile.attribute("duration"));
625                     kproducer.setAttribute("frame_size", avfile.attribute("width") + 'x' + avfile.attribute("height"));
626                     kproducer.setAttribute("frequency", avfile.attribute("frequency"));
627                     if (kproducer.attribute("description").isEmpty() && !avfile.attribute("description").isEmpty())
628                         kproducer.setAttribute("description", avfile.attribute("description"));
629                 }
630             }
631         }
632         infoXml = infoXml_new;
633     }
634
635     if (version <= 0.81) {
636         // Add the tracks information
637         QString tracksOrder = infoXml.attribute("tracks");
638         if (tracksOrder.isEmpty()) {
639             QDomNodeList tracks = m_doc.elementsByTagName("track");
640             for (int i = 0; i < tracks.count(); i++) {
641                 QDomElement track = tracks.at(i).toElement();
642                 if (track.attribute("producer") != "black_track") {
643                     if (track.attribute("hide") == "video")
644                         tracksOrder.append('a');
645                     else
646                         tracksOrder.append('v');
647                 }
648             }
649         }
650         QDomElement tracksinfo = m_doc.createElement("tracksinfo");
651         for (int i = 0; i < tracksOrder.size(); i++) {
652             QDomElement trackinfo = m_doc.createElement("trackinfo");
653             if (tracksOrder.data()[i] == 'a') {
654                 trackinfo.setAttribute("type", "audio");
655                 trackinfo.setAttribute("blind", true);
656             } else
657                 trackinfo.setAttribute("blind", false);
658             trackinfo.setAttribute("mute", false);
659             trackinfo.setAttribute("locked", false);
660             tracksinfo.appendChild(trackinfo);
661         }
662         infoXml.appendChild(tracksinfo);
663     }
664
665     if (version <= 0.82) {
666         // Convert <westley />s in <mlt />s (MLT extreme makeover)
667         QDomNodeList westleyNodes = m_doc.elementsByTagName("westley");
668         for (int i = 0; i < westleyNodes.count(); i++) {
669             QDomElement westley = westleyNodes.at(i).toElement();
670             westley.setTagName("mlt");
671         }
672     }
673
674     if (version <= 0.83) {
675         // Replace point size with pixel size in text titles
676         if (m_doc.toString().contains("font-size")) {
677             KMessageBox::ButtonCode convert = KMessageBox::Continue;
678             QDomNodeList kproducerNodes = m_doc.elementsByTagName("kdenlive_producer");
679             for (int i = 0; i < kproducerNodes.count() && convert != KMessageBox::No; ++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() && convert != KMessageBox::No; ++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() && !textProperties.namedItem("font-size").isNull()) {
689                                 // Ask the user if he wants to convert
690                                 if (convert != KMessageBox::Yes && convert != KMessageBox::No)
691                                     convert = (KMessageBox::ButtonCode)KMessageBox::warningYesNo(kapp->activeWindow(), i18n("Some of your text clips were saved with size in points, which means different sizes on different displays. Do you want to convert them to pixel size, making them portable? It is recommended you do this on the computer they were first created on, or you could have to adjust their size."), i18n("Update Text Clips"));
692                                 if (convert == KMessageBox::Yes) {
693                                     QFont font;
694                                     font.setPointSize(textProperties.namedItem("font-size").nodeValue().toInt());
695                                     QDomElement content = items.at(j).namedItem("content").toElement();
696                                     content.setAttribute("font-pixel-size", QFontInfo(font).pixelSize());
697                                     content.removeAttribute("font-size");
698                                     kproducer.setAttribute("xmldata", data.toString());
699                                     /*
700                                      * You may be tempted to delete the preview file
701                                      * to force its recreation: bad idea (see
702                                      * http://www.kdenlive.org/mantis/view.php?id=749)
703                                      */
704                                 }
705                             }
706                         }
707                     }
708                 }
709             }
710         }
711
712         // Fill the <documentproperties /> element
713         QDomElement docProperties = infoXml.firstChildElement("documentproperties");
714         if (docProperties.isNull()) {
715             docProperties = m_doc.createElement("documentproperties");
716             docProperties.setAttribute("zonein", infoXml.attribute("zonein"));
717             docProperties.setAttribute("zoneout", infoXml.attribute("zoneout"));
718             docProperties.setAttribute("zoom", infoXml.attribute("zoom"));
719             docProperties.setAttribute("position", infoXml.attribute("position"));
720             infoXml.appendChild(docProperties);
721         }
722     }
723
724     // The document has been converted: mark it as modified
725     infoXml.setAttribute("version", currentVersion);
726     m_modified = true;
727
728     return true;
729 }
730
731 QString DocumentValidator::colorToString(const QColor& c)
732 {
733     QString ret = "%1,%2,%3,%4";
734     ret = ret.arg(c.red()).arg(c.green()).arg(c.blue()).arg(c.alpha());
735     return ret;
736 }
737
738 bool DocumentValidator::isProject() const
739 {
740     QDomNode infoXmlNode = m_doc.elementsByTagName("kdenlivedoc").at(0);
741     return !infoXmlNode.isNull();
742 }
743
744 bool DocumentValidator::isModified() const
745 {
746     return m_modified;
747 }