]> git.sesse.net Git - kdenlive/blob - src/titledocument.cpp
initial work for titleproducer
[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-bold", font.bold());
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                     font.setBold(txtProperties.namedItem("font-bold").nodeValue().toInt());
226                     font.setItalic(txtProperties.namedItem("font-italic").nodeValue().toInt());
227                     font.setUnderline(txtProperties.namedItem("font-underline").nodeValue().toInt());
228                     // Older Kdenlive version did not store pixel size but point size
229                     if (txtProperties.namedItem("font-pixel-size").isNull()) {
230                         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"));
231                         QFont f2;
232                         f2.setPointSize(txtProperties.namedItem("font-size").nodeValue().toInt());
233                         font.setPixelSize(QFontInfo(f2).pixelSize());
234                     } else
235                         font.setPixelSize(txtProperties.namedItem("font-pixel-size").nodeValue().toInt());
236                     QColor col(stringToColor(txtProperties.namedItem("font-color").nodeValue()));
237                     QGraphicsTextItem *txt = m_scene->addText(items.item(i).namedItem("content").firstChild().nodeValue(), font);
238                     txt->setDefaultTextColor(col);
239                     txt->setTextInteractionFlags(Qt::NoTextInteraction);
240                     if (txtProperties.namedItem("alignment").isNull() == false) {
241                         txt->setTextWidth(txt->boundingRect().width());
242                         QTextCursor cur = txt->textCursor();
243                         QTextBlockFormat format = cur.blockFormat();
244                         format.setAlignment((Qt::Alignment) txtProperties.namedItem("alignment").nodeValue().toInt());
245                         cur.select(QTextCursor::Document);
246                         cur.setBlockFormat(format);
247                         txt->setTextCursor(cur);
248                         cur.clearSelection();
249                         txt->setTextCursor(cur);
250                     }
251
252                     if (!txtProperties.namedItem("kdenlive-axis-x-inverted").isNull()) {
253                         txt->setData(OriginXLeft, txtProperties.namedItem("kdenlive-axis-x-inverted").nodeValue().toInt());
254                     }
255                     if (!txtProperties.namedItem("kdenlive-axis-y-inverted").isNull()) {
256                         txt->setData(OriginYTop, txtProperties.namedItem("kdenlive-axis-y-inverted").nodeValue().toInt());
257                     }
258
259                     gitem = txt;
260                 } else if (items.item(i).attributes().namedItem("type").nodeValue() == "QGraphicsRectItem") {
261                     QString rect = items.item(i).namedItem("content").attributes().namedItem("rect").nodeValue();
262                     QString br_str = items.item(i).namedItem("content").attributes().namedItem("brushcolor").nodeValue();
263                     QString pen_str = items.item(i).namedItem("content").attributes().namedItem("pencolor").nodeValue();
264                     double penwidth = items.item(i).namedItem("content").attributes().namedItem("penwidth").nodeValue().toDouble();
265                     QGraphicsRectItem *rec = m_scene->addRect(stringToRect(rect), QPen(QBrush(stringToColor(pen_str)), penwidth), QBrush(stringToColor(br_str)));
266                     gitem = rec;
267                 } else if (items.item(i).attributes().namedItem("type").nodeValue() == "QGraphicsPixmapItem") {
268                     QString url = items.item(i).namedItem("content").attributes().namedItem("url").nodeValue();
269                     QPixmap pix(url);
270                     QGraphicsPixmapItem *rec = m_scene->addPixmap(pix);
271                     rec->setData(Qt::UserRole, url);
272                     gitem = rec;
273                 } else if (items.item(i).attributes().namedItem("type").nodeValue() == "QGraphicsSvgItem") {
274                     QString url = items.item(i).namedItem("content").attributes().namedItem("url").nodeValue();
275                     QGraphicsSvgItem *rec = new QGraphicsSvgItem(url);
276                     m_scene->addItem(rec);
277                     rec->setData(Qt::UserRole, url);
278                     gitem = rec;
279                 }
280             }
281             //pos and transform
282             if (gitem) {
283                 QPointF p(items.item(i).namedItem("position").attributes().namedItem("x").nodeValue().toDouble(),
284                           items.item(i).namedItem("position").attributes().namedItem("y").nodeValue().toDouble());
285                 gitem->setPos(p);
286                 gitem->setTransform(stringToTransform(items.item(i).namedItem("position").firstChild().firstChild().nodeValue()));
287                 int zValue = items.item(i).attributes().namedItem("z-index").nodeValue().toInt();
288                 if (zValue > maxZValue) maxZValue = zValue;
289                 gitem->setZValue(zValue);
290                 gitem->setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable);
291             }
292             if (items.item(i).nodeName() == "background") {
293                 kDebug() << items.item(i).attributes().namedItem("color").nodeValue();
294                 QColor color = QColor(stringToColor(items.item(i).attributes().namedItem("color").nodeValue()));
295                 //color.setAlpha(items.item(i).attributes().namedItem("alpha").nodeValue().toInt());
296                 QList<QGraphicsItem *> items = m_scene->items();
297                 for (int i = 0; i < items.size(); i++) {
298                     if (items.at(i)->zValue() == -1100) {
299                         ((QGraphicsRectItem *)items.at(i))->setBrush(QBrush(color));
300                         break;
301                     }
302                 }
303             } else if (items.item(i).nodeName() == "startviewport" && startv) {
304                     QString rect = items.item(i).attributes().namedItem("rect").nodeValue();
305                     startv->setPolygon(stringToRect(rect));
306                     int x = items.item(i).attributes().namedItem("x").nodeValue().toInt();
307                     int y = items.item(i).attributes().namedItem("y").nodeValue().toInt();
308                     int size = items.item(i).attributes().namedItem("size").nodeValue().toInt();
309                     startv->setData(0,x);
310                     startv->setData(1,y);
311                     startv->setData(2,size);
312                     //startv->setPos(p);
313                 } else if (items.item(i).nodeName() == "endviewport" && endv) {
314                     QString rect = items.item(i).attributes().namedItem("rect").nodeValue();
315                     endv->setPolygon(stringToRect(rect));
316                     int x = items.item(i).attributes().namedItem("x").nodeValue().toInt();
317                     int y = items.item(i).attributes().namedItem("y").nodeValue().toInt();
318                     int size = items.item(i).attributes().namedItem("size").nodeValue().toInt();
319                     endv->setData(0,x);
320                     endv->setData(1,y);
321                     endv->setData(2,size);
322                 }
323         }
324     }
325     return maxZValue;
326 }
327
328 QString TitleDocument::colorToString(const QColor& c)
329 {
330     QString ret = "%1,%2,%3,%4";
331     ret = ret.arg(c.red()).arg(c.green()).arg(c.blue()).arg(c.alpha());
332     return ret;
333 }
334
335 QString TitleDocument::rectFToString(const QRectF& c)
336 {
337     QString ret = "%1,%2,%3,%4";
338     ret = ret.arg(c.left()).arg(c.top()).arg(c.width()).arg(c.height());
339     return ret;
340 }
341
342 QRectF TitleDocument::stringToRect(const QString & s)
343 {
344
345     QStringList l = s.split(',');
346     if (l.size() < 4)
347         return QRectF();
348     return QRectF(l.at(0).toDouble(), l.at(1).toDouble(), l.at(2).toDouble(), l.at(3).toDouble()).normalized();
349 }
350
351 QColor TitleDocument::stringToColor(const QString & s)
352 {
353     QStringList l = s.split(',');
354     if (l.size() < 4)
355         return QColor();
356     return QColor(l.at(0).toInt(), l.at(1).toInt(), l.at(2).toInt(), l.at(3).toInt());;
357 }
358 QTransform TitleDocument::stringToTransform(const QString& s)
359 {
360     QStringList l = s.split(',');
361     if (l.size() < 9)
362         return QTransform();
363     return QTransform(
364                l.at(0).toDouble(), l.at(1).toDouble(), l.at(2).toDouble(),
365                l.at(3).toDouble(), l.at(4).toDouble(), l.at(5).toDouble(),
366                l.at(6).toDouble(), l.at(7).toDouble(), l.at(8).toDouble()
367            );
368 }