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>
32 DocumentValidator::DocumentValidator(QDomDocument doc):
37 bool DocumentValidator::validate(const double currentVersion)
39 // Check if we're validating a Kdenlive project
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))
50 * Check the syntax (this will be replaced by XSD validation with Qt 4.6)
51 * and correct some errors
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
58 // Return (or create) the tractor
59 QDomNode tractorNode = m_doc.elementsByTagName("tractor").at(0);
60 QDomElement tractorElm = tractorNode.toElement();
61 if (tractorElm.isNull()) {
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);
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
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) {
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);
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);
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);
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);
118 // TODO: check the tracks references
119 // TODO: check internal mix transitions
125 bool DocumentValidator::upgrade(double version, const double currentVersion)
127 kDebug() << "Opening a document with version " << version;
129 // No conversion needed
130 if (version == currentVersion) {
131 if (!m_doc.toString().contains("font-size")) // TODO: remove when currentVersion == 0.84
135 // The document is too new
136 if (version > currentVersion) {
137 kDebug() << "Unable to open document with version " << version;
138 KMessageBox::sorry(kapp->activeWindow(), i18n("This project type is unsupported (version %1) and can't be loaded.\nPlease consider upgrading you Kdenlive version.", version), i18n("Unable to open project"));
142 // Unsupported document versions
143 if (version == 0.5 || version == 0.7) {
144 kDebug() << "Unable to open document with version " << version;
145 KMessageBox::sorry(kapp->activeWindow(), i18n("This project type is unsupported (version %1) and can't be loaded.", version), i18n("Unable to open project"));
150 QDomNode infoXmlNode = m_doc.elementsByTagName("kdenlivedoc").at(0);
151 QDomElement infoXml = infoXmlNode.toElement();
153 if (version <= 0.6) {
154 QDomElement infoXml_old = infoXmlNode.cloneNode(true).toElement(); // Needed for folders
155 QDomNode westley = m_doc.elementsByTagName("westley").at(1);
156 QDomNode tractor = m_doc.elementsByTagName("tractor").at(0);
157 QDomNode multitrack = m_doc.elementsByTagName("multitrack").at(0);
158 QDomNodeList playlists = m_doc.elementsByTagName("playlist");
160 QDomNode props = m_doc.elementsByTagName("properties").at(0).toElement();
161 QString profile = props.toElement().attribute("videoprofile");
162 int startPos = props.toElement().attribute("timeline_position").toInt();
163 if (profile == "dv_wide")
164 profile = "dv_pal_wide";
166 // move playlists outside of tractor and add the tracks instead
167 int max = playlists.count();
168 if (westley.isNull()) {
169 westley = m_doc.createElement("westley");
170 m_doc.documentElement().appendChild(westley);
172 if (tractor.isNull()) {
173 kDebug() << "// NO MLT PLAYLIST, building empty one";
174 QDomElement blank_tractor = m_doc.createElement("tractor");
175 westley.appendChild(blank_tractor);
176 QDomElement blank_playlist = m_doc.createElement("playlist");
177 blank_playlist.setAttribute("id", "black_track");
178 westley.insertBefore(blank_playlist, QDomNode());
179 QDomElement blank_track = m_doc.createElement("track");
180 blank_track.setAttribute("producer", "black_track");
181 blank_tractor.appendChild(blank_track);
183 QDomNodeList kdenlivetracks = m_doc.elementsByTagName("kdenlivetrack");
184 for (int i = 0; i < kdenlivetracks.count(); i++) {
185 blank_playlist = m_doc.createElement("playlist");
186 blank_playlist.setAttribute("id", "playlist" + QString::number(i));
187 westley.insertBefore(blank_playlist, QDomNode());
188 blank_track = m_doc.createElement("track");
189 blank_track.setAttribute("producer", "playlist" + QString::number(i));
190 blank_tractor.appendChild(blank_track);
191 if (kdenlivetracks.at(i).toElement().attribute("cliptype") == "Sound") {
192 blank_playlist.setAttribute("hide", "video");
193 blank_track.setAttribute("hide", "video");
196 } else for (int i = 0; i < max; i++) {
197 QDomNode n = playlists.at(i);
198 westley.insertBefore(n, QDomNode());
199 QDomElement pl = n.toElement();
200 QDomElement track = m_doc.createElement("track");
201 QString trackType = pl.attribute("hide");
202 if (!trackType.isEmpty())
203 track.setAttribute("hide", trackType);
204 QString playlist_id = pl.attribute("id");
205 if (playlist_id.isEmpty()) {
206 playlist_id = "black_track";
207 pl.setAttribute("id", playlist_id);
209 track.setAttribute("producer", playlist_id);
210 //tractor.appendChild(track);
211 #define KEEP_TRACK_ORDER 1
212 #ifdef KEEP_TRACK_ORDER
213 tractor.insertAfter(track, QDomNode());
215 // Insert the new track in an order that hopefully matches the 3 video, then 2 audio tracks of Kdenlive 0.7.0
216 // insertion sort - O( tracks*tracks )
217 // Note, this breaks _all_ transitions - but you can move them up and down afterwards.
218 QDomElement tractor_elem = tractor.toElement();
219 if (! tractor_elem.isNull()) {
220 QDomNodeList tracks = tractor_elem.elementsByTagName("track");
221 int size = tracks.size();
223 tractor.insertAfter(track, QDomNode());
225 bool inserted = false;
226 for (int i = 0; i < size; ++i) {
227 QDomElement track_elem = tracks.at(i).toElement();
228 if (track_elem.isNull()) {
229 tractor.insertAfter(track, QDomNode());
233 kDebug() << "playlist_id: " << playlist_id << " producer:" << track_elem.attribute("producer");
234 if (playlist_id < track_elem.attribute("producer")) {
235 tractor.insertBefore(track, track_elem);
241 // Reach here, no insertion, insert last
243 tractor.insertAfter(track, QDomNode());
247 kWarning() << "tractor was not a QDomElement";
248 tractor.insertAfter(track, QDomNode());
252 tractor.removeChild(multitrack);
254 // audio track mixing transitions should not be added to track view, so add required attribute
255 QDomNodeList transitions = m_doc.elementsByTagName("transition");
256 max = transitions.count();
257 for (int i = 0; i < max; i++) {
258 QDomElement tr = transitions.at(i).toElement();
259 if (tr.attribute("combine") == "1" && tr.attribute("mlt_service") == "mix") {
260 QDomElement property = m_doc.createElement("property");
261 property.setAttribute("name", "internal_added");
262 QDomText value = m_doc.createTextNode("237");
263 property.appendChild(value);
264 tr.appendChild(property);
265 property = m_doc.createElement("property");
266 property.setAttribute("name", "mlt_service");
267 value = m_doc.createTextNode("mix");
268 property.appendChild(value);
269 tr.appendChild(property);
271 // convert transition
272 QDomNamedNodeMap attrs = tr.attributes();
273 for (int j = 0; j < attrs.count(); j++) {
274 QString attrName = attrs.item(j).nodeName();
275 if (attrName != "in" && attrName != "out" && attrName != "id") {
276 QDomElement property = m_doc.createElement("property");
277 property.setAttribute("name", attrName);
278 QDomText value = m_doc.createTextNode(attrs.item(j).nodeValue());
279 property.appendChild(value);
280 tr.appendChild(property);
286 // move transitions after tracks
287 for (int i = 0; i < max; i++) {
288 tractor.insertAfter(transitions.at(0), QDomNode());
291 // Fix filters format
292 QDomNodeList entries = m_doc.elementsByTagName("entry");
293 max = entries.count();
294 for (int i = 0; i < max; i++) {
297 QDomNode m = entries.at(i).firstChild();
298 while (!m.isNull()) {
299 if (m.toElement().tagName() == "filter") {
300 QDomElement filt = m.toElement();
301 QDomNamedNodeMap attrs = filt.attributes();
302 QString current_id = filt.attribute("kdenlive_id");
303 if (current_id != last_id) {
305 last_id = current_id;
307 QDomElement e = m_doc.createElement("property");
308 e.setAttribute("name", "kdenlive_ix");
309 QDomText value = m_doc.createTextNode(QString::number(effectix));
310 e.appendChild(value);
312 for (int j = 0; j < attrs.count(); j++) {
313 QDomAttr a = attrs.item(j).toAttr();
315 kDebug() << " FILTER; adding :" << a.name() << ":" << a.value();
316 QDomElement e = m_doc.createElement("property");
317 e.setAttribute("name", a.name());
318 QDomText value = m_doc.createTextNode(a.value());
319 e.appendChild(value);
330 QDomNodeList filters = m_doc.elementsByTagName("filter");
331 max = filters.count();
334 for (int i = 0; i < max; i++) {
335 QDomElement filt = filters.at(i).toElement();
336 QDomNamedNodeMap attrs = filt.attributes();
337 QString current_id = filt.attribute("kdenlive_id");
338 if (current_id != last_id) {
340 last_id = current_id;
342 QDomElement e = m_doc.createElement("property");
343 e.setAttribute("name", "kdenlive_ix");
344 QDomText value = m_doc.createTextNode(QString::number(1));
345 e.appendChild(value);
347 for (int j = 0; j < attrs.count(); j++) {
348 QDomAttr a = attrs.item(j).toAttr();
350 kDebug() << " FILTER; adding :" << a.name() << ":" << a.value();
351 QDomElement e = m_doc.createElement("property");
352 e.setAttribute("name", a.name());
353 QDomText value = m_doc.createTextNode(a.value());
354 e.appendChild(value);
361 QDomNodeList producers = westley.toElement().elementsByTagName("producer");
362 max = producers.count();
363 for (int i = 0; i < max; i++) {
364 QDomElement prod = producers.at(i).toElement();
365 if (prod.attribute("mlt_service") == "framebuffer") {
366 QString slowmotionprod = prod.attribute("resource");
367 slowmotionprod.replace(':', '?');
368 kDebug() << "// FOUND WRONG SLOWMO, new: " << slowmotionprod;
369 prod.setAttribute("resource", slowmotionprod);
372 // move producers to correct place, markers to a global list, fix clip descriptions
373 QDomElement markers = m_doc.createElement("markers");
374 // This will get the xml producers:
375 producers = m_doc.elementsByTagName("producer");
376 max = producers.count();
377 for (int i = 0; i < max; i++) {
378 QDomElement prod = producers.at(0).toElement();
379 // add resource also as a property (to allow path correction in setNewResource())
380 // TODO: will it work with slowmotion? needs testing
381 /*if (!prod.attribute("resource").isEmpty()) {
382 QDomElement prop_resource = m_doc.createElement("property");
383 prop_resource.setAttribute("name", "resource");
384 QDomText resource = m_doc.createTextNode(prod.attribute("resource"));
385 prop_resource.appendChild(resource);
386 prod.appendChild(prop_resource);
388 QDomNode m = prod.firstChild();
390 if (m.toElement().tagName() == "markers") {
391 QDomNodeList prodchilds = m.childNodes();
392 int maxchild = prodchilds.count();
393 for (int k = 0; k < maxchild; k++) {
394 QDomElement mark = prodchilds.at(0).toElement();
395 mark.setAttribute("id", prod.attribute("id"));
396 markers.insertAfter(mark, QDomNode());
399 } else if (prod.attribute("type").toInt() == TEXT) {
400 // convert title clip
401 if (m.toElement().tagName() == "textclip") {
403 QDomElement titleclip = m.toElement();
404 QDomElement title = tdoc.createElement("kdenlivetitle");
405 tdoc.appendChild(title);
406 QDomNodeList objects = titleclip.childNodes();
407 int maxchild = objects.count();
408 for (int k = 0; k < maxchild; k++) {
410 QDomElement ob = objects.at(k).toElement();
411 if (ob.attribute("type") == "3") {
412 // text object - all of this goes into "xmldata"...
413 QDomElement item = tdoc.createElement("item");
414 item.setAttribute("z-index", ob.attribute("z"));
415 item.setAttribute("type", "QGraphicsTextItem");
416 QDomElement position = tdoc.createElement("position");
417 position.setAttribute("x", ob.attribute("x"));
418 position.setAttribute("y", ob.attribute("y"));
419 QDomElement content = tdoc.createElement("content");
420 content.setAttribute("font", ob.attribute("font_family"));
421 content.setAttribute("font-size", ob.attribute("font_size"));
422 content.setAttribute("font-bold", ob.attribute("bold"));
423 content.setAttribute("font-italic", ob.attribute("italic"));
424 content.setAttribute("font-underline", ob.attribute("underline"));
425 QString col = ob.attribute("color");
427 content.setAttribute("font-color", colorToString(c));
428 // todo: These fields are missing from the newly generated xmldata:
429 // transform, startviewport, endviewport, background
431 QDomText conttxt = tdoc.createTextNode(ob.attribute("text"));
432 content.appendChild(conttxt);
433 item.appendChild(position);
434 item.appendChild(content);
435 title.appendChild(item);
436 } else if (ob.attribute("type") == "5") {
438 QDomElement item = tdoc.createElement("item");
439 item.setAttribute("z-index", ob.attribute("z"));
440 item.setAttribute("type", "QGraphicsRectItem");
441 QDomElement position = tdoc.createElement("position");
442 position.setAttribute("x", ob.attribute("x"));
443 position.setAttribute("y", ob.attribute("y"));
444 QDomElement content = tdoc.createElement("content");
445 QString col = ob.attribute("color");
447 content.setAttribute("brushcolor", colorToString(c));
448 QString rect = "0,0,";
449 rect.append(ob.attribute("width"));
451 rect.append(ob.attribute("height"));
452 content.setAttribute("rect", rect);
453 item.appendChild(position);
454 item.appendChild(content);
455 title.appendChild(item);
458 prod.setAttribute("xmldata", tdoc.toString());
459 // mbd todo: This clearly does not work, as every title gets the same name - trying to leave it empty
460 // QStringList titleInfo = TitleWidget::getFreeTitleInfo(projectFolder());
461 // prod.setAttribute("titlename", titleInfo.at(0));
462 // prod.setAttribute("resource", titleInfo.at(1));
463 //kDebug()<<"TITLE DATA:\n"<<tdoc.toString();
465 } // End conversion of title clips.
467 } else if (m.isText()) {
468 QString comment = m.nodeValue();
469 if (!comment.isEmpty()) {
470 prod.setAttribute("description", comment);
475 int duration = prod.attribute("duration").toInt();
476 if (duration > 0) prod.setAttribute("out", QString::number(duration));
477 // The clip goes back in, but text clips should not go back in, at least not modified
478 westley.insertBefore(prod, QDomNode());
481 QDomNode westley0 = m_doc.elementsByTagName("westley").at(0);
482 if (!markers.firstChild().isNull()) westley0.appendChild(markers);
485 * Convert as much of the kdenlivedoc as possible. Use the producer in
486 * westley. First, remove the old stuff from westley, and add a new
487 * empty one. Also, track the max id in order to use it for the adding
490 int max_kproducer_id = 0;
491 westley0.removeChild(infoXmlNode);
492 QDomElement infoXml_new = m_doc.createElement("kdenlivedoc");
493 infoXml_new.setAttribute("profile", profile);
494 infoXml.setAttribute("position", startPos);
496 // Add all the producers that has a resource in westley
497 QDomElement westley_element = westley0.toElement();
498 if (westley_element.isNull()) {
499 kWarning() << "westley0 element in document was not a QDomElement - unable to add producers to new kdenlivedoc";
501 QDomNodeList wproducers = westley_element.elementsByTagName("producer");
502 int kmax = wproducers.count();
503 for (int i = 0; i < kmax; i++) {
504 QDomElement wproducer = wproducers.at(i).toElement();
505 if (wproducer.isNull()) {
506 kWarning() << "Found producer in westley0, that was not a QDomElement";
509 if (wproducer.attribute("id") == "black") continue;
510 // We have to do slightly different things, depending on the type
511 kDebug() << "Converting producer element with type" << wproducer.attribute("type");
512 if (wproducer.attribute("type").toInt() == TEXT) {
513 kDebug() << "Found TEXT element in producer" << endl;
514 QDomElement kproducer = wproducer.cloneNode(true).toElement();
515 kproducer.setTagName("kdenlive_producer");
516 infoXml_new.appendChild(kproducer);
518 * TODO: Perhaps needs some more changes here to
519 * "frequency", aspect ratio as a float, frame_size,
520 * channels, and later, resource and title name
523 QDomElement kproducer = m_doc.createElement("kdenlive_producer");
524 kproducer.setAttribute("id", wproducer.attribute("id"));
525 if (!wproducer.attribute("description").isEmpty())
526 kproducer.setAttribute("description", wproducer.attribute("description"));
527 kproducer.setAttribute("resource", wproducer.attribute("resource"));
528 kproducer.setAttribute("type", wproducer.attribute("type"));
529 // Testing fix for 358
530 if (!wproducer.attribute("aspect_ratio").isEmpty()) {
531 kproducer.setAttribute("aspect_ratio", wproducer.attribute("aspect_ratio"));
533 if (!wproducer.attribute("source_fps").isEmpty()) {
534 kproducer.setAttribute("fps", wproducer.attribute("source_fps"));
536 if (!wproducer.attribute("length").isEmpty()) {
537 kproducer.setAttribute("duration", wproducer.attribute("length"));
539 infoXml_new.appendChild(kproducer);
541 if (wproducer.attribute("id").toInt() > max_kproducer_id) {
542 max_kproducer_id = wproducer.attribute("id").toInt();
546 #define LOOKUP_FOLDER 1
549 * Look through all the folder elements of the old doc, for each folder,
550 * for each producer, get the id, look it up in the new doc, set the
551 * groupname and groupid. Note, this does not work at the moment - at
552 * least one folder shows up missing, and clips with no folder does not
555 //QDomElement infoXml_old = infoXmlNode.toElement();
556 if (!infoXml_old.isNull()) {
557 QDomNodeList folders = infoXml_old.elementsByTagName("folder");
558 int fsize = folders.size();
559 int groupId = max_kproducer_id + 1; // Start at +1 of max id of the kdenlive_producers
560 for (int i = 0; i < fsize; ++i) {
561 QDomElement folder = folders.at(i).toElement();
562 if (!folder.isNull()) {
563 QString groupName = folder.attribute("name");
564 kDebug() << "groupName: " << groupName << " with groupId: " << groupId;
565 QDomNodeList fproducers = folder.elementsByTagName("producer");
566 int psize = fproducers.size();
567 for (int j = 0; j < psize; ++j) {
568 QDomElement fproducer = fproducers.at(j).toElement();
569 if (!fproducer.isNull()) {
570 QString id = fproducer.attribute("id");
571 // This is not very effective, but compared to loading the clips, its a breeze
572 QDomNodeList kdenlive_producers = infoXml_new.elementsByTagName("kdenlive_producer");
573 int kpsize = kdenlive_producers.size();
574 for (int k = 0; k < kpsize; ++k) {
575 QDomElement kproducer = kdenlive_producers.at(k).toElement(); // Its an element for sure
576 if (id == kproducer.attribute("id")) {
577 // We do not check that it already is part of a folder
578 kproducer.setAttribute("groupid", groupId);
579 kproducer.setAttribute("groupname", groupName);
590 QDomNodeList elements = westley.childNodes();
591 max = elements.count();
592 for (int i = 0; i < max; i++) {
593 QDomElement prod = elements.at(0).toElement();
594 westley0.insertAfter(prod, QDomNode());
597 westley0.appendChild(infoXml_new);
599 westley0.removeChild(westley);
601 // adds <avfile /> information to <kdenlive_producer />
602 QDomNodeList kproducers = m_doc.elementsByTagName("kdenlive_producer");
603 QDomNodeList avfiles = infoXml_old.elementsByTagName("avfile");
604 kDebug() << "found" << avfiles.count() << "<avfile />s and" << kproducers.count() << "<kdenlive_producer />s";
605 for (int i = 0; i < avfiles.count(); ++i) {
606 QDomElement avfile = avfiles.at(i).toElement();
607 QDomElement kproducer;
609 kWarning() << "found an <avfile /> that is not a QDomElement";
611 QString id = avfile.attribute("id");
612 // this is horrible, must be rewritten, it's just for test
613 for (int j = 0; j < kproducers.count(); ++j) {
614 //kDebug() << "checking <kdenlive_producer /> with id" << kproducers.at(j).toElement().attribute("id");
615 if (kproducers.at(j).toElement().attribute("id") == id) {
616 kproducer = kproducers.at(j).toElement();
620 if (kproducer == QDomElement())
621 kWarning() << "no match for <avfile /> with id =" << id;
623 //kDebug() << "ready to set additional <avfile />'s attributes (id =" << id << ")";
624 kproducer.setAttribute("channels", avfile.attribute("channels"));
625 kproducer.setAttribute("duration", avfile.attribute("duration"));
626 kproducer.setAttribute("frame_size", avfile.attribute("width") + 'x' + avfile.attribute("height"));
627 kproducer.setAttribute("frequency", avfile.attribute("frequency"));
628 if (kproducer.attribute("description").isEmpty() && !avfile.attribute("description").isEmpty())
629 kproducer.setAttribute("description", avfile.attribute("description"));
633 infoXml = infoXml_new;
636 if (version <= 0.81) {
637 // Add the tracks information
638 QString tracksOrder = infoXml.attribute("tracks");
639 if (tracksOrder.isEmpty()) {
640 QDomNodeList tracks = m_doc.elementsByTagName("track");
641 for (int i = 0; i < tracks.count(); i++) {
642 QDomElement track = tracks.at(i).toElement();
643 if (track.attribute("producer") != "black_track") {
644 if (track.attribute("hide") == "video")
645 tracksOrder.append('a');
647 tracksOrder.append('v');
651 QDomElement tracksinfo = m_doc.createElement("tracksinfo");
652 for (int i = 0; i < tracksOrder.size(); i++) {
653 QDomElement trackinfo = m_doc.createElement("trackinfo");
654 if (tracksOrder.data()[i] == 'a') {
655 trackinfo.setAttribute("type", "audio");
656 trackinfo.setAttribute("blind", true);
658 trackinfo.setAttribute("blind", false);
659 trackinfo.setAttribute("mute", false);
660 trackinfo.setAttribute("locked", false);
661 tracksinfo.appendChild(trackinfo);
663 infoXml.appendChild(tracksinfo);
666 if (version <= 0.82) {
667 // Convert <westley />s in <mlt />s (MLT extreme makeover)
668 QDomNodeList westleyNodes = m_doc.elementsByTagName("westley");
669 for (int i = 0; i < westleyNodes.count(); i++) {
670 QDomElement westley = westleyNodes.at(i).toElement();
671 westley.setTagName("mlt");
675 if (version <= 0.83) {
676 // Replace point size with pixel size in text titles
677 if (m_doc.toString().contains("font-size")) {
678 QDomNodeList kproducerNodes = m_doc.elementsByTagName("kdenlive_producer");
679 for (int i = 0; i < kproducerNodes.count(); ++i) {
680 QDomElement kproducer = kproducerNodes.at(i).toElement();
681 if (kproducer.attribute("type").toInt() == TEXT) {
683 data.setContent(kproducer.attribute("xmldata"));
684 QDomNodeList items = data.firstChild().childNodes();
685 for (int j = 0; j < items.count(); ++j) {
686 if (items.at(j).attributes().namedItem("type").nodeValue() == "QGraphicsTextItem") {
687 QDomNamedNodeMap textProperties = items.at(j).namedItem("content").attributes();
688 if (textProperties.namedItem("font-pixel-size").isNull()) {
690 font.setPointSize(textProperties.namedItem("font-size").nodeValue().toInt());
691 QDomElement content = items.at(j).namedItem("content").toElement();
692 content.setAttribute("font-pixel-size", QFontInfo(font).pixelSize());
693 content.removeAttribute("font-size");
694 kproducer.setAttribute("xmldata", data.toString());
696 * You may be tempted to delete the preview file
697 * to force its recreation: bad idea (see
698 * http://www.kdenlive.org/mantis/view.php?id=749)
708 // The document has been converted: mark it as modified
709 infoXml.setAttribute("version", currentVersion);
715 QString DocumentValidator::colorToString(const QColor& c)
717 QString ret = "%1,%2,%3,%4";
718 ret = ret.arg(c.red()).arg(c.green()).arg(c.blue()).arg(c.alpha());
722 bool DocumentValidator::isProject() const
724 QDomNode infoXmlNode = m_doc.elementsByTagName("kdenlivedoc").at(0);
725 return !infoXmlNode.isNull();
728 bool DocumentValidator::isModified() const