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());
102 QTextCursor cursor(t->document());
103 cursor.select(QTextCursor::Document);
104 QColor fontcolor = cursor.charFormat().foreground().color();
105 content.setAttribute("font-color", colorToString(fontcolor));
106 content.setAttribute("font-outline", t->data(101).toDouble());
107 content.setAttribute("font-outline-color", t->data(102).toString());
110 if (!t->data(100).isNull()) {
111 QStringList effectParams = t->data(100).toStringList();
112 QString effectName = effectParams.takeFirst();
113 content.setAttribute(effectName, effectParams.join(";"));
116 // Only save when necessary.
117 if (t->data(OriginXLeft).toInt() == AxisInverted) {
118 content.setAttribute("kdenlive-axis-x-inverted", t->data(OriginXLeft).toInt());
120 if (t->data(OriginYTop).toInt() == AxisInverted) {
121 content.setAttribute("kdenlive-axis-y-inverted", t->data(OriginYTop).toInt());
123 if (t->textWidth() != -1) {
124 content.setAttribute("alignment", t->textCursor().blockFormat().alignment());
132 QDomElement pos = doc.createElement("position");
133 pos.setAttribute("x", item->pos().x());
134 pos.setAttribute("y", item->pos().y());
135 QTransform transform = item->transform();
136 QDomElement tr = doc.createElement("transform");
137 tr.appendChild(doc.createTextNode(
138 QString("%1,%2,%3,%4,%5,%6,%7,%8,%9").arg(
139 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())
142 e.setAttribute("z-index", item->zValue());
145 #if QT_VERSION >= 0x040600
147 QGraphicsEffect *eff = item->graphicsEffect();
149 QGraphicsBlurEffect *blur = static_cast <QGraphicsBlurEffect *>(eff);
150 QDomElement effect = doc.createElement("effect");
152 effect.setAttribute("type", "blur");
153 effect.setAttribute("blurradius", blur->blurRadius());
155 QGraphicsDropShadowEffect *shadow = static_cast <QGraphicsDropShadowEffect *>(eff);
157 effect.setAttribute("type", "shadow");
158 effect.setAttribute("blurradius", shadow->blurRadius());
159 effect.setAttribute("xoffset", shadow->xOffset());
160 effect.setAttribute("yoffset", shadow->yOffset());
163 e.appendChild(effect);
168 e.appendChild(content);
169 if (item->zValue() > -1000) main.appendChild(e);
171 if (startv && endv) {
172 QDomElement endp = doc.createElement("endviewport");
173 QDomElement startp = doc.createElement("startviewport");
174 QRectF r(endv->pos().x(), endv->pos().y(), endv->rect().width(), endv->rect().height());
175 endp.setAttribute("rect", rectFToString(r));
176 QRectF r2(startv->pos().x(), startv->pos().y(), startv->rect().width(), startv->rect().height());
177 startp.setAttribute("rect", rectFToString(r2));
179 main.appendChild(startp);
180 main.appendChild(endp);
182 QDomElement backgr = doc.createElement("background");
183 QColor color = getBackgroundColor();
184 backgr.setAttribute("color", colorToString(color));
185 main.appendChild(backgr);
190 /** \brief Get the background color (incl. alpha) from the document, if possibly
191 * \returns The background color of the document, inclusive alpha. If none found, returns (0,0,0,0) */
192 QColor TitleDocument::getBackgroundColor()
194 QColor color(0, 0, 0, 0);
196 QList<QGraphicsItem *> items = m_scene->items();
197 for (int i = 0; i < items.size(); i++) {
198 if (items.at(i)->zValue() == -1100) {
199 color = ((QGraphicsRectItem *)items.at(i))->brush().color();
208 bool TitleDocument::saveDocument(const KUrl& url, QGraphicsRectItem* startv, QGraphicsRectItem* endv, int out)
213 QDomDocument doc = xml(startv, endv);
214 doc.documentElement().setAttribute("out", out);
215 KTemporaryFile tmpfile;
216 if (!tmpfile.open()) {
217 kWarning() << "///// CANNOT CREATE TMP FILE in: " << tmpfile.fileName();
220 QFile xmlf(tmpfile.fileName());
221 xmlf.open(QIODevice::WriteOnly);
222 xmlf.write(doc.toString().toUtf8());
223 if (xmlf.error() != QFile::NoError) {
228 return KIO::NetAccess::upload(tmpfile.fileName(), url, 0);
231 int TitleDocument::loadFromXml(QDomDocument doc, QGraphicsRectItem* startv, QGraphicsRectItem* endv, int *out)
233 QDomNodeList titles = doc.elementsByTagName("kdenlivetitle");
234 //TODO: Check if the opened title size is equal to project size, otherwise warn user and rescale
235 if (doc.documentElement().hasAttribute("width") && doc.documentElement().hasAttribute("height")) {
236 int doc_width = doc.documentElement().attribute("width").toInt();
237 int doc_height = doc.documentElement().attribute("height").toInt();
238 if (doc_width != m_width || doc_height != m_height) {
239 KMessageBox::information(kapp->activeWindow(), i18n("This title clip was created with a different frame size."), i18n("Title Profile"));
240 //TODO: convert using QTransform
242 m_height = doc_height;
245 //TODO: get default title duration instead of hardcoded one
246 if (doc.documentElement().hasAttribute("out"))
247 *out = doc.documentElement().attribute("out").toInt();
254 QDomNodeList items = titles.item(0).childNodes();
255 for (int i = 0; i < items.count(); i++) {
256 QGraphicsItem *gitem = NULL;
257 kDebug() << items.item(i).attributes().namedItem("type").nodeValue();
258 int zValue = items.item(i).attributes().namedItem("z-index").nodeValue().toInt();
259 if (zValue > -1000) {
260 if (items.item(i).attributes().namedItem("type").nodeValue() == "QGraphicsTextItem") {
261 QDomNamedNodeMap txtProperties = items.item(i).namedItem("content").attributes();
262 QFont font(txtProperties.namedItem("font").nodeValue());
264 QDomNode node = txtProperties.namedItem("font-bold");
265 if (!node.isNull()) {
266 // Old: Bold/Not bold.
267 font.setBold(node.nodeValue().toInt());
269 // New: Font weight (QFont::)
270 font.setWeight(txtProperties.namedItem("font-weight").nodeValue().toInt());
272 //font.setBold(txtProperties.namedItem("font-bold").nodeValue().toInt());
273 font.setItalic(txtProperties.namedItem("font-italic").nodeValue().toInt());
274 font.setUnderline(txtProperties.namedItem("font-underline").nodeValue().toInt());
275 // Older Kdenlive version did not store pixel size but point size
276 if (txtProperties.namedItem("font-pixel-size").isNull()) {
277 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"));
279 f2.setPointSize(txtProperties.namedItem("font-size").nodeValue().toInt());
280 font.setPixelSize(QFontInfo(f2).pixelSize());
282 font.setPixelSize(txtProperties.namedItem("font-pixel-size").nodeValue().toInt());
283 QColor col(stringToColor(txtProperties.namedItem("font-color").nodeValue()));
284 QGraphicsTextItem *txt = m_scene->addText(items.item(i).namedItem("content").firstChild().nodeValue(), font);
285 QTextCursor cursor(txt->document());
286 cursor.select(QTextCursor::Document);
287 QTextCharFormat format;
288 if (txtProperties.namedItem("font-outline").nodeValue().toDouble() > 0.0) {
289 txt->setData(101, txtProperties.namedItem("font-outline").nodeValue().toDouble());
290 txt->setData(102, stringToColor(txtProperties.namedItem("font-outline-color").nodeValue()));
291 format.setTextOutline(
292 QPen(QColor(stringToColor(txtProperties.namedItem("font-outline-color").nodeValue())),
293 txtProperties.namedItem("font-outline").nodeValue().toDouble())
297 format.setForeground(QBrush(col));
298 cursor.mergeCharFormat(format);
299 txt->setTextInteractionFlags(Qt::NoTextInteraction);
300 if (txtProperties.namedItem("alignment").isNull() == false) {
301 txt->setTextWidth(txt->boundingRect().width());
302 QTextCursor cur = txt->textCursor();
303 QTextBlockFormat format = cur.blockFormat();
304 format.setAlignment((Qt::Alignment) txtProperties.namedItem("alignment").nodeValue().toInt());
305 cur.select(QTextCursor::Document);
306 cur.setBlockFormat(format);
307 txt->setTextCursor(cur);
308 cur.clearSelection();
309 txt->setTextCursor(cur);
312 if (!txtProperties.namedItem("kdenlive-axis-x-inverted").isNull()) {
313 txt->setData(OriginXLeft, txtProperties.namedItem("kdenlive-axis-x-inverted").nodeValue().toInt());
315 if (!txtProperties.namedItem("kdenlive-axis-y-inverted").isNull()) {
316 txt->setData(OriginYTop, txtProperties.namedItem("kdenlive-axis-y-inverted").nodeValue().toInt());
320 if (!txtProperties.namedItem("typewriter").isNull()) {
321 QStringList effData = QStringList() << "typewriter" << txtProperties.namedItem("typewriter").nodeValue();
322 txt->setData(100, effData);
326 } else if (items.item(i).attributes().namedItem("type").nodeValue() == "QGraphicsRectItem") {
327 QString rect = items.item(i).namedItem("content").attributes().namedItem("rect").nodeValue();
328 QString br_str = items.item(i).namedItem("content").attributes().namedItem("brushcolor").nodeValue();
329 QString pen_str = items.item(i).namedItem("content").attributes().namedItem("pencolor").nodeValue();
330 double penwidth = items.item(i).namedItem("content").attributes().namedItem("penwidth").nodeValue().toDouble();
331 QGraphicsRectItem *rec = m_scene->addRect(stringToRect(rect), QPen(QBrush(stringToColor(pen_str)), penwidth), QBrush(stringToColor(br_str)));
333 } else if (items.item(i).attributes().namedItem("type").nodeValue() == "QGraphicsPixmapItem") {
334 QString url = items.item(i).namedItem("content").attributes().namedItem("url").nodeValue();
336 QGraphicsPixmapItem *rec = m_scene->addPixmap(pix);
337 rec->setData(Qt::UserRole, url);
339 } else if (items.item(i).attributes().namedItem("type").nodeValue() == "QGraphicsSvgItem") {
340 QString url = items.item(i).namedItem("content").attributes().namedItem("url").nodeValue();
341 QGraphicsSvgItem *rec = new QGraphicsSvgItem(url);
342 m_scene->addItem(rec);
343 rec->setData(Qt::UserRole, url);
349 QPointF p(items.item(i).namedItem("position").attributes().namedItem("x").nodeValue().toDouble(),
350 items.item(i).namedItem("position").attributes().namedItem("y").nodeValue().toDouble());
352 gitem->setTransform(stringToTransform(items.item(i).namedItem("position").firstChild().firstChild().nodeValue()));
353 int zValue = items.item(i).attributes().namedItem("z-index").nodeValue().toInt();
354 if (zValue > maxZValue) maxZValue = zValue;
355 gitem->setZValue(zValue);
356 gitem->setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable);
358 #if QT_VERSION >= 0x040600
360 QDomNode eff = items.item(i).namedItem("effect");
362 QDomElement e = eff.toElement();
363 if (e.attribute("type") == "blur") {
364 QGraphicsBlurEffect *blur = new QGraphicsBlurEffect();
365 blur->setBlurRadius(e.attribute("blurradius").toInt());
366 gitem->setGraphicsEffect(blur);
367 } else if (e.attribute("type") == "shadow") {
368 QGraphicsDropShadowEffect *shadow = new QGraphicsDropShadowEffect();
369 shadow->setBlurRadius(e.attribute("blurradius").toInt());
370 shadow->setOffset(e.attribute("xoffset").toInt(), e.attribute("yoffset").toInt());
371 gitem->setGraphicsEffect(shadow);
377 if (items.item(i).nodeName() == "background") {
378 kDebug() << items.item(i).attributes().namedItem("color").nodeValue();
379 QColor color = QColor(stringToColor(items.item(i).attributes().namedItem("color").nodeValue()));
380 //color.setAlpha(items.item(i).attributes().namedItem("alpha").nodeValue().toInt());
381 QList<QGraphicsItem *> items = m_scene->items();
382 for (int i = 0; i < items.size(); i++) {
383 if (items.at(i)->zValue() == -1100) {
384 ((QGraphicsRectItem *)items.at(i))->setBrush(QBrush(color));
388 } else if (items.item(i).nodeName() == "startviewport" && startv) {
389 QString rect = items.item(i).attributes().namedItem("rect").nodeValue();
390 QRectF r = stringToRect(rect);
391 startv->setRect(0, 0, r.width(), r.height());
392 startv->setPos(r.topLeft());
393 } else if (items.item(i).nodeName() == "endviewport" && endv) {
394 QString rect = items.item(i).attributes().namedItem("rect").nodeValue();
395 QRectF r = stringToRect(rect);
396 endv->setRect(0, 0, r.width(), r.height());
397 endv->setPos(r.topLeft());
404 QString TitleDocument::colorToString(const QColor& c)
406 QString ret = "%1,%2,%3,%4";
407 ret = ret.arg(c.red()).arg(c.green()).arg(c.blue()).arg(c.alpha());
411 QString TitleDocument::rectFToString(const QRectF& c)
413 QString ret = "%1,%2,%3,%4";
414 ret = ret.arg(c.left()).arg(c.top()).arg(c.width()).arg(c.height());
418 QRectF TitleDocument::stringToRect(const QString & s)
421 QStringList l = s.split(',');
424 return QRectF(l.at(0).toDouble(), l.at(1).toDouble(), l.at(2).toDouble(), l.at(3).toDouble()).normalized();
427 QColor TitleDocument::stringToColor(const QString & s)
429 QStringList l = s.split(',');
432 return QColor(l.at(0).toInt(), l.at(1).toInt(), l.at(2).toInt(), l.at(3).toInt());;
434 QTransform TitleDocument::stringToTransform(const QString& s)
436 QStringList l = s.split(',');
440 l.at(0).toDouble(), l.at(1).toDouble(), l.at(2).toDouble(),
441 l.at(3).toDouble(), l.at(4).toDouble(), l.at(5).toDouble(),
442 l.at(6).toDouble(), l.at(7).toDouble(), l.at(8).toDouble()
446 int TitleDocument::frameWidth() const
451 int TitleDocument::frameHeight() const