]> git.sesse.net Git - kdenlive/blob - src/titledocument.cpp
Cleanup, fix mem leaks, fix unused clips not correctly updated on profile change
[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, double 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::loadDocument(const KUrl& url, QGraphicsPolygonItem* startv, QGraphicsPolygonItem* endv, double *out)
196 {
197     QString tmpfile;
198     QDomDocument doc;
199     if (!m_scene)
200         return -1;
201
202     if (KIO::NetAccess::download(url, tmpfile, 0)) {
203         QFile file(tmpfile);
204         if (file.open(QIODevice::ReadOnly)) {
205             doc.setContent(&file, false);
206             file.close();
207         } else
208             return -1;
209         KIO::NetAccess::removeTempFile(tmpfile);
210         return loadFromXml(doc, startv, endv, out);
211     }
212     return -1;
213 }
214
215 int TitleDocument::loadFromXml(QDomDocument doc, QGraphicsPolygonItem* startv, QGraphicsPolygonItem* endv, double *out)
216 {
217     QDomNodeList titles = doc.elementsByTagName("kdenlivetitle");
218     //TODO: Check if the opened title size is equal to project size, otherwise warn user and rescale
219     if (doc.documentElement().hasAttribute("width") && doc.documentElement().hasAttribute("height")) {
220         int doc_width = doc.documentElement().attribute("width").toInt();
221         int doc_height = doc.documentElement().attribute("height").toInt();
222         if (doc_width != m_width || doc_height != m_height) {
223             KMessageBox::information(kapp->activeWindow(), i18n("This title clip was created with a different frame size. It will now be converted to the current project's size."), i18n("Resizing Title Clip"));
224             //TODO: convert using QTransform
225         }
226     }
227     //TODO: get default title duration instead of hardcoded one
228     if (doc.documentElement().hasAttribute("out"))
229         *out = doc.documentElement().attribute("out").toDouble();
230     else
231         *out = 5000;
232
233     int maxZValue = 0;
234     if (titles.size()) {
235
236         QDomNodeList items = titles.item(0).childNodes();
237         for (int i = 0; i < items.count(); i++) {
238             QGraphicsItem *gitem = NULL;
239             kDebug() << items.item(i).attributes().namedItem("type").nodeValue();
240             int zValue = items.item(i).attributes().namedItem("z-index").nodeValue().toInt();
241             if (zValue > -1000) {
242                 if (items.item(i).attributes().namedItem("type").nodeValue() == "QGraphicsTextItem") {
243                     QDomNamedNodeMap txtProperties = items.item(i).namedItem("content").attributes();
244                     QFont font(txtProperties.namedItem("font").nodeValue());
245
246                     QDomNode node = txtProperties.namedItem("font-bold");
247                     if (!node.isNull()) {
248                         // Old: Bold/Not bold.
249                         font.setBold(node.nodeValue().toInt());
250                     } else {
251                         // New: Font weight (QFont::)
252                         font.setWeight(txtProperties.namedItem("font-weight").nodeValue().toInt());
253                     }
254                     //font.setBold(txtProperties.namedItem("font-bold").nodeValue().toInt());
255                     font.setItalic(txtProperties.namedItem("font-italic").nodeValue().toInt());
256                     font.setUnderline(txtProperties.namedItem("font-underline").nodeValue().toInt());
257                     // Older Kdenlive version did not store pixel size but point size
258                     if (txtProperties.namedItem("font-pixel-size").isNull()) {
259                         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"));
260                         QFont f2;
261                         f2.setPointSize(txtProperties.namedItem("font-size").nodeValue().toInt());
262                         font.setPixelSize(QFontInfo(f2).pixelSize());
263                     } else
264                         font.setPixelSize(txtProperties.namedItem("font-pixel-size").nodeValue().toInt());
265                     QColor col(stringToColor(txtProperties.namedItem("font-color").nodeValue()));
266                     QGraphicsTextItem *txt = m_scene->addText(items.item(i).namedItem("content").firstChild().nodeValue(), font);
267                     txt->setDefaultTextColor(col);
268                     txt->setTextInteractionFlags(Qt::NoTextInteraction);
269                     if (txtProperties.namedItem("alignment").isNull() == false) {
270                         txt->setTextWidth(txt->boundingRect().width());
271                         QTextCursor cur = txt->textCursor();
272                         QTextBlockFormat format = cur.blockFormat();
273                         format.setAlignment((Qt::Alignment) txtProperties.namedItem("alignment").nodeValue().toInt());
274                         cur.select(QTextCursor::Document);
275                         cur.setBlockFormat(format);
276                         txt->setTextCursor(cur);
277                         cur.clearSelection();
278                         txt->setTextCursor(cur);
279                     }
280
281                     if (!txtProperties.namedItem("kdenlive-axis-x-inverted").isNull()) {
282                         txt->setData(OriginXLeft, txtProperties.namedItem("kdenlive-axis-x-inverted").nodeValue().toInt());
283                     }
284                     if (!txtProperties.namedItem("kdenlive-axis-y-inverted").isNull()) {
285                         txt->setData(OriginYTop, txtProperties.namedItem("kdenlive-axis-y-inverted").nodeValue().toInt());
286                     }
287
288                     gitem = txt;
289                 } else if (items.item(i).attributes().namedItem("type").nodeValue() == "QGraphicsRectItem") {
290                     QString rect = items.item(i).namedItem("content").attributes().namedItem("rect").nodeValue();
291                     QString br_str = items.item(i).namedItem("content").attributes().namedItem("brushcolor").nodeValue();
292                     QString pen_str = items.item(i).namedItem("content").attributes().namedItem("pencolor").nodeValue();
293                     double penwidth = items.item(i).namedItem("content").attributes().namedItem("penwidth").nodeValue().toDouble();
294                     QGraphicsRectItem *rec = m_scene->addRect(stringToRect(rect), QPen(QBrush(stringToColor(pen_str)), penwidth), QBrush(stringToColor(br_str)));
295                     gitem = rec;
296                 } else if (items.item(i).attributes().namedItem("type").nodeValue() == "QGraphicsPixmapItem") {
297                     QString url = items.item(i).namedItem("content").attributes().namedItem("url").nodeValue();
298                     QPixmap pix(url);
299                     QGraphicsPixmapItem *rec = m_scene->addPixmap(pix);
300                     rec->setData(Qt::UserRole, url);
301                     gitem = rec;
302                 } else if (items.item(i).attributes().namedItem("type").nodeValue() == "QGraphicsSvgItem") {
303                     QString url = items.item(i).namedItem("content").attributes().namedItem("url").nodeValue();
304                     QGraphicsSvgItem *rec = new QGraphicsSvgItem(url);
305                     m_scene->addItem(rec);
306                     rec->setData(Qt::UserRole, url);
307                     gitem = rec;
308                 }
309             }
310             //pos and transform
311             if (gitem) {
312                 QPointF p(items.item(i).namedItem("position").attributes().namedItem("x").nodeValue().toDouble(),
313                           items.item(i).namedItem("position").attributes().namedItem("y").nodeValue().toDouble());
314                 gitem->setPos(p);
315                 gitem->setTransform(stringToTransform(items.item(i).namedItem("position").firstChild().firstChild().nodeValue()));
316                 int zValue = items.item(i).attributes().namedItem("z-index").nodeValue().toInt();
317                 if (zValue > maxZValue) maxZValue = zValue;
318                 gitem->setZValue(zValue);
319                 gitem->setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable);
320             }
321             if (items.item(i).nodeName() == "background") {
322                 kDebug() << items.item(i).attributes().namedItem("color").nodeValue();
323                 QColor color = QColor(stringToColor(items.item(i).attributes().namedItem("color").nodeValue()));
324                 //color.setAlpha(items.item(i).attributes().namedItem("alpha").nodeValue().toInt());
325                 QList<QGraphicsItem *> items = m_scene->items();
326                 for (int i = 0; i < items.size(); i++) {
327                     if (items.at(i)->zValue() == -1100) {
328                         ((QGraphicsRectItem *)items.at(i))->setBrush(QBrush(color));
329                         break;
330                     }
331                 }
332             } else if (items.item(i).nodeName() == "startviewport" && startv) {
333                 QString rect = items.item(i).attributes().namedItem("rect").nodeValue();
334                 startv->setPolygon(stringToRect(rect));
335                 int x = items.item(i).attributes().namedItem("x").nodeValue().toInt();
336                 int y = items.item(i).attributes().namedItem("y").nodeValue().toInt();
337                 int size = items.item(i).attributes().namedItem("size").nodeValue().toInt();
338                 startv->setData(0, x);
339                 startv->setData(1, y);
340                 startv->setData(2, size);
341                 //startv->setPos(p);
342             } else if (items.item(i).nodeName() == "endviewport" && endv) {
343                 QString rect = items.item(i).attributes().namedItem("rect").nodeValue();
344                 endv->setPolygon(stringToRect(rect));
345                 int x = items.item(i).attributes().namedItem("x").nodeValue().toInt();
346                 int y = items.item(i).attributes().namedItem("y").nodeValue().toInt();
347                 int size = items.item(i).attributes().namedItem("size").nodeValue().toInt();
348                 endv->setData(0, x);
349                 endv->setData(1, y);
350                 endv->setData(2, size);
351             }
352         }
353     }
354     return maxZValue;
355 }
356
357 QString TitleDocument::colorToString(const QColor& c)
358 {
359     QString ret = "%1,%2,%3,%4";
360     ret = ret.arg(c.red()).arg(c.green()).arg(c.blue()).arg(c.alpha());
361     return ret;
362 }
363
364 QString TitleDocument::rectFToString(const QRectF& c)
365 {
366     QString ret = "%1,%2,%3,%4";
367     ret = ret.arg(c.left()).arg(c.top()).arg(c.width()).arg(c.height());
368     return ret;
369 }
370
371 QRectF TitleDocument::stringToRect(const QString & s)
372 {
373
374     QStringList l = s.split(',');
375     if (l.size() < 4)
376         return QRectF();
377     return QRectF(l.at(0).toDouble(), l.at(1).toDouble(), l.at(2).toDouble(), l.at(3).toDouble()).normalized();
378 }
379
380 QColor TitleDocument::stringToColor(const QString & s)
381 {
382     QStringList l = s.split(',');
383     if (l.size() < 4)
384         return QColor();
385     return QColor(l.at(0).toInt(), l.at(1).toInt(), l.at(2).toInt(), l.at(3).toInt());;
386 }
387 QTransform TitleDocument::stringToTransform(const QString& s)
388 {
389     QStringList l = s.split(',');
390     if (l.size() < 9)
391         return QTransform();
392     return QTransform(
393                l.at(0).toDouble(), l.at(1).toDouble(), l.at(2).toDouble(),
394                l.at(3).toDouble(), l.at(4).toDouble(), l.at(5).toDouble(),
395                l.at(6).toDouble(), l.at(7).toDouble(), l.at(8).toDouble()
396            );
397 }