]> git.sesse.net Git - kdenlive/blob - src/titledocument.cpp
Fix opening of title with profile different than current one
[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."), i18n("Title Profile"));
224             //TODO: convert using QTransform
225             m_width = doc_width;
226             m_height = doc_height;
227         }
228     }
229     //TODO: get default title duration instead of hardcoded one
230     if (doc.documentElement().hasAttribute("out"))
231         *out = doc.documentElement().attribute("out").toDouble();
232     else
233         *out = 5000;
234
235     int maxZValue = 0;
236     if (titles.size()) {
237
238         QDomNodeList items = titles.item(0).childNodes();
239         for (int i = 0; i < items.count(); i++) {
240             QGraphicsItem *gitem = NULL;
241             kDebug() << items.item(i).attributes().namedItem("type").nodeValue();
242             int zValue = items.item(i).attributes().namedItem("z-index").nodeValue().toInt();
243             if (zValue > -1000) {
244                 if (items.item(i).attributes().namedItem("type").nodeValue() == "QGraphicsTextItem") {
245                     QDomNamedNodeMap txtProperties = items.item(i).namedItem("content").attributes();
246                     QFont font(txtProperties.namedItem("font").nodeValue());
247
248                     QDomNode node = txtProperties.namedItem("font-bold");
249                     if (!node.isNull()) {
250                         // Old: Bold/Not bold.
251                         font.setBold(node.nodeValue().toInt());
252                     } else {
253                         // New: Font weight (QFont::)
254                         font.setWeight(txtProperties.namedItem("font-weight").nodeValue().toInt());
255                     }
256                     //font.setBold(txtProperties.namedItem("font-bold").nodeValue().toInt());
257                     font.setItalic(txtProperties.namedItem("font-italic").nodeValue().toInt());
258                     font.setUnderline(txtProperties.namedItem("font-underline").nodeValue().toInt());
259                     // Older Kdenlive version did not store pixel size but point size
260                     if (txtProperties.namedItem("font-pixel-size").isNull()) {
261                         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"));
262                         QFont f2;
263                         f2.setPointSize(txtProperties.namedItem("font-size").nodeValue().toInt());
264                         font.setPixelSize(QFontInfo(f2).pixelSize());
265                     } else
266                         font.setPixelSize(txtProperties.namedItem("font-pixel-size").nodeValue().toInt());
267                     QColor col(stringToColor(txtProperties.namedItem("font-color").nodeValue()));
268                     QGraphicsTextItem *txt = m_scene->addText(items.item(i).namedItem("content").firstChild().nodeValue(), font);
269                     txt->setDefaultTextColor(col);
270                     txt->setTextInteractionFlags(Qt::NoTextInteraction);
271                     if (txtProperties.namedItem("alignment").isNull() == false) {
272                         txt->setTextWidth(txt->boundingRect().width());
273                         QTextCursor cur = txt->textCursor();
274                         QTextBlockFormat format = cur.blockFormat();
275                         format.setAlignment((Qt::Alignment) txtProperties.namedItem("alignment").nodeValue().toInt());
276                         cur.select(QTextCursor::Document);
277                         cur.setBlockFormat(format);
278                         txt->setTextCursor(cur);
279                         cur.clearSelection();
280                         txt->setTextCursor(cur);
281                     }
282
283                     if (!txtProperties.namedItem("kdenlive-axis-x-inverted").isNull()) {
284                         txt->setData(OriginXLeft, txtProperties.namedItem("kdenlive-axis-x-inverted").nodeValue().toInt());
285                     }
286                     if (!txtProperties.namedItem("kdenlive-axis-y-inverted").isNull()) {
287                         txt->setData(OriginYTop, txtProperties.namedItem("kdenlive-axis-y-inverted").nodeValue().toInt());
288                     }
289
290                     gitem = txt;
291                 } else if (items.item(i).attributes().namedItem("type").nodeValue() == "QGraphicsRectItem") {
292                     QString rect = items.item(i).namedItem("content").attributes().namedItem("rect").nodeValue();
293                     QString br_str = items.item(i).namedItem("content").attributes().namedItem("brushcolor").nodeValue();
294                     QString pen_str = items.item(i).namedItem("content").attributes().namedItem("pencolor").nodeValue();
295                     double penwidth = items.item(i).namedItem("content").attributes().namedItem("penwidth").nodeValue().toDouble();
296                     QGraphicsRectItem *rec = m_scene->addRect(stringToRect(rect), QPen(QBrush(stringToColor(pen_str)), penwidth), QBrush(stringToColor(br_str)));
297                     gitem = rec;
298                 } else if (items.item(i).attributes().namedItem("type").nodeValue() == "QGraphicsPixmapItem") {
299                     QString url = items.item(i).namedItem("content").attributes().namedItem("url").nodeValue();
300                     QPixmap pix(url);
301                     QGraphicsPixmapItem *rec = m_scene->addPixmap(pix);
302                     rec->setData(Qt::UserRole, url);
303                     gitem = rec;
304                 } else if (items.item(i).attributes().namedItem("type").nodeValue() == "QGraphicsSvgItem") {
305                     QString url = items.item(i).namedItem("content").attributes().namedItem("url").nodeValue();
306                     QGraphicsSvgItem *rec = new QGraphicsSvgItem(url);
307                     m_scene->addItem(rec);
308                     rec->setData(Qt::UserRole, url);
309                     gitem = rec;
310                 }
311             }
312             //pos and transform
313             if (gitem) {
314                 QPointF p(items.item(i).namedItem("position").attributes().namedItem("x").nodeValue().toDouble(),
315                           items.item(i).namedItem("position").attributes().namedItem("y").nodeValue().toDouble());
316                 gitem->setPos(p);
317                 gitem->setTransform(stringToTransform(items.item(i).namedItem("position").firstChild().firstChild().nodeValue()));
318                 int zValue = items.item(i).attributes().namedItem("z-index").nodeValue().toInt();
319                 if (zValue > maxZValue) maxZValue = zValue;
320                 gitem->setZValue(zValue);
321                 gitem->setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable);
322             }
323             if (items.item(i).nodeName() == "background") {
324                 kDebug() << items.item(i).attributes().namedItem("color").nodeValue();
325                 QColor color = QColor(stringToColor(items.item(i).attributes().namedItem("color").nodeValue()));
326                 //color.setAlpha(items.item(i).attributes().namedItem("alpha").nodeValue().toInt());
327                 QList<QGraphicsItem *> items = m_scene->items();
328                 for (int i = 0; i < items.size(); i++) {
329                     if (items.at(i)->zValue() == -1100) {
330                         ((QGraphicsRectItem *)items.at(i))->setBrush(QBrush(color));
331                         break;
332                     }
333                 }
334             } else if (items.item(i).nodeName() == "startviewport" && startv) {
335                 QString rect = items.item(i).attributes().namedItem("rect").nodeValue();
336                 startv->setPolygon(stringToRect(rect));
337                 int x = items.item(i).attributes().namedItem("x").nodeValue().toInt();
338                 int y = items.item(i).attributes().namedItem("y").nodeValue().toInt();
339                 int size = items.item(i).attributes().namedItem("size").nodeValue().toInt();
340                 startv->setData(0, x);
341                 startv->setData(1, y);
342                 startv->setData(2, size);
343                 //startv->setPos(p);
344             } else if (items.item(i).nodeName() == "endviewport" && endv) {
345                 QString rect = items.item(i).attributes().namedItem("rect").nodeValue();
346                 endv->setPolygon(stringToRect(rect));
347                 int x = items.item(i).attributes().namedItem("x").nodeValue().toInt();
348                 int y = items.item(i).attributes().namedItem("y").nodeValue().toInt();
349                 int size = items.item(i).attributes().namedItem("size").nodeValue().toInt();
350                 endv->setData(0, x);
351                 endv->setData(1, y);
352                 endv->setData(2, size);
353             }
354         }
355     }
356     return maxZValue;
357 }
358
359 QString TitleDocument::colorToString(const QColor& c)
360 {
361     QString ret = "%1,%2,%3,%4";
362     ret = ret.arg(c.red()).arg(c.green()).arg(c.blue()).arg(c.alpha());
363     return ret;
364 }
365
366 QString TitleDocument::rectFToString(const QRectF& c)
367 {
368     QString ret = "%1,%2,%3,%4";
369     ret = ret.arg(c.left()).arg(c.top()).arg(c.width()).arg(c.height());
370     return ret;
371 }
372
373 QRectF TitleDocument::stringToRect(const QString & s)
374 {
375
376     QStringList l = s.split(',');
377     if (l.size() < 4)
378         return QRectF();
379     return QRectF(l.at(0).toDouble(), l.at(1).toDouble(), l.at(2).toDouble(), l.at(3).toDouble()).normalized();
380 }
381
382 QColor TitleDocument::stringToColor(const QString & s)
383 {
384     QStringList l = s.split(',');
385     if (l.size() < 4)
386         return QColor();
387     return QColor(l.at(0).toInt(), l.at(1).toInt(), l.at(2).toInt(), l.at(3).toInt());;
388 }
389 QTransform TitleDocument::stringToTransform(const QString& s)
390 {
391     QStringList l = s.split(',');
392     if (l.size() < 9)
393         return QTransform();
394     return QTransform(
395                l.at(0).toDouble(), l.at(1).toDouble(), l.at(2).toDouble(),
396                l.at(3).toDouble(), l.at(4).toDouble(), l.at(5).toDouble(),
397                l.at(6).toDouble(), l.at(7).toDouble(), l.at(8).toDouble()
398            );
399 }
400
401 int TitleDocument::frameWidth() const
402 {
403     return m_width;
404 }
405
406 int TitleDocument::frameHeight() const
407 {
408     return m_height;
409 }
410
411