]> git.sesse.net Git - kdenlive/blob - src/titledocument.cpp
round oulines pens
[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                                  Qt::SolidLine,Qt::RoundCap,Qt::RoundJoin)
292                         );
293
294                     }
295                     format.setForeground(QBrush(col));
296                     cursor.mergeCharFormat(format);
297                     txt->setTextInteractionFlags(Qt::NoTextInteraction);
298                     if (txtProperties.namedItem("alignment").isNull() == false) {
299                         txt->setTextWidth(txt->boundingRect().width());
300                         QTextCursor cur = txt->textCursor();
301                         QTextBlockFormat format = cur.blockFormat();
302                         format.setAlignment((Qt::Alignment) txtProperties.namedItem("alignment").nodeValue().toInt());
303                         cur.select(QTextCursor::Document);
304                         cur.setBlockFormat(format);
305                         txt->setTextCursor(cur);
306                         cur.clearSelection();
307                         txt->setTextCursor(cur);
308                     }
309
310                     if (!txtProperties.namedItem("kdenlive-axis-x-inverted").isNull()) {
311                         txt->setData(OriginXLeft, txtProperties.namedItem("kdenlive-axis-x-inverted").nodeValue().toInt());
312                     }
313                     if (!txtProperties.namedItem("kdenlive-axis-y-inverted").isNull()) {
314                         txt->setData(OriginYTop, txtProperties.namedItem("kdenlive-axis-y-inverted").nodeValue().toInt());
315                     }
316
317                     // Effects
318                     if (!txtProperties.namedItem("typewriter").isNull()) {
319                         QStringList effData = QStringList() << "typewriter" << txtProperties.namedItem("typewriter").nodeValue();
320                         txt->setData(100, effData);
321                     }
322
323                     gitem = txt;
324                 } else if (items.item(i).attributes().namedItem("type").nodeValue() == "QGraphicsRectItem") {
325                     QString rect = items.item(i).namedItem("content").attributes().namedItem("rect").nodeValue();
326                     QString br_str = items.item(i).namedItem("content").attributes().namedItem("brushcolor").nodeValue();
327                     QString pen_str = items.item(i).namedItem("content").attributes().namedItem("pencolor").nodeValue();
328                     double penwidth = items.item(i).namedItem("content").attributes().namedItem("penwidth").nodeValue().toDouble();
329                     QGraphicsRectItem *rec = m_scene->addRect(stringToRect(rect), QPen(QBrush(stringToColor(pen_str)), penwidth), QBrush(stringToColor(br_str)));
330                     gitem = rec;
331                 } else if (items.item(i).attributes().namedItem("type").nodeValue() == "QGraphicsPixmapItem") {
332                     QString url = items.item(i).namedItem("content").attributes().namedItem("url").nodeValue();
333                     QPixmap pix(url);
334                     QGraphicsPixmapItem *rec = m_scene->addPixmap(pix);
335                     rec->setData(Qt::UserRole, url);
336                     gitem = rec;
337                 } else if (items.item(i).attributes().namedItem("type").nodeValue() == "QGraphicsSvgItem") {
338                     QString url = items.item(i).namedItem("content").attributes().namedItem("url").nodeValue();
339                     QGraphicsSvgItem *rec = new QGraphicsSvgItem(url);
340                     m_scene->addItem(rec);
341                     rec->setData(Qt::UserRole, url);
342                     gitem = rec;
343                 }
344             }
345             //pos and transform
346             if (gitem) {
347                 QPointF p(items.item(i).namedItem("position").attributes().namedItem("x").nodeValue().toDouble(),
348                           items.item(i).namedItem("position").attributes().namedItem("y").nodeValue().toDouble());
349                 gitem->setPos(p);
350                 gitem->setTransform(stringToTransform(items.item(i).namedItem("position").firstChild().firstChild().nodeValue()));
351                 int zValue = items.item(i).attributes().namedItem("z-index").nodeValue().toInt();
352                 if (zValue > maxZValue) maxZValue = zValue;
353                 gitem->setZValue(zValue);
354                 gitem->setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable);
355
356 #if QT_VERSION >= 0x040600
357                 // effects
358                 QDomNode eff = items.item(i).namedItem("effect");
359                 if (!eff.isNull()) {
360                     QDomElement e = eff.toElement();
361                     if (e.attribute("type") == "blur") {
362                         QGraphicsBlurEffect *blur = new QGraphicsBlurEffect();
363                         blur->setBlurRadius(e.attribute("blurradius").toInt());
364                         gitem->setGraphicsEffect(blur);
365                     } else if (e.attribute("type") == "shadow") {
366                         QGraphicsDropShadowEffect *shadow = new QGraphicsDropShadowEffect();
367                         shadow->setBlurRadius(e.attribute("blurradius").toInt());
368                         shadow->setOffset(e.attribute("xoffset").toInt(), e.attribute("yoffset").toInt());
369                         gitem->setGraphicsEffect(shadow);
370                     }
371                 }
372 #endif
373             }
374
375             if (items.item(i).nodeName() == "background") {
376                 kDebug() << items.item(i).attributes().namedItem("color").nodeValue();
377                 QColor color = QColor(stringToColor(items.item(i).attributes().namedItem("color").nodeValue()));
378                 //color.setAlpha(items.item(i).attributes().namedItem("alpha").nodeValue().toInt());
379                 QList<QGraphicsItem *> items = m_scene->items();
380                 for (int i = 0; i < items.size(); i++) {
381                     if (items.at(i)->zValue() == -1100) {
382                         ((QGraphicsRectItem *)items.at(i))->setBrush(QBrush(color));
383                         break;
384                     }
385                 }
386             } else if (items.item(i).nodeName() == "startviewport" && startv) {
387                 QString rect = items.item(i).attributes().namedItem("rect").nodeValue();
388                 QRectF r = stringToRect(rect);
389                 startv->setRect(0, 0, r.width(), r.height());
390                 startv->setPos(r.topLeft());
391             } else if (items.item(i).nodeName() == "endviewport" && endv) {
392                 QString rect = items.item(i).attributes().namedItem("rect").nodeValue();
393                 QRectF r = stringToRect(rect);
394                 endv->setRect(0, 0, r.width(), r.height());
395                 endv->setPos(r.topLeft());
396             }
397         }
398     }
399     return maxZValue;
400 }
401
402 QString TitleDocument::colorToString(const QColor& c)
403 {
404     QString ret = "%1,%2,%3,%4";
405     ret = ret.arg(c.red()).arg(c.green()).arg(c.blue()).arg(c.alpha());
406     return ret;
407 }
408
409 QString TitleDocument::rectFToString(const QRectF& c)
410 {
411     QString ret = "%1,%2,%3,%4";
412     ret = ret.arg(c.left()).arg(c.top()).arg(c.width()).arg(c.height());
413     return ret;
414 }
415
416 QRectF TitleDocument::stringToRect(const QString & s)
417 {
418
419     QStringList l = s.split(',');
420     if (l.size() < 4)
421         return QRectF();
422     return QRectF(l.at(0).toDouble(), l.at(1).toDouble(), l.at(2).toDouble(), l.at(3).toDouble()).normalized();
423 }
424
425 QColor TitleDocument::stringToColor(const QString & s)
426 {
427     QStringList l = s.split(',');
428     if (l.size() < 4)
429         return QColor();
430     return QColor(l.at(0).toInt(), l.at(1).toInt(), l.at(2).toInt(), l.at(3).toInt());;
431 }
432 QTransform TitleDocument::stringToTransform(const QString& s)
433 {
434     QStringList l = s.split(',');
435     if (l.size() < 9)
436         return QTransform();
437     return QTransform(
438                l.at(0).toDouble(), l.at(1).toDouble(), l.at(2).toDouble(),
439                l.at(3).toDouble(), l.at(4).toDouble(), l.at(5).toDouble(),
440                l.at(6).toDouble(), l.at(7).toDouble(), l.at(8).toDouble()
441            );
442 }
443
444 int TitleDocument::frameWidth() const
445 {
446     return m_width;
447 }
448
449 int TitleDocument::frameHeight() const
450 {
451     return m_height;
452 }
453
454