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) {
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"));
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"));
149 QDomNode infoXmlNode = m_doc.elementsByTagName("kdenlivedoc").at(0);
150 QDomElement infoXml = infoXmlNode.toElement();
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");
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";
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);
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);
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");
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);
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());
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();
222 tractor.insertAfter(track, QDomNode());
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());
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);
240 // Reach here, no insertion, insert last
242 tractor.insertAfter(track, QDomNode());
246 kWarning() << "tractor was not a QDomElement";
247 tractor.insertAfter(track, QDomNode());
251 tractor.removeChild(multitrack);
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);
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);
285 // move transitions after tracks
286 for (int i = 0; i < max; i++) {
287 tractor.insertAfter(transitions.at(0), QDomNode());
290 // Fix filters format
291 QDomNodeList entries = m_doc.elementsByTagName("entry");
292 max = entries.count();
293 for (int i = 0; i < max; i++) {
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) {
304 last_id = current_id;
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);
311 for (int j = 0; j < attrs.count(); j++) {
312 QDomAttr a = attrs.item(j).toAttr();
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);
329 QDomNodeList filters = m_doc.elementsByTagName("filter");
330 max = filters.count();
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) {
339 last_id = current_id;
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);
346 for (int j = 0; j < attrs.count(); j++) {
347 QDomAttr a = attrs.item(j).toAttr();
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);
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);
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);
387 QDomNode m = prod.firstChild();
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());
398 } else if (prod.attribute("type").toInt() == TEXT) {
399 // convert title clip
400 if (m.toElement().tagName() == "textclip") {
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++) {
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");
426 content.setAttribute("font-color", colorToString(c));
427 // todo: These fields are missing from the newly generated xmldata:
428 // transform, startviewport, endviewport, background
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") {
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");
446 content.setAttribute("brushcolor", colorToString(c));
447 QString rect = "0,0,";
448 rect.append(ob.attribute("width"));
450 rect.append(ob.attribute("height"));
451 content.setAttribute("rect", rect);
452 item.appendChild(position);
453 item.appendChild(content);
454 title.appendChild(item);
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();
464 } // End conversion of title clips.
466 } else if (m.isText()) {
467 QString comment = m.nodeValue();
468 if (!comment.isEmpty()) {
469 prod.setAttribute("description", comment);
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());
480 QDomNode westley0 = m_doc.elementsByTagName("westley").at(0);
481 if (!markers.firstChild().isNull()) westley0.appendChild(markers);
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
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);
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";
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";
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);
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
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"));
532 if (!wproducer.attribute("source_fps").isEmpty()) {
533 kproducer.setAttribute("fps", wproducer.attribute("source_fps"));
535 if (!wproducer.attribute("length").isEmpty()) {
536 kproducer.setAttribute("duration", wproducer.attribute("length"));
538 infoXml_new.appendChild(kproducer);
540 if (wproducer.attribute("id").toInt() > max_kproducer_id) {
541 max_kproducer_id = wproducer.attribute("id").toInt();
545 #define LOOKUP_FOLDER 1
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
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);
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());
596 westley0.appendChild(infoXml_new);
598 westley0.removeChild(westley);
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;
608 kWarning() << "found an <avfile /> that is not a QDomElement";
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();
619 if (kproducer == QDomElement())
620 kWarning() << "no match for <avfile /> with id =" << id;
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"));
632 infoXml = infoXml_new;
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');
646 tracksOrder.append('v');
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);
657 trackinfo.setAttribute("blind", false);
658 trackinfo.setAttribute("mute", false);
659 trackinfo.setAttribute("locked", false);
660 tracksinfo.appendChild(trackinfo);
662 infoXml.appendChild(tracksinfo);
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");
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;
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) {
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) {
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());
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)
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);
724 // The document has been converted: mark it as modified
725 infoXml.setAttribute("version", currentVersion);
731 QString DocumentValidator::colorToString(const QColor& c)
733 QString ret = "%1,%2,%3,%4";
734 ret = ret.arg(c.red()).arg(c.green()).arg(c.blue()).arg(c.alpha());
738 bool DocumentValidator::isProject() const
740 QDomNode infoXmlNode = m_doc.elementsByTagName("kdenlivedoc").at(0);
741 return !infoXmlNode.isNull();
744 bool DocumentValidator::isModified() const