]> git.sesse.net Git - kdenlive/blob - src/titledocument.cpp
load and save outline values
[kdenlive] / src / titledocument.cpp
1 /***************************************************************************
2                           titledocument.h  -  description
3                              -------------------
4     begin                : Feb 28 2008
5     copyright            : (C) 2008 by Marco Gittler
6     email                : g.marco@freenet.de
7  ***************************************************************************/
8
9 /***************************************************************************
10  *                                                                         *
11  *   This program is free software; you can redistribute it and/or modify  *
12  *   it under the terms of the GNU General Public License as published by  *
13  *   the Free Software Foundation; either version 2 of the License, or     *
14  *   (at your option) any later version.                                   *
15  *                                                                         *
16  ***************************************************************************/
17
18 #include "titledocument.h"
19
20 #include <KDebug>
21 #include <KTemporaryFile>
22 #include <kio/netaccess.h>
23 #include <KApplication>
24 #include <KLocale>
25 #include <KMessageBox>
26
27 #include <QGraphicsScene>
28 #include <QDomElement>
29 #include <QGraphicsItem>
30 #include <QGraphicsRectItem>
31 #include <QGraphicsTextItem>
32 #include <QGraphicsSvgItem>
33 #include <QFontInfo>
34 #include <QFile>
35 #include <QTextCursor>
36
37 #if QT_VERSION >= 0x040600
38 #include <QGraphicsEffect>
39 #include <QGraphicsBlurEffect>
40 #include <QGraphicsDropShadowEffect>
41 #endif
42
43 TitleDocument::TitleDocument()
44 {
45     m_scene = NULL;
46 }
47
48 void TitleDocument::setScene(QGraphicsScene* _scene, int width, int height)
49 {
50     m_scene = _scene;
51     m_width = width;
52     m_height = height;
53 }
54
55 QDomDocument TitleDocument::xml(QGraphicsRectItem* startv, QGraphicsRectItem* endv)
56 {
57     QDomDocument doc;
58
59     QDomElement main = doc.createElement("kdenlivetitle");
60     main.setAttribute("width", m_width);
61     main.setAttribute("height", m_height);
62     doc.appendChild(main);
63
64     foreach(QGraphicsItem* item, m_scene->items()) {
65         QDomElement e = doc.createElement("item");
66         QDomElement content = doc.createElement("content");
67         QFont font;
68         QGraphicsTextItem *t;
69
70         switch (item->type()) {
71         case 7:
72             e.setAttribute("type", "QGraphicsPixmapItem");
73             content.setAttribute("url", item->data(Qt::UserRole).toString());
74             break;
75         case 13:
76             e.setAttribute("type", "QGraphicsSvgItem");
77             content.setAttribute("url", item->data(Qt::UserRole).toString());
78             break;
79         case 3:
80             e.setAttribute("type", "QGraphicsRectItem");
81             content.setAttribute("rect", rectFToString(((QGraphicsRectItem*)item)->rect().normalized()));
82             content.setAttribute("pencolor", colorToString(((QGraphicsRectItem*)item)->pen().color()));
83             content.setAttribute("penwidth", ((QGraphicsRectItem*)item)->pen().width());
84             content.setAttribute("brushcolor", colorToString(((QGraphicsRectItem*)item)->brush().color()));
85             break;
86         case 8:
87             e.setAttribute("type", "QGraphicsTextItem");
88             t = static_cast<QGraphicsTextItem *>(item);
89             // Don't save empty text nodes
90             if (t->toPlainText().simplified().isEmpty()) continue;
91             //content.appendChild(doc.createTextNode(((QGraphicsTextItem*)item)->toHtml()));
92             content.appendChild(doc.createTextNode(t->toPlainText()));
93             font = t->font();
94             content.setAttribute("font", font.family());
95             content.setAttribute("font-weight", font.weight());
96             content.setAttribute("font-pixel-size", font.pixelSize());
97             content.setAttribute("font-italic", font.italic());
98             content.setAttribute("font-underline", font.underline());
99
100             {
101
102                 QTextCursor cursor(t->document());
103                 cursor.select(QTextCursor::Document);
104                 QColor fontcolor = cursor.charFormat().foreground().color();
105                 content.setAttribute("font-color", colorToString(fontcolor));
106                 content.setAttribute("font-outline", t->data(101).toDouble());
107                 content.setAttribute("font-outline-color", t->data(102).toString());
108             }
109
110             if (!t->data(100).isNull()) {
111                 QStringList effectParams = t->data(100).toStringList();
112                 QString effectName = effectParams.takeFirst();
113                 content.setAttribute(effectName, effectParams.join(";"));
114             }
115
116             // Only save when necessary.
117             if (t->data(OriginXLeft).toInt() == AxisInverted) {
118                 content.setAttribute("kdenlive-axis-x-inverted", t->data(OriginXLeft).toInt());
119             }
120             if (t->data(OriginYTop).toInt() == AxisInverted) {
121                 content.setAttribute("kdenlive-axis-y-inverted", t->data(OriginYTop).toInt());
122             }
123             if (t->textWidth() != -1) {
124                 content.setAttribute("alignment", t->textCursor().blockFormat().alignment());
125             }
126             break;
127         default:
128             continue;
129         }
130
131         // position
132         QDomElement pos = doc.createElement("position");
133         pos.setAttribute("x", item->pos().x());
134         pos.setAttribute("y", item->pos().y());
135         QTransform transform = item->transform();
136         QDomElement tr = doc.createElement("transform");
137         tr.appendChild(doc.createTextNode(
138                            QString("%1,%2,%3,%4,%5,%6,%7,%8,%9").arg(
139                                transform.m11()).arg(transform.m12()).arg(transform.m13()).arg(transform.m21()).arg(transform.m22()).arg(transform.m23()).arg(transform.m31()).arg(transform.m32()).arg(transform.m33())
140                        )
141                       );
142         e.setAttribute("z-index", item->zValue());
143         pos.appendChild(tr);
144
145 #if QT_VERSION >= 0x040600
146         // effects
147         QGraphicsEffect *eff = item->graphicsEffect();
148         if (eff) {
149             QGraphicsBlurEffect *blur = static_cast <QGraphicsBlurEffect *>(eff);
150             QDomElement effect = doc.createElement("effect");
151             if (blur) {
152                 effect.setAttribute("type", "blur");
153                 effect.setAttribute("blurradius", blur->blurRadius());
154             } else {
155                 QGraphicsDropShadowEffect *shadow = static_cast <QGraphicsDropShadowEffect *>(eff);
156                 if (shadow) {
157                     effect.setAttribute("type", "shadow");
158                     effect.setAttribute("blurradius", shadow->blurRadius());
159                     effect.setAttribute("xoffset", shadow->xOffset());
160                     effect.setAttribute("yoffset", shadow->yOffset());
161                 }
162             }
163             e.appendChild(effect);
164         }
165 #endif
166
167         e.appendChild(pos);
168         e.appendChild(content);
169         if (item->zValue() > -1000) main.appendChild(e);
170     }
171     if (startv && endv) {
172         QDomElement endp = doc.createElement("endviewport");
173         QDomElement startp = doc.createElement("startviewport");
174         QRectF r(endv->pos().x(), endv->pos().y(), endv->rect().width(), endv->rect().height());
175         endp.setAttribute("rect", rectFToString(r));
176         QRectF r2(startv->pos().x(), startv->pos().y(), startv->rect().width(), startv->rect().height());
177         startp.setAttribute("rect", rectFToString(r2));
178
179         main.appendChild(startp);
180         main.appendChild(endp);
181     }
182     QDomElement backgr = doc.createElement("background");
183     QColor color = getBackgroundColor();
184     backgr.setAttribute("color", colorToString(color));
185     main.appendChild(backgr);
186
187     return doc;
188 }
189
190 /** \brief Get the background color (incl. alpha) from the document, if possibly
191   * \returns The background color of the document, inclusive alpha. If none found, returns (0,0,0,0) */
192 QColor TitleDocument::getBackgroundColor()
193 {
194     QColor color(0, 0, 0, 0);
195     if (m_scene) {
196         QList<QGraphicsItem *> items = m_scene->items();
197         for (int i = 0; i < items.size(); i++) {
198             if (items.at(i)->zValue() == -1100) {
199                 color = ((QGraphicsRectItem *)items.at(i))->brush().color();
200                 return color;
201             }
202         }
203     }
204     return color;
205 }
206
207
208 bool TitleDocument::saveDocument(const KUrl& url, QGraphicsRectItem* startv, QGraphicsRectItem* endv, int out)
209 {
210     if (!m_scene)
211         return false;
212
213     QDomDocument doc = xml(startv, endv);
214     doc.documentElement().setAttribute("out", out);
215     KTemporaryFile tmpfile;
216     if (!tmpfile.open()) {
217         kWarning() << "/////  CANNOT CREATE TMP FILE in: " << tmpfile.fileName();
218         return false;
219     }
220     QFile xmlf(tmpfile.fileName());
221     xmlf.open(QIODevice::WriteOnly);
222     xmlf.write(doc.toString().toUtf8());
223     if (xmlf.error() != QFile::NoError) {
224         xmlf.close();
225         return false;
226     }
227     xmlf.close();
228     return KIO::NetAccess::upload(tmpfile.fileName(), url, 0);
229 }
230
231 int TitleDocument::loadFromXml(QDomDocument doc, QGraphicsRectItem* startv, QGraphicsRectItem* endv, int *out)
232 {
233     QDomNodeList titles = doc.elementsByTagName("kdenlivetitle");
234     //TODO: Check if the opened title size is equal to project size, otherwise warn user and rescale
235     if (doc.documentElement().hasAttribute("width") && doc.documentElement().hasAttribute("height")) {
236         int doc_width = doc.documentElement().attribute("width").toInt();
237         int doc_height = doc.documentElement().attribute("height").toInt();
238         if (doc_width != m_width || doc_height != m_height) {
239             KMessageBox::information(kapp->activeWindow(), i18n("This title clip was created with a different frame size."), i18n("Title Profile"));
240             //TODO: convert using QTransform
241             m_width = doc_width;
242             m_height = doc_height;
243         }
244     }
245     //TODO: get default title duration instead of hardcoded one
246     if (doc.documentElement().hasAttribute("out"))
247         *out = doc.documentElement().attribute("out").toInt();
248     else
249         *out = 125;
250
251     int maxZValue = 0;
252     if (titles.size()) {
253
254         QDomNodeList items = titles.item(0).childNodes();
255         for (int i = 0; i < items.count(); i++) {
256             QGraphicsItem *gitem = NULL;
257             kDebug() << items.item(i).attributes().namedItem("type").nodeValue();
258             int zValue = items.item(i).attributes().namedItem("z-index").nodeValue().toInt();
259             if (zValue > -1000) {
260                 if (items.item(i).attributes().namedItem("type").nodeValue() == "QGraphicsTextItem") {
261                     QDomNamedNodeMap txtProperties = items.item(i).namedItem("content").attributes();
262                     QFont font(txtProperties.namedItem("font").nodeValue());
263
264                     QDomNode node = txtProperties.namedItem("font-bold");
265                     if (!node.isNull()) {
266                         // Old: Bold/Not bold.
267                         font.setBold(node.nodeValue().toInt());
268                     } else {
269                         // New: Font weight (QFont::)
270                         font.setWeight(txtProperties.namedItem("font-weight").nodeValue().toInt());
271                     }
272                     //font.setBold(txtProperties.namedItem("font-bold").nodeValue().toInt());
273                     font.setItalic(txtProperties.namedItem("font-italic").nodeValue().toInt());
274                     font.setUnderline(txtProperties.namedItem("font-underline").nodeValue().toInt());
275                     // Older Kdenlive version did not store pixel size but point size
276                     if (txtProperties.namedItem("font-pixel-size").isNull()) {
277                         KMessageBox::information(kapp->activeWindow(), i18n("Some of your text clips were saved with size in points, which means different sizes on different displays. They will be converted to pixel size, making them portable, but you could have to adjust their size."), i18n("Text Clips Updated"));
278                         QFont f2;
279                         f2.setPointSize(txtProperties.namedItem("font-size").nodeValue().toInt());
280                         font.setPixelSize(QFontInfo(f2).pixelSize());
281                     } else
282                         font.setPixelSize(txtProperties.namedItem("font-pixel-size").nodeValue().toInt());
283                     QColor col(stringToColor(txtProperties.namedItem("font-color").nodeValue()));
284                     QGraphicsTextItem *txt = m_scene->addText(items.item(i).namedItem("content").firstChild().nodeValue(), font);
285                     QTextCursor cursor(txt->document());
286                     cursor.select(QTextCursor::Document);
287                     QTextCharFormat format;
288                     if (txtProperties.namedItem("font-outline").nodeValue().toDouble() > 0.0) {
289                         txt->setData(101, txtProperties.namedItem("font-outline").nodeValue().toDouble());
290                         txt->setData(102, stringToColor(txtProperties.namedItem("font-outline-color").nodeValue()));
291                         format.setTextOutline(
292                             QPen(QColor(stringToColor(txtProperties.namedItem("font-outline-color").nodeValue())),
293                                  txtProperties.namedItem("font-outline").nodeValue().toDouble())
294                         );
295
296                     }
297                     format.setForeground(QBrush(col));
298                     cursor.mergeCharFormat(format);
299                     txt->setTextInteractionFlags(Qt::NoTextInteraction);
300                     if (txtProperties.namedItem("alignment").isNull() == false) {
301                         txt->setTextWidth(txt->boundingRect().width());
302                         QTextCursor cur = txt->textCursor();
303                         QTextBlockFormat format = cur.blockFormat();
304                         format.setAlignment((Qt::Alignment) txtProperties.namedItem("alignment").nodeValue().toInt());
305                         cur.select(QTextCursor::Document);
306                         cur.setBlockFormat(format);
307                         txt->setTextCursor(cur);
308                         cur.clearSelection();
309                         txt->setTextCursor(cur);
310                     }
311
312                     if (!txtProperties.namedItem("kdenlive-axis-x-inverted").isNull()) {
313                         txt->setData(OriginXLeft, txtProperties.namedItem("kdenlive-axis-x-inverted").nodeValue().toInt());
314                     }
315                     if (!txtProperties.namedItem("kdenlive-axis-y-inverted").isNull()) {
316                         txt->setData(OriginYTop, txtProperties.namedItem("kdenlive-axis-y-inverted").nodeValue().toInt());
317                     }
318
319                     // Effects
320                     if (!txtProperties.namedItem("typewriter").isNull()) {
321                         QStringList effData = QStringList() << "typewriter" << txtProperties.namedItem("typewriter").nodeValue();
322                         txt->setData(100, effData);
323                     }
324
325                     gitem = txt;
326                 } else if (items.item(i).attributes().namedItem("type").nodeValue() == "QGraphicsRectItem") {
327                     QString rect = items.item(i).namedItem("content").attributes().namedItem("rect").nodeValue();
328                     QString br_str = items.item(i).namedItem("content").attributes().namedItem("brushcolor").nodeValue();
329                     QString pen_str = items.item(i).namedItem("content").attributes().namedItem("pencolor").nodeValue();
330                     double penwidth = items.item(i).namedItem("content").attributes().namedItem("penwidth").nodeValue().toDouble();
331                     QGraphicsRectItem *rec = m_scene->addRect(stringToRect(rect), QPen(QBrush(stringToColor(pen_str)), penwidth), QBrush(stringToColor(br_str)));
332                     gitem = rec;
333                 } else if (items.item(i).attributes().namedItem("type").nodeValue() == "QGraphicsPixmapItem") {
334                     QString url = items.item(i).namedItem("content").attributes().namedItem("url").nodeValue();
335                     QPixmap pix(url);
336                     QGraphicsPixmapItem *rec = m_scene->addPixmap(pix);
337                     rec->setData(Qt::UserRole, url);
338                     gitem = rec;
339                 } else if (items.item(i).attributes().namedItem("type").nodeValue() == "QGraphicsSvgItem") {
340                     QString url = items.item(i).namedItem("content").attributes().namedItem("url").nodeValue();
341                     QGraphicsSvgItem *rec = new QGraphicsSvgItem(url);
342                     m_scene->addItem(rec);
343                     rec->setData(Qt::UserRole, url);
344                     gitem = rec;
345                 }
346             }
347             //pos and transform
348             if (gitem) {
349                 QPointF p(items.item(i).namedItem("position").attributes().namedItem("x").nodeValue().toDouble(),
350                           items.item(i).namedItem("position").attributes().namedItem("y").nodeValue().toDouble());
351                 gitem->setPos(p);
352                 gitem->setTransform(stringToTransform(items.item(i).namedItem("position").firstChild().firstChild().nodeValue()));
353                 int zValue = items.item(i).attributes().namedItem("z-index").nodeValue().toInt();
354                 if (zValue > maxZValue) maxZValue = zValue;
355                 gitem->setZValue(zValue);
356                 gitem->setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable);
357
358 #if QT_VERSION >= 0x040600
359                 // effects
360                 QDomNode eff = items.item(i).namedItem("effect");
361                 if (!eff.isNull()) {
362                     QDomElement e = eff.toElement();
363                     if (e.attribute("type") == "blur") {
364                         QGraphicsBlurEffect *blur = new QGraphicsBlurEffect();
365                         blur->setBlurRadius(e.attribute("blurradius").toInt());
366                         gitem->setGraphicsEffect(blur);
367                     } else if (e.attribute("type") == "shadow") {
368                         QGraphicsDropShadowEffect *shadow = new QGraphicsDropShadowEffect();
369                         shadow->setBlurRadius(e.attribute("blurradius").toInt());
370                         shadow->setOffset(e.attribute("xoffset").toInt(), e.attribute("yoffset").toInt());
371                         gitem->setGraphicsEffect(shadow);
372                     }
373                 }
374 #endif
375             }
376
377             if (items.item(i).nodeName() == "background") {
378                 kDebug() << items.item(i).attributes().namedItem("color").nodeValue();
379                 QColor color = QColor(stringToColor(items.item(i).attributes().namedItem("color").nodeValue()));
380                 //color.setAlpha(items.item(i).attributes().namedItem("alpha").nodeValue().toInt());
381                 QList<QGraphicsItem *> items = m_scene->items();
382                 for (int i = 0; i < items.size(); i++) {
383                     if (items.at(i)->zValue() == -1100) {
384                         ((QGraphicsRectItem *)items.at(i))->setBrush(QBrush(color));
385                         break;
386                     }
387                 }
388             } else if (items.item(i).nodeName() == "startviewport" && startv) {
389                 QString rect = items.item(i).attributes().namedItem("rect").nodeValue();
390                 QRectF r = stringToRect(rect);
391                 startv->setRect(0, 0, r.width(), r.height());
392                 startv->setPos(r.topLeft());
393             } else if (items.item(i).nodeName() == "endviewport" && endv) {
394                 QString rect = items.item(i).attributes().namedItem("rect").nodeValue();
395                 QRectF r = stringToRect(rect);
396                 endv->setRect(0, 0, r.width(), r.height());
397                 endv->setPos(r.topLeft());
398             }
399         }
400     }
401     return maxZValue;
402 }
403
404 QString TitleDocument::colorToString(const QColor& c)
405 {
406     QString ret = "%1,%2,%3,%4";
407     ret = ret.arg(c.red()).arg(c.green()).arg(c.blue()).arg(c.alpha());
408     return ret;
409 }
410
411 QString TitleDocument::rectFToString(const QRectF& c)
412 {
413     QString ret = "%1,%2,%3,%4";
414     ret = ret.arg(c.left()).arg(c.top()).arg(c.width()).arg(c.height());
415     return ret;
416 }
417
418 QRectF TitleDocument::stringToRect(const QString & s)
419 {
420
421     QStringList l = s.split(',');
422     if (l.size() < 4)
423         return QRectF();
424     return QRectF(l.at(0).toDouble(), l.at(1).toDouble(), l.at(2).toDouble(), l.at(3).toDouble()).normalized();
425 }
426
427 QColor TitleDocument::stringToColor(const QString & s)
428 {
429     QStringList l = s.split(',');
430     if (l.size() < 4)
431         return QColor();
432     return QColor(l.at(0).toInt(), l.at(1).toInt(), l.at(2).toInt(), l.at(3).toInt());;
433 }
434 QTransform TitleDocument::stringToTransform(const QString& s)
435 {
436     QStringList l = s.split(',');
437     if (l.size() < 9)
438         return QTransform();
439     return QTransform(
440                l.at(0).toDouble(), l.at(1).toDouble(), l.at(2).toDouble(),
441                l.at(3).toDouble(), l.at(4).toDouble(), l.at(5).toDouble(),
442                l.at(6).toDouble(), l.at(7).toDouble(), l.at(8).toDouble()
443            );
444 }
445
446 int TitleDocument::frameWidth() const
447 {
448     return m_width;
449 }
450
451 int TitleDocument::frameHeight() const
452 {
453     return m_height;
454 }
455
456