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