1 /***************************************************************************
2 titledocument.h - description
5 copyright : (C) 2008 by Marco Gittler
6 email : g.marco@freenet.de
7 ***************************************************************************/
9 /***************************************************************************
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. *
16 ***************************************************************************/
18 #include "titledocument.h"
21 #include <KTemporaryFile>
22 #include <kio/netaccess.h>
23 #include <KApplication>
25 #include <KMessageBox>
27 #include <QGraphicsScene>
28 #include <QDomElement>
29 #include <QGraphicsItem>
30 #include <QGraphicsRectItem>
31 #include <QGraphicsTextItem>
32 #include <QGraphicsSvgItem>
35 #include <QTextCursor>
37 #if QT_VERSION >= 0x040600
38 #include <QGraphicsEffect>
39 #include <QGraphicsBlurEffect>
40 #include <QGraphicsDropShadowEffect>
43 TitleDocument::TitleDocument()
48 void TitleDocument::setScene(QGraphicsScene* _scene, int width, int height)
55 QDomDocument TitleDocument::xml(QGraphicsRectItem* startv, QGraphicsRectItem* endv)
59 QDomElement main = doc.createElement("kdenlivetitle");
60 main.setAttribute("width", m_width);
61 main.setAttribute("height", m_height);
62 doc.appendChild(main);
64 foreach(QGraphicsItem* item, m_scene->items()) {
65 QDomElement e = doc.createElement("item");
66 QDomElement content = doc.createElement("content");
70 switch (item->type()) {
72 e.setAttribute("type", "QGraphicsPixmapItem");
73 content.setAttribute("url", item->data(Qt::UserRole).toString());
76 e.setAttribute("type", "QGraphicsSvgItem");
77 content.setAttribute("url", item->data(Qt::UserRole).toString());
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()));
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()));
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());
100 QTextCursor cursor(t->document());
101 cursor.select(QTextCursor::Document);
102 QColor fontcolor = cursor.charFormat().foreground().color();
103 content.setAttribute("font-color", colorToString(fontcolor));
104 if (!t->data(101).isNull()) content.setAttribute("font-outline", t->data(101).toDouble());
105 if (!t->data(102).isNull()) content.setAttribute("font-outline-color", colorToString(QColor(t->data(102).toString())));
107 if (!t->data(100).isNull()) {
108 QStringList effectParams = t->data(100).toStringList();
109 QString effectName = effectParams.takeFirst();
110 content.setAttribute(effectName, effectParams.join(";"));
113 // Only save when necessary.
114 if (t->data(OriginXLeft).toInt() == AxisInverted) {
115 content.setAttribute("kdenlive-axis-x-inverted", t->data(OriginXLeft).toInt());
117 if (t->data(OriginYTop).toInt() == AxisInverted) {
118 content.setAttribute("kdenlive-axis-y-inverted", t->data(OriginYTop).toInt());
120 if (t->textWidth() != -1) {
121 content.setAttribute("alignment", t->textCursor().blockFormat().alignment());
129 QDomElement pos = doc.createElement("position");
130 pos.setAttribute("x", item->pos().x());
131 pos.setAttribute("y", item->pos().y());
132 QTransform transform = item->transform();
133 QDomElement tr = doc.createElement("transform");
134 if (!item->data(ZOOMFACTOR).isNull()) {
135 tr.setAttribute("zoom", QString::number(item->data(ZOOMFACTOR).toInt()));
137 if (!item->data(ROTATEFACTOR).isNull()) {
138 QList<QVariant> rotlist = item->data(ROTATEFACTOR).toList();
139 tr.setAttribute("rotation", QString("%1,%2,%3").arg(rotlist[0].toDouble()).arg(rotlist[1].toDouble()).arg(rotlist[2].toDouble()));
141 tr.appendChild(doc.createTextNode(
142 QString("%1,%2,%3,%4,%5,%6,%7,%8,%9").arg(
143 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())
146 e.setAttribute("z-index", item->zValue());
149 #if QT_VERSION >= 0x040600
151 QGraphicsEffect *eff = item->graphicsEffect();
153 QGraphicsBlurEffect *blur = static_cast <QGraphicsBlurEffect *>(eff);
154 QDomElement effect = doc.createElement("effect");
156 effect.setAttribute("type", "blur");
157 effect.setAttribute("blurradius", blur->blurRadius());
159 QGraphicsDropShadowEffect *shadow = static_cast <QGraphicsDropShadowEffect *>(eff);
161 effect.setAttribute("type", "shadow");
162 effect.setAttribute("blurradius", shadow->blurRadius());
163 effect.setAttribute("xoffset", shadow->xOffset());
164 effect.setAttribute("yoffset", shadow->yOffset());
167 e.appendChild(effect);
172 e.appendChild(content);
173 if (item->zValue() > -1000) main.appendChild(e);
175 if (startv && endv) {
176 QDomElement endp = doc.createElement("endviewport");
177 QDomElement startp = doc.createElement("startviewport");
178 QRectF r(endv->pos().x(), endv->pos().y(), endv->rect().width(), endv->rect().height());
179 endp.setAttribute("rect", rectFToString(r));
180 QRectF r2(startv->pos().x(), startv->pos().y(), startv->rect().width(), startv->rect().height());
181 startp.setAttribute("rect", rectFToString(r2));
183 main.appendChild(startp);
184 main.appendChild(endp);
186 QDomElement backgr = doc.createElement("background");
187 QColor color = getBackgroundColor();
188 backgr.setAttribute("color", colorToString(color));
189 main.appendChild(backgr);
194 /** \brief Get the background color (incl. alpha) from the document, if possibly
195 * \returns The background color of the document, inclusive alpha. If none found, returns (0,0,0,0) */
196 QColor TitleDocument::getBackgroundColor()
198 QColor color(0, 0, 0, 0);
200 QList<QGraphicsItem *> items = m_scene->items();
201 for (int i = 0; i < items.size(); i++) {
202 if (items.at(i)->zValue() == -1100) {
203 color = ((QGraphicsRectItem *)items.at(i))->brush().color();
212 bool TitleDocument::saveDocument(const KUrl& url, QGraphicsRectItem* startv, QGraphicsRectItem* endv, int out)
217 QDomDocument doc = xml(startv, endv);
218 doc.documentElement().setAttribute("out", out);
219 KTemporaryFile tmpfile;
220 if (!tmpfile.open()) {
221 kWarning() << "///// CANNOT CREATE TMP FILE in: " << tmpfile.fileName();
224 QFile xmlf(tmpfile.fileName());
225 xmlf.open(QIODevice::WriteOnly);
226 xmlf.write(doc.toString().toUtf8());
227 if (xmlf.error() != QFile::NoError) {
232 return KIO::NetAccess::upload(tmpfile.fileName(), url, 0);
235 int TitleDocument::loadFromXml(QDomDocument doc, QGraphicsRectItem* startv, QGraphicsRectItem* endv, int *out)
237 QDomNodeList titles = doc.elementsByTagName("kdenlivetitle");
238 //TODO: Check if the opened title size is equal to project size, otherwise warn user and rescale
239 if (doc.documentElement().hasAttribute("width") && doc.documentElement().hasAttribute("height")) {
240 int doc_width = doc.documentElement().attribute("width").toInt();
241 int doc_height = doc.documentElement().attribute("height").toInt();
242 if (doc_width != m_width || doc_height != m_height) {
243 KMessageBox::information(kapp->activeWindow(), i18n("This title clip was created with a different frame size."), i18n("Title Profile"));
244 //TODO: convert using QTransform
246 m_height = doc_height;
249 //TODO: get default title duration instead of hardcoded one
250 if (doc.documentElement().hasAttribute("out"))
251 *out = doc.documentElement().attribute("out").toInt();
258 QDomNodeList items = titles.item(0).childNodes();
259 for (int i = 0; i < items.count(); i++) {
260 QGraphicsItem *gitem = NULL;
261 kDebug() << items.item(i).attributes().namedItem("type").nodeValue();
262 int zValue = items.item(i).attributes().namedItem("z-index").nodeValue().toInt();
263 if (zValue > -1000) {
264 if (items.item(i).attributes().namedItem("type").nodeValue() == "QGraphicsTextItem") {
265 QDomNamedNodeMap txtProperties = items.item(i).namedItem("content").attributes();
266 QFont font(txtProperties.namedItem("font").nodeValue());
268 QDomNode node = txtProperties.namedItem("font-bold");
269 if (!node.isNull()) {
270 // Old: Bold/Not bold.
271 font.setBold(node.nodeValue().toInt());
273 // New: Font weight (QFont::)
274 font.setWeight(txtProperties.namedItem("font-weight").nodeValue().toInt());
276 //font.setBold(txtProperties.namedItem("font-bold").nodeValue().toInt());
277 font.setItalic(txtProperties.namedItem("font-italic").nodeValue().toInt());
278 font.setUnderline(txtProperties.namedItem("font-underline").nodeValue().toInt());
279 // Older Kdenlive version did not store pixel size but point size
280 if (txtProperties.namedItem("font-pixel-size").isNull()) {
281 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"));
283 f2.setPointSize(txtProperties.namedItem("font-size").nodeValue().toInt());
284 font.setPixelSize(QFontInfo(f2).pixelSize());
286 font.setPixelSize(txtProperties.namedItem("font-pixel-size").nodeValue().toInt());
287 QColor col(stringToColor(txtProperties.namedItem("font-color").nodeValue()));
288 QGraphicsTextItem *txt = m_scene->addText(items.item(i).namedItem("content").firstChild().nodeValue(), font);
289 QTextCursor cursor(txt->document());
290 cursor.select(QTextCursor::Document);
291 QTextCharFormat format;
292 if (txtProperties.namedItem("font-outline").nodeValue().toDouble() > 0.0) {
293 txt->setData(101, txtProperties.namedItem("font-outline").nodeValue().toDouble());
294 txt->setData(102, stringToColor(txtProperties.namedItem("font-outline-color").nodeValue()));
295 format.setTextOutline(
296 QPen(QColor(stringToColor(txtProperties.namedItem("font-outline-color").nodeValue())),
297 txtProperties.namedItem("font-outline").nodeValue().toDouble(),
298 Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin)
302 format.setForeground(QBrush(col));
303 cursor.mergeCharFormat(format);
304 txt->setTextInteractionFlags(Qt::NoTextInteraction);
305 if (txtProperties.namedItem("alignment").isNull() == false) {
306 txt->setTextWidth(txt->boundingRect().width());
307 QTextCursor cur = txt->textCursor();
308 QTextBlockFormat format = cur.blockFormat();
309 format.setAlignment((Qt::Alignment) txtProperties.namedItem("alignment").nodeValue().toInt());
310 cur.select(QTextCursor::Document);
311 cur.setBlockFormat(format);
312 txt->setTextCursor(cur);
313 cur.clearSelection();
314 txt->setTextCursor(cur);
317 if (!txtProperties.namedItem("kdenlive-axis-x-inverted").isNull()) {
318 txt->setData(OriginXLeft, txtProperties.namedItem("kdenlive-axis-x-inverted").nodeValue().toInt());
320 if (!txtProperties.namedItem("kdenlive-axis-y-inverted").isNull()) {
321 txt->setData(OriginYTop, txtProperties.namedItem("kdenlive-axis-y-inverted").nodeValue().toInt());
325 if (!txtProperties.namedItem("typewriter").isNull()) {
326 QStringList effData = QStringList() << "typewriter" << txtProperties.namedItem("typewriter").nodeValue();
327 txt->setData(100, effData);
331 } else if (items.item(i).attributes().namedItem("type").nodeValue() == "QGraphicsRectItem") {
332 QString rect = items.item(i).namedItem("content").attributes().namedItem("rect").nodeValue();
333 QString br_str = items.item(i).namedItem("content").attributes().namedItem("brushcolor").nodeValue();
334 QString pen_str = items.item(i).namedItem("content").attributes().namedItem("pencolor").nodeValue();
335 double penwidth = items.item(i).namedItem("content").attributes().namedItem("penwidth").nodeValue().toDouble();
336 QGraphicsRectItem *rec = m_scene->addRect(stringToRect(rect), QPen(QBrush(stringToColor(pen_str)), penwidth, Qt::SolidLine, Qt::SquareCap, Qt::RoundJoin), QBrush(stringToColor(br_str)));
338 } else if (items.item(i).attributes().namedItem("type").nodeValue() == "QGraphicsPixmapItem") {
339 QString url = items.item(i).namedItem("content").attributes().namedItem("url").nodeValue();
341 QGraphicsPixmapItem *rec = m_scene->addPixmap(pix);
342 rec->setData(Qt::UserRole, url);
344 } else if (items.item(i).attributes().namedItem("type").nodeValue() == "QGraphicsSvgItem") {
345 QString url = items.item(i).namedItem("content").attributes().namedItem("url").nodeValue();
346 QGraphicsSvgItem *rec = new QGraphicsSvgItem(url);
347 m_scene->addItem(rec);
348 rec->setData(Qt::UserRole, url);
354 QPointF p(items.item(i).namedItem("position").attributes().namedItem("x").nodeValue().toDouble(),
355 items.item(i).namedItem("position").attributes().namedItem("y").nodeValue().toDouble());
357 QDomElement trans = items.item(i).namedItem("position").firstChild().toElement();
358 gitem->setTransform(stringToTransform(trans.firstChild().nodeValue()));
359 QString rotate = trans.attribute("rotation");
360 if (!rotate.isEmpty()) gitem->setData(ROTATEFACTOR, stringToList(rotate));
361 QString zoom = trans.attribute("zoom");
362 if (!zoom.isEmpty()) gitem->setData(ZOOMFACTOR, zoom.toInt());
363 int zValue = items.item(i).attributes().namedItem("z-index").nodeValue().toInt();
364 if (zValue > maxZValue) maxZValue = zValue;
365 gitem->setZValue(zValue);
366 gitem->setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable);
368 #if QT_VERSION >= 0x040600
370 QDomNode eff = items.item(i).namedItem("effect");
372 QDomElement e = eff.toElement();
373 if (e.attribute("type") == "blur") {
374 QGraphicsBlurEffect *blur = new QGraphicsBlurEffect();
375 blur->setBlurRadius(e.attribute("blurradius").toInt());
376 gitem->setGraphicsEffect(blur);
377 } else if (e.attribute("type") == "shadow") {
378 QGraphicsDropShadowEffect *shadow = new QGraphicsDropShadowEffect();
379 shadow->setBlurRadius(e.attribute("blurradius").toInt());
380 shadow->setOffset(e.attribute("xoffset").toInt(), e.attribute("yoffset").toInt());
381 gitem->setGraphicsEffect(shadow);
387 if (items.item(i).nodeName() == "background") {
388 kDebug() << items.item(i).attributes().namedItem("color").nodeValue();
389 QColor color = QColor(stringToColor(items.item(i).attributes().namedItem("color").nodeValue()));
390 //color.setAlpha(items.item(i).attributes().namedItem("alpha").nodeValue().toInt());
391 QList<QGraphicsItem *> items = m_scene->items();
392 for (int i = 0; i < items.size(); i++) {
393 if (items.at(i)->zValue() == -1100) {
394 ((QGraphicsRectItem *)items.at(i))->setBrush(QBrush(color));
398 } else if (items.item(i).nodeName() == "startviewport" && startv) {
399 QString rect = items.item(i).attributes().namedItem("rect").nodeValue();
400 QRectF r = stringToRect(rect);
401 startv->setRect(0, 0, r.width(), r.height());
402 startv->setPos(r.topLeft());
403 } else if (items.item(i).nodeName() == "endviewport" && endv) {
404 QString rect = items.item(i).attributes().namedItem("rect").nodeValue();
405 QRectF r = stringToRect(rect);
406 endv->setRect(0, 0, r.width(), r.height());
407 endv->setPos(r.topLeft());
414 QString TitleDocument::colorToString(const QColor& c)
416 QString ret = "%1,%2,%3,%4";
417 ret = ret.arg(c.red()).arg(c.green()).arg(c.blue()).arg(c.alpha());
421 QString TitleDocument::rectFToString(const QRectF& c)
423 QString ret = "%1,%2,%3,%4";
424 ret = ret.arg(c.left()).arg(c.top()).arg(c.width()).arg(c.height());
428 QRectF TitleDocument::stringToRect(const QString & s)
431 QStringList l = s.split(',');
434 return QRectF(l.at(0).toDouble(), l.at(1).toDouble(), l.at(2).toDouble(), l.at(3).toDouble()).normalized();
437 QColor TitleDocument::stringToColor(const QString & s)
439 QStringList l = s.split(',');
442 return QColor(l.at(0).toInt(), l.at(1).toInt(), l.at(2).toInt(), l.at(3).toInt());;
445 QTransform TitleDocument::stringToTransform(const QString& s)
447 QStringList l = s.split(',');
451 l.at(0).toDouble(), l.at(1).toDouble(), l.at(2).toDouble(),
452 l.at(3).toDouble(), l.at(4).toDouble(), l.at(5).toDouble(),
453 l.at(6).toDouble(), l.at(7).toDouble(), l.at(8).toDouble()
457 QList<QVariant> TitleDocument::stringToList(const QString & s)
459 QStringList l = s.split(',');
461 return QList<QVariant>();
462 return QList<QVariant>() << QVariant(l.at(0).toDouble()) << QVariant(l.at(1).toDouble()) << QVariant(l.at(2).toDouble());
465 int TitleDocument::frameWidth() const
470 int TitleDocument::frameHeight() const