1 /***************************************************************************
2 * Copyright (C) 2007 by Jean-Baptiste Mardelle (jb@kdenlive.org) *
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. *
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. *
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 ***************************************************************************/
21 #include "documentvalidator.h"
22 #include "definitions.h"
25 #include <KMessageBox>
26 #include <KApplication>
31 DocumentValidator::DocumentValidator(QDomDocument doc):
36 bool DocumentValidator::validate(const double currentVersion)
38 // Check if we're validating a Kdenlive project
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))
49 * Check the syntax (this will be replaced by XSD validation with Qt 4.6)
50 * and correct some errors
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
57 // Return (or create) the tractor
58 QDomNode tractorNode = m_doc.elementsByTagName("tractor").at(0);
59 QDomElement tractorElm = tractorNode.toElement();
60 if (tractorElm.isNull()) {
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);
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
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) {
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);
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);
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);
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);
117 // TODO: check the tracks references
118 // TODO: check internal mix transitions
124 bool DocumentValidator::upgrade(double version, const double currentVersion)
126 kDebug() << "Opening a document with version " << version;
128 // No conversion needed
129 if (version == currentVersion)
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"));
140 QDomNode infoXmlNode = m_doc.elementsByTagName("kdenlivedoc").at(0);
141 QDomElement infoXml = infoXmlNode.toElement();
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"));
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");
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";
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);
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);
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");
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);
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());
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();
220 tractor.insertAfter(track, QDomNode());
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());
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);
238 // Reach here, no insertion, insert last
240 tractor.insertAfter(track, QDomNode());
244 kWarning() << "tractor was not a QDomElement";
245 tractor.insertAfter(track, QDomNode());
249 tractor.removeChild(multitrack);
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);
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);
283 // move transitions after tracks
284 for (int i = 0; i < max; i++) {
285 tractor.insertAfter(transitions.at(0), QDomNode());
288 // Fix filters format
289 QDomNodeList entries = m_doc.elementsByTagName("entry");
290 max = entries.count();
291 for (int i = 0; i < max; i++) {
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) {
302 last_id = current_id;
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);
309 for (int j = 0; j < attrs.count(); j++) {
310 QDomAttr a = attrs.item(j).toAttr();
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);
327 QDomNodeList filters = m_doc.elementsByTagName("filter");
328 max = filters.count();
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) {
337 last_id = current_id;
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);
344 for (int j = 0; j < attrs.count(); j++) {
345 QDomAttr a = attrs.item(j).toAttr();
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);
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);
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);
385 QDomNode m = prod.firstChild();
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());
396 } else if (prod.attribute("type").toInt() == TEXT) {
397 // convert title clip
398 if (m.toElement().tagName() == "textclip") {
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++) {
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");
424 content.setAttribute("font-color", colorToString(c));
425 // todo: These fields are missing from the newly generated xmldata:
426 // transform, startviewport, endviewport, background
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") {
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");
444 content.setAttribute("brushcolor", colorToString(c));
445 QString rect = "0,0,";
446 rect.append(ob.attribute("width"));
448 rect.append(ob.attribute("height"));
449 content.setAttribute("rect", rect);
450 item.appendChild(position);
451 item.appendChild(content);
452 title.appendChild(item);
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();
462 } // End conversion of title clips.
464 } else if (m.isText()) {
465 QString comment = m.nodeValue();
466 if (!comment.isEmpty()) {
467 prod.setAttribute("description", comment);
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());
478 QDomNode westley0 = m_doc.elementsByTagName("westley").at(0);
479 if (!markers.firstChild().isNull()) westley0.appendChild(markers);
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
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);
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";
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";
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);
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
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"));
530 if (!wproducer.attribute("source_fps").isEmpty()) {
531 kproducer.setAttribute("fps", wproducer.attribute("source_fps"));
533 if (!wproducer.attribute("length").isEmpty()) {
534 kproducer.setAttribute("duration", wproducer.attribute("length"));
536 infoXml_new.appendChild(kproducer);
538 if (wproducer.attribute("id").toInt() > max_kproducer_id) {
539 max_kproducer_id = wproducer.attribute("id").toInt();
543 #define LOOKUP_FOLDER 1
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
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);
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());
594 westley0.appendChild(infoXml_new);
596 westley0.removeChild(westley);
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;
606 kWarning() << "found an <avfile /> that is not a QDomElement";
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();
617 if (kproducer == QDomElement())
618 kWarning() << "no match for <avfile /> with id =" << id;
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"));
630 infoXml = infoXml_new;
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');
644 tracksOrder.append('v');
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);
655 trackinfo.setAttribute("blind", false);
656 trackinfo.setAttribute("mute", false);
657 trackinfo.setAttribute("locked", false);
658 tracksinfo.appendChild(trackinfo);
660 infoXml.appendChild(tracksinfo);
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");
672 // The document has been converted: mark it as modified
673 infoXml.setAttribute("version", currentVersion);
679 QString DocumentValidator::colorToString(const QColor& c)
681 QString ret = "%1,%2,%3,%4";
682 ret = ret.arg(c.red()).arg(c.green()).arg(c.blue()).arg(c.alpha());
686 bool DocumentValidator::isProject() const
688 QDomNode infoXmlNode = m_doc.elementsByTagName("kdenlivedoc").at(0);
689 return !infoXmlNode.isNull();
692 bool DocumentValidator::isModified() const