]> git.sesse.net Git - kdenlive/blob - src/titledocument.cpp
Fix loading / saving of outline color
[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                 QTextCursor cursor(t->document());
101                 cursor.select(QTextCursor::Document);
102                 QColor fontcolor = cursor.charFormat().foreground().color();
103                 content.setAttribute("font-color", colorToString(fontcolor));
104                 if (!t->data(101).isNull()) content.setAttribute("font-outline", t->data(101).toDouble());
105                 if (!t->data(102).isNull()) content.setAttribute("font-outline-color", colorToString(QColor(t->data(102).toString())));
106             }
107             if (!t->data(100).isNull()) {
108                 QStringList effectParams = t->data(100).toStringList();
109                 QString effectName = effectParams.takeFirst();
110                 content.setAttribute(effectName, effectParams.join(";"));
111             }
112
113             // Only save when necessary.
114             if (t->data(OriginXLeft).toInt() == AxisInverted) {
115                 content.setAttribute("kdenlive-axis-x-inverted", t->data(OriginXLeft).toInt());
116             }
117             if (t->data(OriginYTop).toInt() == AxisInverted) {
118                 content.setAttribute("kdenlive-axis-y-inverted", t->data(OriginYTop).toInt());
119             }
120             if (t->textWidth() != -1) {
121                 content.setAttribute("alignment", t->textCursor().blockFormat().alignment());
122             }
123             break;
124         default:
125             continue;
126         }
127
128         // position
129         QDomElement pos = doc.createElement("position");
130         pos.setAttribute("x", item->pos().x());
131         pos.setAttribute("y", item->pos().y());
132         QTransform transform = item->transform();
133         QDomElement tr = doc.createElement("transform");
134         tr.appendChild(doc.createTextNode(
135                            QString("%1,%2,%3,%4,%5,%6,%7,%8,%9").arg(
136                                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())
137                        )
138                       );
139         e.setAttribute("z-index", item->zValue());
140         pos.appendChild(tr);
141
142 #if QT_VERSION >= 0x040600
143         // effects
144         QGraphicsEffect *eff = item->graphicsEffect();
145         if (eff) {
146             QGraphicsBlurEffect *blur = static_cast <QGraphicsBlurEffect *>(eff);
147             QDomElement effect = doc.createElement("effect");
148             if (blur) {
149                 effect.setAttribute("type", "blur");
150                 effect.setAttribute("blurradius", blur->blurRadius());
151             } else {
152                 QGraphicsDropShadowEffect *shadow = static_cast <QGraphicsDropShadowEffect *>(eff);
153                 if (shadow) {
154                     effect.setAttribute("type", "shadow");
155                     effect.setAttribute("blurradius", shadow->blurRadius());
156                     effect.setAttribute("xoffset", shadow->xOffset());
157                     effect.setAttribute("yoffset", shadow->yOffset());
158                 }
159             }
160             e.appendChild(effect);
161         }
162 #endif
163
164         e.appendChild(pos);
165         e.appendChild(content);
166         if (item->zValue() > -1000) main.appendChild(e);
167     }
168     if (startv && endv) {
169         QDomElement endp = doc.createElement("endviewport");
170         QDomElement startp = doc.createElement("startviewport");
171         QRectF r(endv->pos().x(), endv->pos().y(), endv->rect().width(), endv->rect().height());
172         endp.setAttribute("rect", rectFToString(r));
173         QRectF r2(startv->pos().x(), startv->pos().y(), startv->rect().width(), startv->rect().height());
174         startp.setAttribute("rect", rectFToString(r2));
175
176         main.appendChild(startp);
177         main.appendChild(endp);
178     }
179     QDomElement backgr = doc.createElement("background");
180     QColor color = getBackgroundColor();
181     backgr.setAttribute("color", colorToString(color));
182     main.appendChild(backgr);
183
184     return doc;
185 }
186
187 /** \brief Get the background color (incl. alpha) from the document, if possibly
188   * \returns The background color of the document, inclusive alpha. If none found, returns (0,0,0,0) */
189 QColor TitleDocument::getBackgroundColor()
190 {
191     QColor color(0, 0, 0, 0);
192     if (m_scene) {
193         QList<QGraphicsItem *> items = m_scene->items();
194         for (int i = 0; i < items.size(); i++) {
195             if (items.at(i)->zValue() == -1100) {
196                 color = ((QGraphicsRectItem *)items.at(i))->brush().color();
197                 return color;
198             }
199         }
200     }
201     return color;
202 }
203
204
205 bool TitleDocument::saveDocument(const KUrl& url, QGraphicsRectItem* startv, QGraphicsRectItem* endv, int out)
206 {
207     if (!m_scene)
208         return false;
209
210     QDomDocument doc = xml(startv, endv);
211     doc.documentElement().setAttribute("out", out);
212     KTemporaryFile tmpfile;
213     if (!tmpfile.open()) {
214         kWarning() << "/////  CANNOT CREATE TMP FILE in: " << tmpfile.fileName();
215         return false;
216     }
217     QFile xmlf(tmpfile.fileName());
218     xmlf.open(QIODevice::WriteOnly);
219     xmlf.write(doc.toString().toUtf8());
220     if (xmlf.error() != QFile::NoError) {
221         xmlf.close();
222         return false;
223     }
224     xmlf.close();
225     return KIO::NetAccess::upload(tmpfile.fileName(), url, 0);
226 }
227
228 int TitleDocument::loadFromXml(QDomDocument doc, QGraphicsRectItem* startv, QGraphicsRectItem* endv, int *out)
229 {
230     QDomNodeList titles = doc.elementsByTagName("kdenlivetitle");
231     //TODO: Check if the opened title size is equal to project size, otherwise warn user and rescale
232     if (doc.documentElement().hasAttribute("width") && doc.documentElement().hasAttribute("height")) {
233         int doc_width = doc.documentElement().attribute("width").toInt();
234         int doc_height = doc.documentElement().attribute("height").toInt();
235         if (doc_width != m_width || doc_height != m_height) {
236             KMessageBox::information(kapp->activeWindow(), i18n("This title clip was created with a different frame size."), i18n("Title Profile"));
237             //TODO: convert using QTransform
238             m_width = doc_width;
239             m_height = doc_height;
240         }
241     }
242     //TODO: get default title duration instead of hardcoded one
243     if (doc.documentElement().hasAttribute("out"))
244         *out = doc.documentElement().attribute("out").toInt();
245     else
246         *out = 125;
247
248     int maxZValue = 0;
249     if (titles.size()) {
250
251         QDomNodeList items = titles.item(0).childNodes();
252         for (int i = 0; i < items.count(); i++) {
253             QGraphicsItem *gitem = NULL;
254             kDebug() << items.item(i).attributes().namedItem("type").nodeValue();
255             int zValue = items.item(i).attributes().namedItem("z-index").nodeValue().toInt();
256             if (zValue > -1000) {
257                 if (items.item(i).attributes().namedItem("type").nodeValue() == "QGraphicsTextItem") {
258                     QDomNamedNodeMap txtProperties = items.item(i).namedItem("content").attributes();
259                     QFont font(txtProperties.namedItem("font").nodeValue());
260
261                     QDomNode node = txtProperties.namedItem("font-bold");
262                     if (!node.isNull()) {
263                         // Old: Bold/Not bold.
264                         font.setBold(node.nodeValue().toInt());
265                     } else {
266                         // New: Font weight (QFont::)
267                         font.setWeight(txtProperties.namedItem("font-weight").nodeValue().toInt());
268                     }
269                     //font.setBold(txtProperties.namedItem("font-bold").nodeValue().toInt());
270                     font.setItalic(txtProperties.namedItem("font-italic").nodeValue().toInt());
271                     font.setUnderline(txtProperties.namedItem("font-underline").nodeValue().toInt());
272                     // Older Kdenlive version did not store pixel size but point size
273                     if (txtProperties.namedItem("font-pixel-size").isNull()) {
274                         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"));
275                         QFont f2;
276                         f2.setPointSize(txtProperties.namedItem("font-size").nodeValue().toInt());
277                         font.setPixelSize(QFontInfo(f2).pixelSize());
278                     } else
279                         font.setPixelSize(txtProperties.namedItem("font-pixel-size").nodeValue().toInt());
280                     QColor col(stringToColor(txtProperties.namedItem("font-color").nodeValue()));
281                     QGraphicsTextItem *txt = m_scene->addText(items.item(i).namedItem("content").firstChild().nodeValue(), font);
282                     QTextCursor cursor(txt->document());
283                     cursor.select(QTextCursor::Document);
284                     QTextCharFormat format;
285                     if (txtProperties.namedItem("font-outline").nodeValue().toDouble() > 0.0) {
286                         txt->setData(101, txtProperties.namedItem("font-outline").nodeValue().toDouble());
287                         txt->setData(102, stringToColor(txtProperties.namedItem("font-outline-color").nodeValue()));
288                         format.setTextOutline(
289                             QPen(QColor(stringToColor(txtProperties.namedItem("font-outline-color").nodeValue())),
290                                  txtProperties.namedItem("font-outline").nodeValue().toDouble())
291                         );
292
293                     }
294                     format.setForeground(QBrush(col));
295                     cursor.mergeCharFormat(format);
296                     txt->setTextInteractionFlags(Qt::NoTextInteraction);
297                     if (txtProperties.namedItem("alignment").isNull() == false) {
298                         txt->setTextWidth(txt->boundingRect().width());
299                         QTextCursor cur = txt->textCursor();
300                         QTextBlockFormat format = cur.blockFormat();
301                         format.setAlignment((Qt::Alignment) txtProperties.namedItem("alignment").nodeValue().toInt());
302                         cur.select(QTextCursor::Document);
303                         cur.setBlockFormat(format);
304                         txt->setTextCursor(cur);
305                         cur.clearSelection();
306                         txt->setTextCursor(cur);
307                     }
308
309                     if (!txtProperties.namedItem("kdenlive-axis-x-inverted").isNull()) {
310                         txt->setData(OriginXLeft, txtProperties.namedItem("kdenlive-axis-x-inverted").nodeValue().toInt());
311                     }
312                     if (!txtProperties.namedItem("kdenlive-axis-y-inverted").isNull()) {
313                         txt->setData(OriginYTop, txtProperties.namedItem("kdenlive-axis-y-inverted").nodeValue().toInt());
314                     }
315
316                     // Effects
317                     if (!txtProperties.namedItem("typewriter").isNull()) {
318                         QStringList effData = QStringList() << "typewriter" << txtProperties.namedItem("typewriter").nodeValue();
319                         txt->setData(100, effData);
320                     }
321
322                     gitem = txt;
323                 } else if (items.item(i).attributes().namedItem("type").nodeValue() == "QGraphicsRectItem") {
324                     QString rect = items.item(i).namedItem("content").attributes().namedItem("rect").nodeValue();
325                     QString br_str = items.item(i).namedItem("content").attributes().namedItem("brushcolor").nodeValue();
326                     QString pen_str = items.item(i).namedItem("content").attributes().namedItem("pencolor").nodeValue();
327                     double penwidth = items.item(i).namedItem("content").attributes().namedItem("penwidth").nodeValue().toDouble();
328                     QGraphicsRectItem *rec = m_scene->addRect(stringToRect(rect), QPen(QBrush(stringToColor(pen_str)), penwidth), QBrush(stringToColor(br_str)));
329                     gitem = rec;
330                 } else if (items.item(i).attributes().namedItem("type").nodeValue() == "QGraphicsPixmapItem") {
331                     QString url = items.item(i).namedItem("content").attributes().namedItem("url").nodeValue();
332                     QPixmap pix(url);
333                     QGraphicsPixmapItem *rec = m_scene->addPixmap(pix);
334                     rec->setData(Qt::UserRole, url);
335                     gitem = rec;
336                 } else if (items.item(i).attributes().namedItem("type").nodeValue() == "QGraphicsSvgItem") {
337                     QString url = items.item(i).namedItem("content").attributes().namedItem("url").nodeValue();
338                     QGraphicsSvgItem *rec = new QGraphicsSvgItem(url);
339                     m_scene->addItem(rec);
340                     rec->setData(Qt::UserRole, url);
341                     gitem = rec;
342                 }
343             }
344             //pos and transform
345             if (gitem) {
346                 QPointF p(items.item(i).namedItem("position").attributes().namedItem("x").nodeValue().toDouble(),
347                           items.item(i).namedItem("position").attributes().namedItem("y").nodeValue().toDouble());
348                 gitem->setPos(p);
349                 gitem->setTransform(stringToTransform(items.item(i).namedItem("position").firstChild().firstChild().nodeValue()));
350                 int zValue = items.item(i).attributes().namedItem("z-index").nodeValue().toInt();
351                 if (zValue > maxZValue) maxZValue = zValue;
352                 gitem->setZValue(zValue);
353                 gitem->setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable);
354
355 #if QT_VERSION >= 0x040600
356                 // effects
357                 QDomNode eff = items.item(i).namedItem("effect");
358                 if (!eff.isNull()) {
359                     QDomElement e = eff.toElement();
360                     if (e.attribute("type") == "blur") {
361                         QGraphicsBlurEffect *blur = new QGraphicsBlurEffect();
362                         blur->setBlurRadius(e.attribute("blurradius").toInt());
363                         gitem->setGraphicsEffect(blur);
364                     } else if (e.attribute("type") == "shadow") {
365                         QGraphicsDropShadowEffect *shadow = new QGraphicsDropShadowEffect();
366                         shadow->setBlurRadius(e.attribute("blurradius").toInt());
367                         shadow->setOffset(e.attribute("xoffset").toInt(), e.attribute("yoffset").toInt());
368                         gitem->setGraphicsEffect(shadow);
369                     }
370                 }
371 #endif
372             }
373
374             if (items.item(i).nodeName() == "background") {
375                 kDebug() << items.item(i).attributes().namedItem("color").nodeValue();
376                 QColor color = QColor(stringToColor(items.item(i).attributes().namedItem("color").nodeValue()));
377                 //color.setAlpha(items.item(i).attributes().namedItem("alpha").nodeValue().toInt());
378                 QList<QGraphicsItem *> items = m_scene->items();
379                 for (int i = 0; i < items.size(); i++) {
380                     if (items.at(i)->zValue() == -1100) {
381                         ((QGraphicsRectItem *)items.at(i))->setBrush(QBrush(color));
382                         break;
383                     }
384                 }
385             } else if (items.item(i).nodeName() == "startviewport" && startv) {
386                 QString rect = items.item(i).attributes().namedItem("rect").nodeValue();
387                 QRectF r = stringToRect(rect);
388                 startv->setRect(0, 0, r.width(), r.height());
389                 startv->setPos(r.topLeft());
390             } else if (items.item(i).nodeName() == "endviewport" && endv) {
391                 QString rect = items.item(i).attributes().namedItem("rect").nodeValue();
392                 QRectF r = stringToRect(rect);
393                 endv->setRect(0, 0, r.width(), r.height());
394                 endv->setPos(r.topLeft());
395             }
396         }
397     }
398     return maxZValue;
399 }
400
401 QString TitleDocument::colorToString(const QColor& c)
402 {
403     QString ret = "%1,%2,%3,%4";
404     ret = ret.arg(c.red()).arg(c.green()).arg(c.blue()).arg(c.alpha());
405     return ret;
406 }
407
408 QString TitleDocument::rectFToString(const QRectF& c)
409 {
410     QString ret = "%1,%2,%3,%4";
411     ret = ret.arg(c.left()).arg(c.top()).arg(c.width()).arg(c.height());
412     return ret;
413 }
414
415 QRectF TitleDocument::stringToRect(const QString & s)
416 {
417
418     QStringList l = s.split(',');
419     if (l.size() < 4)
420         return QRectF();
421     return QRectF(l.at(0).toDouble(), l.at(1).toDouble(), l.at(2).toDouble(), l.at(3).toDouble()).normalized();
422 }
423
424 QColor TitleDocument::stringToColor(const QString & s)
425 {
426     QStringList l = s.split(',');
427     if (l.size() < 4)
428         return QColor();
429     return QColor(l.at(0).toInt(), l.at(1).toInt(), l.at(2).toInt(), l.at(3).toInt());;
430 }
431 QTransform TitleDocument::stringToTransform(const QString& s)
432 {
433     QStringList l = s.split(',');
434     if (l.size() < 9)
435         return QTransform();
436     return QTransform(
437                l.at(0).toDouble(), l.at(1).toDouble(), l.at(2).toDouble(),
438                l.at(3).toDouble(), l.at(4).toDouble(), l.at(5).toDouble(),
439                l.at(6).toDouble(), l.at(7).toDouble(), l.at(8).toDouble()
440            );
441 }
442
443 int TitleDocument::frameWidth() const
444 {
445     return m_width;
446 }
447
448 int TitleDocument::frameHeight() const
449 {
450     return m_height;
451 }
452
453