]> git.sesse.net Git - kdenlive/blob - src/titledocument.cpp
Save scene dimensions in titles
[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     doc.documentElement().setAttribute("width", m_scene->width());
176     doc.documentElement().setAttribute("height", m_scene->height());
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 }