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