]> git.sesse.net Git - kdenlive/blob - src/titledocument.cpp
Missing commit for title clip duration + fix background for title clips
[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
38 TitleDocument::TitleDocument()
39 {
40     m_scene = NULL;
41 }
42
43 void TitleDocument::setScene(QGraphicsScene* _scene, int width, int height)
44 {
45     m_scene = _scene;
46     m_width = width;
47     m_height = height;
48 }
49
50 QDomDocument TitleDocument::xml(QGraphicsPolygonItem* startv, QGraphicsPolygonItem* endv)
51 {
52     QDomDocument doc;
53
54     QDomElement main = doc.createElement("kdenlivetitle");
55     main.setAttribute("width", m_width);
56     main.setAttribute("height", m_height);
57     doc.appendChild(main);
58
59     foreach(QGraphicsItem* item, m_scene->items()) {
60         QDomElement e = doc.createElement("item");
61         QDomElement content = doc.createElement("content");
62         QFont font;
63         QGraphicsTextItem *t;
64
65         switch (item->type()) {
66         case 7:
67             e.setAttribute("type", "QGraphicsPixmapItem");
68             content.setAttribute("url", item->data(Qt::UserRole).toString());
69             break;
70         case 13:
71             e.setAttribute("type", "QGraphicsSvgItem");
72             content.setAttribute("url", item->data(Qt::UserRole).toString());
73             break;
74         case 3:
75             e.setAttribute("type", "QGraphicsRectItem");
76             content.setAttribute("rect", rectFToString(((QGraphicsRectItem*)item)->rect().normalized()));
77             content.setAttribute("pencolor", colorToString(((QGraphicsRectItem*)item)->pen().color()));
78             content.setAttribute("penwidth", ((QGraphicsRectItem*)item)->pen().width());
79             content.setAttribute("brushcolor", colorToString(((QGraphicsRectItem*)item)->brush().color()));
80             break;
81         case 8:
82             e.setAttribute("type", "QGraphicsTextItem");
83             t = static_cast<QGraphicsTextItem *>(item);
84             // Don't save empty text nodes
85             if (t->toPlainText().simplified().isEmpty()) continue;
86             //content.appendChild(doc.createTextNode(((QGraphicsTextItem*)item)->toHtml()));
87             content.appendChild(doc.createTextNode(t->toPlainText()));
88             font = t->font();
89             content.setAttribute("font", font.family());
90             content.setAttribute("font-weight", font.weight());
91             content.setAttribute("font-pixel-size", font.pixelSize());
92             content.setAttribute("font-italic", font.italic());
93             content.setAttribute("font-underline", font.underline());
94             content.setAttribute("font-color", colorToString(t->defaultTextColor()));
95
96             // Only save when necessary.
97             if (t->data(OriginXLeft).toInt() == AxisInverted) {
98                 content.setAttribute("kdenlive-axis-x-inverted", t->data(OriginXLeft).toInt());
99             }
100             if (t->data(OriginYTop).toInt() == AxisInverted) {
101                 content.setAttribute("kdenlive-axis-y-inverted", t->data(OriginYTop).toInt());
102             }
103             if (t->textWidth() != -1) {
104                 content.setAttribute("alignment", t->textCursor().blockFormat().alignment());
105             }
106             break;
107         default:
108             continue;
109         }
110         QDomElement pos = doc.createElement("position");
111         pos.setAttribute("x", item->pos().x());
112         pos.setAttribute("y", item->pos().y());
113         QTransform transform = item->transform();
114         QDomElement tr = doc.createElement("transform");
115         tr.appendChild(doc.createTextNode(
116                            QString("%1,%2,%3,%4,%5,%6,%7,%8,%9").arg(
117                                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())
118                        )
119                       );
120         e.setAttribute("z-index", item->zValue());
121         pos.appendChild(tr);
122
123
124         e.appendChild(pos);
125         e.appendChild(content);
126         if (item->zValue() > -1100) main.appendChild(e);
127     }
128     if (startv && endv) {
129         QDomElement endp = doc.createElement("endviewport");
130         QDomElement startp = doc.createElement("startviewport");
131         endp.setAttribute("x", endv->data(0).toString());
132         endp.setAttribute("y", endv->data(1).toString());
133         endp.setAttribute("size", endv->data(2).toString());
134         endp.setAttribute("rect", rectFToString(endv->boundingRect()));
135
136         startp.setAttribute("x", startv->data(0).toString());
137         startp.setAttribute("y", startv->data(1).toString());
138         startp.setAttribute("size", startv->data(2).toString());
139         startp.setAttribute("rect", rectFToString(startv->boundingRect()));
140
141         startp.setAttribute("z-index", startv->zValue());
142         endp.setAttribute("z-index", endv->zValue());
143         main.appendChild(startp);
144         main.appendChild(endp);
145     }
146     QDomElement backgr = doc.createElement("background");
147     QColor color = getBackgroundColor();
148     backgr.setAttribute("color", colorToString(color));
149     main.appendChild(backgr);
150
151     return doc;
152 }
153
154 /** \brief Get the background color (incl. alpha) from the document, if possibly
155   * \returns The background color of the document, inclusive alpha. If none found, returns (0,0,0,0) */
156 QColor TitleDocument::getBackgroundColor()
157 {
158     QColor color(0, 0, 0, 0);
159     if (m_scene) {
160         QList<QGraphicsItem *> items = m_scene->items();
161         for (int i = 0; i < items.size(); i++) {
162             if (items.at(i)->zValue() == -1100) {
163                 color = ((QGraphicsRectItem *)items.at(i))->brush().color();
164                 return color;
165             }
166         }
167     }
168     return color;
169 }
170
171
172 bool TitleDocument::saveDocument(const KUrl& url, QGraphicsPolygonItem* startv, QGraphicsPolygonItem* endv, int out)
173 {
174     if (!m_scene)
175         return false;
176
177     QDomDocument doc = xml(startv, endv);
178     doc.documentElement().setAttribute("out", out);
179     KTemporaryFile tmpfile;
180     if (!tmpfile.open()) {
181         kWarning() << "/////  CANNOT CREATE TMP FILE in: " << tmpfile.fileName();
182         return false;
183     }
184     QFile xmlf(tmpfile.fileName());
185     xmlf.open(QIODevice::WriteOnly);
186     xmlf.write(doc.toString().toUtf8());
187     if (xmlf.error() != QFile::NoError) {
188         xmlf.close();
189         return false;
190     }
191     xmlf.close();
192     return KIO::NetAccess::upload(tmpfile.fileName(), url, 0);
193 }
194
195 int TitleDocument::loadFromXml(QDomDocument doc, QGraphicsPolygonItem* startv, QGraphicsPolygonItem* endv, int *out)
196 {
197     QDomNodeList titles = doc.elementsByTagName("kdenlivetitle");
198     //TODO: Check if the opened title size is equal to project size, otherwise warn user and rescale
199     if (doc.documentElement().hasAttribute("width") && doc.documentElement().hasAttribute("height")) {
200         int doc_width = doc.documentElement().attribute("width").toInt();
201         int doc_height = doc.documentElement().attribute("height").toInt();
202         if (doc_width != m_width || doc_height != m_height) {
203             KMessageBox::information(kapp->activeWindow(), i18n("This title clip was created with a different frame size."), i18n("Title Profile"));
204             //TODO: convert using QTransform
205             m_width = doc_width;
206             m_height = doc_height;
207         }
208     }
209     //TODO: get default title duration instead of hardcoded one
210     if (doc.documentElement().hasAttribute("out"))
211         *out = doc.documentElement().attribute("out").toInt();
212     else
213         *out = 125;
214
215     int maxZValue = 0;
216     if (titles.size()) {
217
218         QDomNodeList items = titles.item(0).childNodes();
219         for (int i = 0; i < items.count(); i++) {
220             QGraphicsItem *gitem = NULL;
221             kDebug() << items.item(i).attributes().namedItem("type").nodeValue();
222             int zValue = items.item(i).attributes().namedItem("z-index").nodeValue().toInt();
223             if (zValue > -1000) {
224                 if (items.item(i).attributes().namedItem("type").nodeValue() == "QGraphicsTextItem") {
225                     QDomNamedNodeMap txtProperties = items.item(i).namedItem("content").attributes();
226                     QFont font(txtProperties.namedItem("font").nodeValue());
227
228                     QDomNode node = txtProperties.namedItem("font-bold");
229                     if (!node.isNull()) {
230                         // Old: Bold/Not bold.
231                         font.setBold(node.nodeValue().toInt());
232                     } else {
233                         // New: Font weight (QFont::)
234                         font.setWeight(txtProperties.namedItem("font-weight").nodeValue().toInt());
235                     }
236                     //font.setBold(txtProperties.namedItem("font-bold").nodeValue().toInt());
237                     font.setItalic(txtProperties.namedItem("font-italic").nodeValue().toInt());
238                     font.setUnderline(txtProperties.namedItem("font-underline").nodeValue().toInt());
239                     // Older Kdenlive version did not store pixel size but point size
240                     if (txtProperties.namedItem("font-pixel-size").isNull()) {
241                         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"));
242                         QFont f2;
243                         f2.setPointSize(txtProperties.namedItem("font-size").nodeValue().toInt());
244                         font.setPixelSize(QFontInfo(f2).pixelSize());
245                     } else
246                         font.setPixelSize(txtProperties.namedItem("font-pixel-size").nodeValue().toInt());
247                     QColor col(stringToColor(txtProperties.namedItem("font-color").nodeValue()));
248                     QGraphicsTextItem *txt = m_scene->addText(items.item(i).namedItem("content").firstChild().nodeValue(), font);
249                     txt->setDefaultTextColor(col);
250                     txt->setTextInteractionFlags(Qt::NoTextInteraction);
251                     if (txtProperties.namedItem("alignment").isNull() == false) {
252                         txt->setTextWidth(txt->boundingRect().width());
253                         QTextCursor cur = txt->textCursor();
254                         QTextBlockFormat format = cur.blockFormat();
255                         format.setAlignment((Qt::Alignment) txtProperties.namedItem("alignment").nodeValue().toInt());
256                         cur.select(QTextCursor::Document);
257                         cur.setBlockFormat(format);
258                         txt->setTextCursor(cur);
259                         cur.clearSelection();
260                         txt->setTextCursor(cur);
261                     }
262
263                     if (!txtProperties.namedItem("kdenlive-axis-x-inverted").isNull()) {
264                         txt->setData(OriginXLeft, txtProperties.namedItem("kdenlive-axis-x-inverted").nodeValue().toInt());
265                     }
266                     if (!txtProperties.namedItem("kdenlive-axis-y-inverted").isNull()) {
267                         txt->setData(OriginYTop, txtProperties.namedItem("kdenlive-axis-y-inverted").nodeValue().toInt());
268                     }
269
270                     gitem = txt;
271                 } else if (items.item(i).attributes().namedItem("type").nodeValue() == "QGraphicsRectItem") {
272                     QString rect = items.item(i).namedItem("content").attributes().namedItem("rect").nodeValue();
273                     QString br_str = items.item(i).namedItem("content").attributes().namedItem("brushcolor").nodeValue();
274                     QString pen_str = items.item(i).namedItem("content").attributes().namedItem("pencolor").nodeValue();
275                     double penwidth = items.item(i).namedItem("content").attributes().namedItem("penwidth").nodeValue().toDouble();
276                     QGraphicsRectItem *rec = m_scene->addRect(stringToRect(rect), QPen(QBrush(stringToColor(pen_str)), penwidth), QBrush(stringToColor(br_str)));
277                     gitem = rec;
278                 } else if (items.item(i).attributes().namedItem("type").nodeValue() == "QGraphicsPixmapItem") {
279                     QString url = items.item(i).namedItem("content").attributes().namedItem("url").nodeValue();
280                     QPixmap pix(url);
281                     QGraphicsPixmapItem *rec = m_scene->addPixmap(pix);
282                     rec->setData(Qt::UserRole, url);
283                     gitem = rec;
284                 } else if (items.item(i).attributes().namedItem("type").nodeValue() == "QGraphicsSvgItem") {
285                     QString url = items.item(i).namedItem("content").attributes().namedItem("url").nodeValue();
286                     QGraphicsSvgItem *rec = new QGraphicsSvgItem(url);
287                     m_scene->addItem(rec);
288                     rec->setData(Qt::UserRole, url);
289                     gitem = rec;
290                 }
291             }
292             //pos and transform
293             if (gitem) {
294                 QPointF p(items.item(i).namedItem("position").attributes().namedItem("x").nodeValue().toDouble(),
295                           items.item(i).namedItem("position").attributes().namedItem("y").nodeValue().toDouble());
296                 gitem->setPos(p);
297                 gitem->setTransform(stringToTransform(items.item(i).namedItem("position").firstChild().firstChild().nodeValue()));
298                 int zValue = items.item(i).attributes().namedItem("z-index").nodeValue().toInt();
299                 if (zValue > maxZValue) maxZValue = zValue;
300                 gitem->setZValue(zValue);
301                 gitem->setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable);
302             }
303             if (items.item(i).nodeName() == "background") {
304                 kDebug() << items.item(i).attributes().namedItem("color").nodeValue();
305                 QColor color = QColor(stringToColor(items.item(i).attributes().namedItem("color").nodeValue()));
306                 //color.setAlpha(items.item(i).attributes().namedItem("alpha").nodeValue().toInt());
307                 QList<QGraphicsItem *> items = m_scene->items();
308                 for (int i = 0; i < items.size(); i++) {
309                     if (items.at(i)->zValue() == -1100) {
310                         ((QGraphicsRectItem *)items.at(i))->setBrush(QBrush(color));
311                         break;
312                     }
313                 }
314             } else if (items.item(i).nodeName() == "startviewport" && startv) {
315                 QString rect = items.item(i).attributes().namedItem("rect").nodeValue();
316                 startv->setPolygon(stringToRect(rect));
317                 int x = items.item(i).attributes().namedItem("x").nodeValue().toInt();
318                 int y = items.item(i).attributes().namedItem("y").nodeValue().toInt();
319                 int size = items.item(i).attributes().namedItem("size").nodeValue().toInt();
320                 startv->setData(0, x);
321                 startv->setData(1, y);
322                 startv->setData(2, size);
323                 //startv->setPos(p);
324             } else if (items.item(i).nodeName() == "endviewport" && endv) {
325                 QString rect = items.item(i).attributes().namedItem("rect").nodeValue();
326                 endv->setPolygon(stringToRect(rect));
327                 int x = items.item(i).attributes().namedItem("x").nodeValue().toInt();
328                 int y = items.item(i).attributes().namedItem("y").nodeValue().toInt();
329                 int size = items.item(i).attributes().namedItem("size").nodeValue().toInt();
330                 endv->setData(0, x);
331                 endv->setData(1, y);
332                 endv->setData(2, size);
333             }
334         }
335     }
336     return maxZValue;
337 }
338
339 QString TitleDocument::colorToString(const QColor& c)
340 {
341     QString ret = "%1,%2,%3,%4";
342     ret = ret.arg(c.red()).arg(c.green()).arg(c.blue()).arg(c.alpha());
343     return ret;
344 }
345
346 QString TitleDocument::rectFToString(const QRectF& c)
347 {
348     QString ret = "%1,%2,%3,%4";
349     ret = ret.arg(c.left()).arg(c.top()).arg(c.width()).arg(c.height());
350     return ret;
351 }
352
353 QRectF TitleDocument::stringToRect(const QString & s)
354 {
355
356     QStringList l = s.split(',');
357     if (l.size() < 4)
358         return QRectF();
359     return QRectF(l.at(0).toDouble(), l.at(1).toDouble(), l.at(2).toDouble(), l.at(3).toDouble()).normalized();
360 }
361
362 QColor TitleDocument::stringToColor(const QString & s)
363 {
364     QStringList l = s.split(',');
365     if (l.size() < 4)
366         return QColor();
367     return QColor(l.at(0).toInt(), l.at(1).toInt(), l.at(2).toInt(), l.at(3).toInt());;
368 }
369 QTransform TitleDocument::stringToTransform(const QString& s)
370 {
371     QStringList l = s.split(',');
372     if (l.size() < 9)
373         return QTransform();
374     return QTransform(
375                l.at(0).toDouble(), l.at(1).toDouble(), l.at(2).toDouble(),
376                l.at(3).toDouble(), l.at(4).toDouble(), l.at(5).toDouble(),
377                l.at(6).toDouble(), l.at(7).toDouble(), l.at(8).toDouble()
378            );
379 }
380
381 int TitleDocument::frameWidth() const
382 {
383     return m_width;
384 }
385
386 int TitleDocument::frameHeight() const
387 {
388     return m_height;
389 }
390
391