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