]> git.sesse.net Git - kdenlive/blob - src/titledocument.cpp
Titler:
[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)
169 {
170     if (!m_scene)
171         return false;
172
173     QDomDocument doc = xml(startv, endv);
174     KTemporaryFile tmpfile;
175     if (!tmpfile.open()) {
176         kWarning() << "/////  CANNOT CREATE TMP FILE in: " << tmpfile.fileName();
177         return false;
178     }
179     QFile xmlf(tmpfile.fileName());
180     xmlf.open(QIODevice::WriteOnly);
181     xmlf.write(doc.toString().toUtf8());
182     if (xmlf.error() != QFile::NoError) {
183         xmlf.close();
184         return false;
185     }
186     xmlf.close();
187     return KIO::NetAccess::upload(tmpfile.fileName(), url, 0);
188 }
189
190 int TitleDocument::loadDocument(const KUrl& url, QGraphicsPolygonItem* startv, QGraphicsPolygonItem* endv)
191 {
192     QString tmpfile;
193     QDomDocument doc;
194     if (!m_scene)
195         return -1;
196
197     if (KIO::NetAccess::download(url, tmpfile, 0)) {
198         QFile file(tmpfile);
199         if (file.open(QIODevice::ReadOnly)) {
200             doc.setContent(&file, false);
201             file.close();
202         } else
203             return -1;
204         KIO::NetAccess::removeTempFile(tmpfile);
205         return loadFromXml(doc, startv, endv);
206     }
207     return -1;
208 }
209
210 int TitleDocument::loadFromXml(QDomDocument doc, QGraphicsPolygonItem* startv, QGraphicsPolygonItem* endv)
211 {
212     QDomNodeList titles = doc.elementsByTagName("kdenlivetitle");
213     int maxZValue = 0;
214     if (titles.size()) {
215
216         QDomNodeList items = titles.item(0).childNodes();
217         for (int i = 0; i < items.count(); i++) {
218             QGraphicsItem *gitem = NULL;
219             kDebug() << items.item(i).attributes().namedItem("type").nodeValue();
220             int zValue = items.item(i).attributes().namedItem("z-index").nodeValue().toInt();
221             if (zValue > -1000) {
222                 if (items.item(i).attributes().namedItem("type").nodeValue() == "QGraphicsTextItem") {
223                     QDomNamedNodeMap txtProperties = items.item(i).namedItem("content").attributes();
224                     QFont font(txtProperties.namedItem("font").nodeValue());
225
226                     QDomNode node = txtProperties.namedItem("font-bold");
227                     if (!node.isNull()) {
228                         // Old: Bold/Not bold.
229                         font.setBold(node.nodeValue().toInt());
230                     } else {
231                         // New: Font weight (QFont::)
232                         font.setWeight(txtProperties.namedItem("font-weight").nodeValue().toInt());
233                     }
234                     //font.setBold(txtProperties.namedItem("font-bold").nodeValue().toInt());
235                     font.setItalic(txtProperties.namedItem("font-italic").nodeValue().toInt());
236                     font.setUnderline(txtProperties.namedItem("font-underline").nodeValue().toInt());
237                     // Older Kdenlive version did not store pixel size but point size
238                     if (txtProperties.namedItem("font-pixel-size").isNull()) {
239                         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"));
240                         QFont f2;
241                         f2.setPointSize(txtProperties.namedItem("font-size").nodeValue().toInt());
242                         font.setPixelSize(QFontInfo(f2).pixelSize());
243                     } else
244                         font.setPixelSize(txtProperties.namedItem("font-pixel-size").nodeValue().toInt());
245                     QColor col(stringToColor(txtProperties.namedItem("font-color").nodeValue()));
246                     QGraphicsTextItem *txt = m_scene->addText(items.item(i).namedItem("content").firstChild().nodeValue(), font);
247                     txt->setDefaultTextColor(col);
248                     txt->setTextInteractionFlags(Qt::NoTextInteraction);
249                     if (txtProperties.namedItem("alignment").isNull() == false) {
250                         txt->setTextWidth(txt->boundingRect().width());
251                         QTextCursor cur = txt->textCursor();
252                         QTextBlockFormat format = cur.blockFormat();
253                         format.setAlignment((Qt::Alignment) txtProperties.namedItem("alignment").nodeValue().toInt());
254                         cur.select(QTextCursor::Document);
255                         cur.setBlockFormat(format);
256                         txt->setTextCursor(cur);
257                         cur.clearSelection();
258                         txt->setTextCursor(cur);
259                     }
260
261                     if (!txtProperties.namedItem("kdenlive-axis-x-inverted").isNull()) {
262                         txt->setData(OriginXLeft, txtProperties.namedItem("kdenlive-axis-x-inverted").nodeValue().toInt());
263                     }
264                     if (!txtProperties.namedItem("kdenlive-axis-y-inverted").isNull()) {
265                         txt->setData(OriginYTop, txtProperties.namedItem("kdenlive-axis-y-inverted").nodeValue().toInt());
266                     }
267
268                     gitem = txt;
269                 } else if (items.item(i).attributes().namedItem("type").nodeValue() == "QGraphicsRectItem") {
270                     QString rect = items.item(i).namedItem("content").attributes().namedItem("rect").nodeValue();
271                     QString br_str = items.item(i).namedItem("content").attributes().namedItem("brushcolor").nodeValue();
272                     QString pen_str = items.item(i).namedItem("content").attributes().namedItem("pencolor").nodeValue();
273                     double penwidth = items.item(i).namedItem("content").attributes().namedItem("penwidth").nodeValue().toDouble();
274                     QGraphicsRectItem *rec = m_scene->addRect(stringToRect(rect), QPen(QBrush(stringToColor(pen_str)), penwidth), QBrush(stringToColor(br_str)));
275                     gitem = rec;
276                 } else if (items.item(i).attributes().namedItem("type").nodeValue() == "QGraphicsPixmapItem") {
277                     QString url = items.item(i).namedItem("content").attributes().namedItem("url").nodeValue();
278                     QPixmap pix(url);
279                     QGraphicsPixmapItem *rec = m_scene->addPixmap(pix);
280                     rec->setData(Qt::UserRole, url);
281                     gitem = rec;
282                 } else if (items.item(i).attributes().namedItem("type").nodeValue() == "QGraphicsSvgItem") {
283                     QString url = items.item(i).namedItem("content").attributes().namedItem("url").nodeValue();
284                     QGraphicsSvgItem *rec = new QGraphicsSvgItem(url);
285                     m_scene->addItem(rec);
286                     rec->setData(Qt::UserRole, url);
287                     gitem = rec;
288                 }
289             }
290             //pos and transform
291             if (gitem) {
292                 QPointF p(items.item(i).namedItem("position").attributes().namedItem("x").nodeValue().toDouble(),
293                           items.item(i).namedItem("position").attributes().namedItem("y").nodeValue().toDouble());
294                 gitem->setPos(p);
295                 gitem->setTransform(stringToTransform(items.item(i).namedItem("position").firstChild().firstChild().nodeValue()));
296                 int zValue = items.item(i).attributes().namedItem("z-index").nodeValue().toInt();
297                 if (zValue > maxZValue) maxZValue = zValue;
298                 gitem->setZValue(zValue);
299                 gitem->setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable);
300             }
301             if (items.item(i).nodeName() == "background") {
302                 kDebug() << items.item(i).attributes().namedItem("color").nodeValue();
303                 QColor color = QColor(stringToColor(items.item(i).attributes().namedItem("color").nodeValue()));
304                 //color.setAlpha(items.item(i).attributes().namedItem("alpha").nodeValue().toInt());
305                 QList<QGraphicsItem *> items = m_scene->items();
306                 for (int i = 0; i < items.size(); i++) {
307                     if (items.at(i)->zValue() == -1100) {
308                         ((QGraphicsRectItem *)items.at(i))->setBrush(QBrush(color));
309                         break;
310                     }
311                 }
312             } else if (items.item(i).nodeName() == "startviewport" && startv) {
313                 QString rect = items.item(i).attributes().namedItem("rect").nodeValue();
314                 startv->setPolygon(stringToRect(rect));
315                 int x = items.item(i).attributes().namedItem("x").nodeValue().toInt();
316                 int y = items.item(i).attributes().namedItem("y").nodeValue().toInt();
317                 int size = items.item(i).attributes().namedItem("size").nodeValue().toInt();
318                 startv->setData(0, x);
319                 startv->setData(1, y);
320                 startv->setData(2, size);
321                 //startv->setPos(p);
322             } else if (items.item(i).nodeName() == "endviewport" && endv) {
323                 QString rect = items.item(i).attributes().namedItem("rect").nodeValue();
324                 endv->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                 endv->setData(0, x);
329                 endv->setData(1, y);
330                 endv->setData(2, size);
331             }
332         }
333     }
334     return maxZValue;
335 }
336
337 QString TitleDocument::colorToString(const QColor& c)
338 {
339     QString ret = "%1,%2,%3,%4";
340     ret = ret.arg(c.red()).arg(c.green()).arg(c.blue()).arg(c.alpha());
341     return ret;
342 }
343
344 QString TitleDocument::rectFToString(const QRectF& c)
345 {
346     QString ret = "%1,%2,%3,%4";
347     ret = ret.arg(c.left()).arg(c.top()).arg(c.width()).arg(c.height());
348     return ret;
349 }
350
351 QRectF TitleDocument::stringToRect(const QString & s)
352 {
353
354     QStringList l = s.split(',');
355     if (l.size() < 4)
356         return QRectF();
357     return QRectF(l.at(0).toDouble(), l.at(1).toDouble(), l.at(2).toDouble(), l.at(3).toDouble()).normalized();
358 }
359
360 QColor TitleDocument::stringToColor(const QString & s)
361 {
362     QStringList l = s.split(',');
363     if (l.size() < 4)
364         return QColor();
365     return QColor(l.at(0).toInt(), l.at(1).toInt(), l.at(2).toInt(), l.at(3).toInt());;
366 }
367 QTransform TitleDocument::stringToTransform(const QString& s)
368 {
369     QStringList l = s.split(',');
370     if (l.size() < 9)
371         return QTransform();
372     return QTransform(
373                l.at(0).toDouble(), l.at(1).toDouble(), l.at(2).toDouble(),
374                l.at(3).toDouble(), l.at(4).toDouble(), l.at(5).toDouble(),
375                l.at(6).toDouble(), l.at(7).toDouble(), l.at(8).toDouble()
376            );
377 }