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 "documentconvert.h"
22 #include "definitions.h"
25 #include <KMessageBox>
26 #include <KApplication>
31 DocumentConvert::DocumentConvert(QDomDocument doc):
37 bool DocumentConvert::doConvert(double version, const double current_version)
39 kDebug() << "Opening a document with version " << version;
41 if (version == current_version) return true;
43 if (version > current_version) {
44 kDebug() << "Unable to open document with version " << version;
45 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"));
49 // Opening a old Kdenlive document
50 if (version == 0.5 || version == 0.7) {
51 KMessageBox::sorry(kapp->activeWindow(), i18n("This project type is unsupported (version %1) and can't be loaded.", version), i18n("Unable to open project"));
52 kDebug() << "Unable to open document with version " << version;
53 // TODO: convert 0.7 (0.5?) files to the new document format.
59 if (version == 0.82) {
64 if (version == 0.81) {
65 // Add correct tracks info
66 QDomNode kdenlivedoc = m_doc.elementsByTagName("kdenlivedoc").at(0);
67 QDomElement infoXml = kdenlivedoc.toElement();
68 infoXml.setAttribute("version", current_version);
69 QString currentTrackOrder = infoXml.attribute("tracks");
70 QDomElement tracksinfo = m_doc.createElement("tracksinfo");
71 for (int i = 0; i < currentTrackOrder.size(); i++) {
72 QDomElement trackinfo = m_doc.createElement("trackinfo");
73 if (currentTrackOrder.data()[i] == 'a') {
74 trackinfo.setAttribute("type", "audio");
75 trackinfo.setAttribute("blind", true);
76 } else trackinfo.setAttribute("blind", false);
77 trackinfo.setAttribute("mute", false);
78 trackinfo.setAttribute("locked", false);
79 tracksinfo.appendChild(trackinfo);
81 infoXml.appendChild(tracksinfo);
87 // Add the tracks information
88 QDomNodeList tracks = m_doc.elementsByTagName("track");
89 int max = tracks.count();
91 QDomNode kdenlivedoc = m_doc.elementsByTagName("kdenlivedoc").at(0);
92 QDomElement infoXml = kdenlivedoc.toElement();
93 infoXml.setAttribute("version", current_version);
94 QDomElement tracksinfo = m_doc.createElement("tracksinfo");
96 for (int i = 0; i < max; i++) {
97 QDomElement trackinfo = m_doc.createElement("trackinfo");
98 QDomElement t = tracks.at(i).toElement();
99 if (t.attribute("hide") == "video") {
100 trackinfo.setAttribute("type", "audio");
101 trackinfo.setAttribute("blind", true);
102 } else trackinfo.setAttribute("blind", false);
103 trackinfo.setAttribute("mute", false);
104 trackinfo.setAttribute("locked", false);
105 if (t.attribute("producer") != "black_track") tracksinfo.appendChild(trackinfo);
107 infoXml.appendChild(tracksinfo);
112 QDomNode westley = m_doc.elementsByTagName("westley").at(1);
113 QDomNode tractor = m_doc.elementsByTagName("tractor").at(0);
114 QDomNode kdenlivedoc = m_doc.elementsByTagName("kdenlivedoc").at(0);
115 QDomElement kdenlivedoc_old = kdenlivedoc.cloneNode(true).toElement(); // Needed for folders
116 QDomElement infoXml = kdenlivedoc.toElement();
117 infoXml.setAttribute("version", current_version);
118 QDomNode multitrack = m_doc.elementsByTagName("multitrack").at(0);
119 QDomNodeList playlists = m_doc.elementsByTagName("playlist");
121 QDomNode props = m_doc.elementsByTagName("properties").at(0).toElement();
122 QString profile = props.toElement().attribute("videoprofile");
123 int startPos = props.toElement().attribute("timeline_position").toInt();
124 infoXml.setAttribute("position", startPos);
125 if (profile == "dv_wide") profile = "dv_pal_wide";
127 // move playlists outside of tractor and add the tracks instead
128 int max = playlists.count();
129 if (westley.isNull())
130 westley = m_doc.elementsByTagName("westley").at(1);
131 if (westley.isNull()) {
132 westley = m_doc.createElement("mlt");
133 m_doc.documentElement().appendChild(westley);
135 if (tractor.isNull()) {
136 kDebug() << "// NO MLT PLAYLIST, building empty one";
137 QDomElement blank_tractor = m_doc.createElement("tractor");
138 westley.appendChild(blank_tractor);
139 QDomElement blank_playlist = m_doc.createElement("playlist");
140 blank_playlist.setAttribute("id", "black_track");
141 westley.insertBefore(blank_playlist, QDomNode());
142 QDomElement blank_track = m_doc.createElement("track");
143 blank_track.setAttribute("producer", "black_track");
144 blank_tractor.appendChild(blank_track);
146 QDomNodeList kdenlivetracks = m_doc.elementsByTagName("kdenlivetrack");
147 for (int i = 0; i < kdenlivetracks.count(); i++) {
148 blank_playlist = m_doc.createElement("playlist");
149 blank_playlist.setAttribute("id", "playlist" + QString::number(i));
150 westley.insertBefore(blank_playlist, QDomNode());
151 blank_track = m_doc.createElement("track");
152 blank_track.setAttribute("producer", "playlist" + QString::number(i));
153 blank_tractor.appendChild(blank_track);
154 if (kdenlivetracks.at(i).toElement().attribute("cliptype") == "Sound") {
155 blank_playlist.setAttribute("hide", "video");
156 blank_track.setAttribute("hide", "video");
159 } else for (int i = 0; i < max; i++) {
160 QDomNode n = playlists.at(i);
161 westley.insertBefore(n, QDomNode());
162 QDomElement pl = n.toElement();
163 QDomElement track = m_doc.createElement("track");
164 QString trackType = pl.attribute("hide");
165 if (!trackType.isEmpty())
166 track.setAttribute("hide", trackType);
167 QString playlist_id = pl.attribute("id");
168 if (playlist_id.isEmpty()) {
169 playlist_id = "black_track";
170 pl.setAttribute("id", playlist_id);
172 track.setAttribute("producer", playlist_id);
173 //tractor.appendChild(track);
174 #define KEEP_TRACK_ORDER 1
175 #ifdef KEEP_TRACK_ORDER
176 tractor.insertAfter(track, QDomNode());
178 // Insert the new track in an order that hopefully matches the 3 video, then 2 audio tracks of Kdenlive 0.7.0
179 // insertion sort - O( tracks*tracks )
180 // Note, this breaks _all_ transitions - but you can move them up and down afterwards.
181 QDomElement tractor_elem = tractor.toElement();
182 if (! tractor_elem.isNull()) {
183 QDomNodeList tracks = tractor_elem.elementsByTagName("track");
184 int size = tracks.size();
186 tractor.insertAfter(track, QDomNode());
188 bool inserted = false;
189 for (int i = 0; i < size; ++i) {
190 QDomElement track_elem = tracks.at(i).toElement();
191 if (track_elem.isNull()) {
192 tractor.insertAfter(track, QDomNode());
196 kDebug() << "playlist_id: " << playlist_id << " producer:" << track_elem.attribute("producer");
197 if (playlist_id < track_elem.attribute("producer")) {
198 tractor.insertBefore(track, track_elem);
204 // Reach here, no insertion, insert last
206 tractor.insertAfter(track, QDomNode());
210 kWarning() << "tractor was not a QDomElement";
211 tractor.insertAfter(track, QDomNode());
215 tractor.removeChild(multitrack);
217 // audio track mixing transitions should not be added to track view, so add required attribute
218 QDomNodeList transitions = m_doc.elementsByTagName("transition");
219 max = transitions.count();
220 for (int i = 0; i < max; i++) {
221 QDomElement tr = transitions.at(i).toElement();
222 if (tr.attribute("combine") == "1" && tr.attribute("mlt_service") == "mix") {
223 QDomElement property = m_doc.createElement("property");
224 property.setAttribute("name", "internal_added");
225 QDomText value = m_doc.createTextNode("237");
226 property.appendChild(value);
227 tr.appendChild(property);
228 property = m_doc.createElement("property");
229 property.setAttribute("name", "mlt_service");
230 value = m_doc.createTextNode("mix");
231 property.appendChild(value);
232 tr.appendChild(property);
234 // convert transition
235 QDomNamedNodeMap attrs = tr.attributes();
236 for (int j = 0; j < attrs.count(); j++) {
237 QString attrName = attrs.item(j).nodeName();
238 if (attrName != "in" && attrName != "out" && attrName != "id") {
239 QDomElement property = m_doc.createElement("property");
240 property.setAttribute("name", attrName);
241 QDomText value = m_doc.createTextNode(attrs.item(j).nodeValue());
242 property.appendChild(value);
243 tr.appendChild(property);
249 // move transitions after tracks
250 for (int i = 0; i < max; i++) {
251 tractor.insertAfter(transitions.at(0), QDomNode());
254 // Fix filters format
255 QDomNodeList entries = m_doc.elementsByTagName("entry");
256 max = entries.count();
257 for (int i = 0; i < max; i++) {
260 QDomNode m = entries.at(i).firstChild();
261 while (!m.isNull()) {
262 if (m.toElement().tagName() == "filter") {
263 QDomElement filt = m.toElement();
264 QDomNamedNodeMap attrs = filt.attributes();
265 QString current_id = filt.attribute("kdenlive_id");
266 if (current_id != last_id) {
268 last_id = current_id;
270 QDomElement e = m_doc.createElement("property");
271 e.setAttribute("name", "kdenlive_ix");
272 QDomText value = m_doc.createTextNode(QString::number(effectix));
273 e.appendChild(value);
275 for (int j = 0; j < attrs.count(); j++) {
276 QDomAttr a = attrs.item(j).toAttr();
278 kDebug() << " FILTER; adding :" << a.name() << ":" << a.value();
279 QDomElement e = m_doc.createElement("property");
280 e.setAttribute("name", a.name());
281 QDomText value = m_doc.createTextNode(a.value());
282 e.appendChild(value);
293 QDomNodeList filters = m_doc.elementsByTagName("filter");
294 max = filters.count();
297 for (int i = 0; i < max; i++) {
298 QDomElement filt = filters.at(i).toElement();
299 QDomNamedNodeMap attrs = filt.attributes();
300 QString current_id = filt.attribute("kdenlive_id");
301 if (current_id != last_id) {
303 last_id = current_id;
305 QDomElement e = m_doc.createElement("property");
306 e.setAttribute("name", "kdenlive_ix");
307 QDomText value = m_doc.createTextNode(QString::number(1));
308 e.appendChild(value);
310 for (int j = 0; j < attrs.count(); j++) {
311 QDomAttr a = attrs.item(j).toAttr();
313 kDebug() << " FILTER; adding :" << a.name() << ":" << a.value();
314 QDomElement e = m_doc.createElement("property");
315 e.setAttribute("name", a.name());
316 QDomText value = m_doc.createTextNode(a.value());
317 e.appendChild(value);
324 QDomNodeList producers = westley.toElement().elementsByTagName("producer");
325 max = producers.count();
326 for (int i = 0; i < max; i++) {
327 QDomElement prod = producers.at(i).toElement();
328 if (prod.attribute("mlt_service") == "framebuffer") {
329 QString slowmotionprod = prod.attribute("resource");
330 slowmotionprod.replace(':', '?');
331 kDebug() << "// FOUND WRONG SLOWMO, new: " << slowmotionprod;
332 prod.setAttribute("resource", slowmotionprod);
335 // move producers to correct place, markers to a global list, fix clip descriptions
336 QDomElement markers = m_doc.createElement("markers");
337 // This will get the xml producers:
338 producers = m_doc.elementsByTagName("producer");
339 max = producers.count();
340 for (int i = 0; i < max; i++) {
341 QDomElement prod = producers.at(0).toElement();
342 // add resource also as a property (to allow path correction in setNewResource())
343 // TODO: will it work with slowmotion? needs testing
344 /*if (!prod.attribute("resource").isEmpty()) {
345 QDomElement prop_resource = m_doc.createElement("property");
346 prop_resource.setAttribute("name", "resource");
347 QDomText resource = m_doc.createTextNode(prod.attribute("resource"));
348 prop_resource.appendChild(resource);
349 prod.appendChild(prop_resource);
351 QDomNode m = prod.firstChild();
353 if (m.toElement().tagName() == "markers") {
354 QDomNodeList prodchilds = m.childNodes();
355 int maxchild = prodchilds.count();
356 for (int k = 0; k < maxchild; k++) {
357 QDomElement mark = prodchilds.at(0).toElement();
358 mark.setAttribute("id", prod.attribute("id"));
359 markers.insertAfter(mark, QDomNode());
362 } else if (prod.attribute("type").toInt() == TEXT) {
363 // convert title clip
364 if (m.toElement().tagName() == "textclip") {
366 QDomElement titleclip = m.toElement();
367 QDomElement title = tdoc.createElement("kdenlivetitle");
368 tdoc.appendChild(title);
369 QDomNodeList objects = titleclip.childNodes();
370 int maxchild = objects.count();
371 for (int k = 0; k < maxchild; k++) {
373 QDomElement ob = objects.at(k).toElement();
374 if (ob.attribute("type") == "3") {
375 // text object - all of this goes into "xmldata"...
376 QDomElement item = tdoc.createElement("item");
377 item.setAttribute("z-index", ob.attribute("z"));
378 item.setAttribute("type", "QGraphicsTextItem");
379 QDomElement position = tdoc.createElement("position");
380 position.setAttribute("x", ob.attribute("x"));
381 position.setAttribute("y", ob.attribute("y"));
382 QDomElement content = tdoc.createElement("content");
383 content.setAttribute("font", ob.attribute("font_family"));
384 content.setAttribute("font-size", ob.attribute("font_size"));
385 content.setAttribute("font-bold", ob.attribute("bold"));
386 content.setAttribute("font-italic", ob.attribute("italic"));
387 content.setAttribute("font-underline", ob.attribute("underline"));
388 QString col = ob.attribute("color");
390 content.setAttribute("font-color", colorToString(c));
391 // todo: These fields are missing from the newly generated xmldata:
392 // transform, startviewport, endviewport, background
394 QDomText conttxt = tdoc.createTextNode(ob.attribute("text"));
395 content.appendChild(conttxt);
396 item.appendChild(position);
397 item.appendChild(content);
398 title.appendChild(item);
399 } else if (ob.attribute("type") == "5") {
401 QDomElement item = tdoc.createElement("item");
402 item.setAttribute("z-index", ob.attribute("z"));
403 item.setAttribute("type", "QGraphicsRectItem");
404 QDomElement position = tdoc.createElement("position");
405 position.setAttribute("x", ob.attribute("x"));
406 position.setAttribute("y", ob.attribute("y"));
407 QDomElement content = tdoc.createElement("content");
408 QString col = ob.attribute("color");
410 content.setAttribute("brushcolor", colorToString(c));
411 QString rect = "0,0,";
412 rect.append(ob.attribute("width"));
414 rect.append(ob.attribute("height"));
415 content.setAttribute("rect", rect);
416 item.appendChild(position);
417 item.appendChild(content);
418 title.appendChild(item);
421 prod.setAttribute("xmldata", tdoc.toString());
422 // mbd todo: This clearly does not work, as every title gets the same name - trying to leave it empty
423 // QStringList titleInfo = TitleWidget::getFreeTitleInfo(projectFolder());
424 // prod.setAttribute("titlename", titleInfo.at(0));
425 // prod.setAttribute("resource", titleInfo.at(1));
426 //kDebug()<<"TITLE DATA:\n"<<tdoc.toString();
428 } // End conversion of title clips.
430 } else if (m.isText()) {
431 QString comment = m.nodeValue();
432 if (!comment.isEmpty()) {
433 prod.setAttribute("description", comment);
438 int duration = prod.attribute("duration").toInt();
439 if (duration > 0) prod.setAttribute("out", QString::number(duration));
440 // The clip goes back in, but text clips should not go back in, at least not modified
441 westley.insertBefore(prod, QDomNode());
445 QDomNode westley0 = m_doc.elementsByTagName("westley").at(0);
446 if (!markers.firstChild().isNull()) westley0.appendChild(markers);
449 // Convert as much of the kdenlivedoc as possible. Use the producer in westley
450 // First, remove the old stuff from westley, and add a new empty one
451 // Also, track the max id in order to use it for the adding of groups/folders
452 int max_kproducer_id = 0;
453 westley0.removeChild(kdenlivedoc);
454 QDomElement kdenlivedoc_new = m_doc.createElement("kdenlivedoc");
455 kdenlivedoc_new.setAttribute("profile", profile);
458 QDomNodeList tracks = m_doc.elementsByTagName("track");
459 max = tracks.count();
460 QDomElement tracksinfo = m_doc.createElement("tracksinfo");
461 for (int i = 0; i < max; i++) {
462 QDomElement trackinfo = m_doc.createElement("trackinfo");
463 QDomElement t = tracks.at(i).toElement();
464 if (t.attribute("hide") == "video") {
465 trackinfo.setAttribute("type", "audio");
466 trackinfo.setAttribute("blind", true);
467 } else trackinfo.setAttribute("blind", false);
468 trackinfo.setAttribute("mute", false);
469 trackinfo.setAttribute("locked", false);
470 if (t.attribute("producer") != "black_track") tracksinfo.appendChild(trackinfo);
472 kdenlivedoc_new.appendChild(tracksinfo);
474 // Add all the producers that has a resource in westley
475 QDomElement westley_element = westley0.toElement();
476 if (westley_element.isNull()) {
477 kWarning() << "westley0 element in document was not a QDomElement - unable to add producers to new kdenlivedoc";
479 QDomNodeList wproducers = westley_element.elementsByTagName("producer");
480 int kmax = wproducers.count();
481 for (int i = 0; i < kmax; i++) {
482 QDomElement wproducer = wproducers.at(i).toElement();
483 if (wproducer.isNull()) {
484 kWarning() << "Found producer in westley0, that was not a QDomElement";
487 if (wproducer.attribute("id") == "black") continue;
488 // We have to do slightly different things, depending on the type
489 kDebug() << "Converting producer element with type" << wproducer.attribute("type");
490 if (wproducer.attribute("type").toInt() == TEXT) {
491 kDebug() << "Found TEXT element in producer" << endl;
492 QDomElement kproducer = wproducer.cloneNode(true).toElement();
493 kproducer.setTagName("kdenlive_producer");
494 kdenlivedoc_new.appendChild(kproducer);
495 // TODO: Perhaps needs some more changes here to "frequency", aspect ratio as a float, frame_size, channels, and later, ressource and title name
497 QDomElement kproducer = m_doc.createElement("kdenlive_producer");
498 kproducer.setAttribute("id", wproducer.attribute("id"));
499 if (!wproducer.attribute("description").isEmpty())
500 kproducer.setAttribute("description", wproducer.attribute("description"));
501 kproducer.setAttribute("resource", wproducer.attribute("resource"));
502 kproducer.setAttribute("type", wproducer.attribute("type"));
503 // Testing fix for 358
504 if (!wproducer.attribute("aspect_ratio").isEmpty()) {
505 kproducer.setAttribute("aspect_ratio", wproducer.attribute("aspect_ratio"));
507 if (!wproducer.attribute("source_fps").isEmpty()) {
508 kproducer.setAttribute("fps", wproducer.attribute("source_fps"));
510 if (!wproducer.attribute("length").isEmpty()) {
511 kproducer.setAttribute("duration", wproducer.attribute("length"));
513 kdenlivedoc_new.appendChild(kproducer);
515 if (wproducer.attribute("id").toInt() > max_kproducer_id) {
516 max_kproducer_id = wproducer.attribute("id").toInt();
520 #define LOOKUP_FOLDER 1
522 // Look through all the folder elements of the old doc, for each folder, for each producer,
523 // get the id, look it up in the new doc, set the groupname and groupid
524 // Note, this does not work at the moment - at least one folders shows up missing, and clips with no folder
526 // QDomElement kdenlivedoc_old = kdenlivedoc.toElement();
527 if (!kdenlivedoc_old.isNull()) {
528 QDomNodeList folders = kdenlivedoc_old.elementsByTagName("folder");
529 int fsize = folders.size();
530 int groupId = max_kproducer_id + 1; // Start at +1 of max id of the kdenlive_producers
531 for (int i = 0; i < fsize; ++i) {
532 QDomElement folder = folders.at(i).toElement();
533 if (!folder.isNull()) {
534 QString groupName = folder.attribute("name");
535 kDebug() << "groupName: " << groupName << " with groupId: " << groupId;
536 QDomNodeList fproducers = folder.elementsByTagName("producer");
537 int psize = fproducers.size();
538 for (int j = 0; j < psize; ++j) {
539 QDomElement fproducer = fproducers.at(j).toElement();
540 if (!fproducer.isNull()) {
541 QString id = fproducer.attribute("id");
542 // This is not very effective, but compared to loading the clips, its a breeze
543 QDomNodeList kdenlive_producers = kdenlivedoc_new.elementsByTagName("kdenlive_producer");
544 int kpsize = kdenlive_producers.size();
545 for (int k = 0; k < kpsize; ++k) {
546 QDomElement kproducer = kdenlive_producers.at(k).toElement(); // Its an element for sure
547 if (id == kproducer.attribute("id")) {
548 // We do not check that it already is part of a folder
549 kproducer.setAttribute("groupid", groupId);
550 kproducer.setAttribute("groupname", groupName);
561 westley0.appendChild(kdenlivedoc_new);
563 QDomNodeList elements = westley.childNodes();
564 max = elements.count();
565 for (int i = 0; i < max; i++) {
566 QDomElement prod = elements.at(0).toElement();
567 westley0.insertAfter(prod, QDomNode());
570 westley0.removeChild(westley);
572 // experimental and probably slow
573 // adds <avfile /> information to <kdenlive_producer />
574 QDomNodeList kproducers = m_doc.elementsByTagName("kdenlive_producer");
575 QDomNodeList avfiles = kdenlivedoc_old.elementsByTagName("avfile");
576 kDebug() << "found" << avfiles.count() << "<avfile />s and" << kproducers.count() << "<kdenlive_producer />s";
577 for (int i = 0; i < avfiles.count(); ++i) {
578 QDomElement avfile = avfiles.at(i).toElement();
579 QDomElement kproducer;
581 kWarning() << "found an <avfile /> that is not a QDomElement";
583 QString id = avfile.attribute("id");
584 // this is horrible, must be rewritten, it's just for test
585 for (int j = 0; j < kproducers.count(); ++j) {
586 //kDebug() << "checking <kdenlive_producer /> with id" << kproducers.at(j).toElement().attribute("id");
587 if (kproducers.at(j).toElement().attribute("id") == id) {
588 kproducer = kproducers.at(j).toElement();
592 if (kproducer == QDomElement())
593 kWarning() << "no match for <avfile /> with id =" << id;
595 //kDebug() << "ready to set additional <avfile />'s attributes (id =" << id << ")";
596 kproducer.setAttribute("channels", avfile.attribute("channels"));
597 kproducer.setAttribute("duration", avfile.attribute("duration"));
598 kproducer.setAttribute("frame_size", avfile.attribute("width") + 'x' + avfile.attribute("height"));
599 kproducer.setAttribute("frequency", avfile.attribute("frequency"));
600 if (kproducer.attribute("description").isEmpty() && !avfile.attribute("description").isEmpty())
601 kproducer.setAttribute("description", avfile.attribute("description"));
606 /*kDebug() << "///////////////// CONVERTED DOC:";
607 kDebug() << m_doc.toString();
608 kDebug() << "///////////////// END CONVERTED DOC:";
610 QFile file("converted.kdenlive");
611 if (file.open(QIODevice::WriteOnly)) {
612 QTextStream stream(&file);
613 stream << m_doc.toString().toUtf8();
616 kDebug() << "Unable to dump file to converted.kdenlive";
619 //kDebug() << "///////////////// END CONVERTED DOC:";
624 QString DocumentConvert::colorToString(const QColor& c)
626 QString ret = "%1,%2,%3,%4";
627 ret = ret.arg(c.red()).arg(c.green()).arg(c.blue()).arg(c.alpha());
631 bool DocumentConvert::isModified() const
636 void DocumentConvert::convertToMelt()
638 QDomNodeList list = m_doc.elementsByTagName("westley");
639 int max = list.count();
640 for (int i = 0; i < max; i++) {
641 QDomElement elem = list.at(i).toElement();
642 elem.setTagName("mlt");