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