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 tr.appendChild(doc.createTextNode(
135 QString("%1,%2,%3,%4,%5,%6,%7,%8,%9").arg(
136 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())
139 e.setAttribute("z-index", item->zValue());
142 #if QT_VERSION >= 0x040600
144 QGraphicsEffect *eff = item->graphicsEffect();
146 QGraphicsBlurEffect *blur = static_cast <QGraphicsBlurEffect *>(eff);
147 QDomElement effect = doc.createElement("effect");
149 effect.setAttribute("type", "blur");
150 effect.setAttribute("blurradius", blur->blurRadius());
152 QGraphicsDropShadowEffect *shadow = static_cast <QGraphicsDropShadowEffect *>(eff);
154 effect.setAttribute("type", "shadow");
155 effect.setAttribute("blurradius", shadow->blurRadius());
156 effect.setAttribute("xoffset", shadow->xOffset());
157 effect.setAttribute("yoffset", shadow->yOffset());
160 e.appendChild(effect);
165 e.appendChild(content);
166 if (item->zValue() > -1000) main.appendChild(e);
168 if (startv && endv) {
169 QDomElement endp = doc.createElement("endviewport");
170 QDomElement startp = doc.createElement("startviewport");
171 QRectF r(endv->pos().x(), endv->pos().y(), endv->rect().width(), endv->rect().height());
172 endp.setAttribute("rect", rectFToString(r));
173 QRectF r2(startv->pos().x(), startv->pos().y(), startv->rect().width(), startv->rect().height());
174 startp.setAttribute("rect", rectFToString(r2));
176 main.appendChild(startp);
177 main.appendChild(endp);
179 QDomElement backgr = doc.createElement("background");
180 QColor color = getBackgroundColor();
181 backgr.setAttribute("color", colorToString(color));
182 main.appendChild(backgr);
187 /** \brief Get the background color (incl. alpha) from the document, if possibly
188 * \returns The background color of the document, inclusive alpha. If none found, returns (0,0,0,0) */
189 QColor TitleDocument::getBackgroundColor()
191 QColor color(0, 0, 0, 0);
193 QList<QGraphicsItem *> items = m_scene->items();
194 for (int i = 0; i < items.size(); i++) {
195 if (items.at(i)->zValue() == -1100) {
196 color = ((QGraphicsRectItem *)items.at(i))->brush().color();
205 bool TitleDocument::saveDocument(const KUrl& url, QGraphicsRectItem* startv, QGraphicsRectItem* endv, int out)
210 QDomDocument doc = xml(startv, endv);
211 doc.documentElement().setAttribute("out", out);
212 KTemporaryFile tmpfile;
213 if (!tmpfile.open()) {
214 kWarning() << "///// CANNOT CREATE TMP FILE in: " << tmpfile.fileName();
217 QFile xmlf(tmpfile.fileName());
218 xmlf.open(QIODevice::WriteOnly);
219 xmlf.write(doc.toString().toUtf8());
220 if (xmlf.error() != QFile::NoError) {
225 return KIO::NetAccess::upload(tmpfile.fileName(), url, 0);
228 int TitleDocument::loadFromXml(QDomDocument doc, QGraphicsRectItem* startv, QGraphicsRectItem* endv, int *out)
230 QDomNodeList titles = doc.elementsByTagName("kdenlivetitle");
231 //TODO: Check if the opened title size is equal to project size, otherwise warn user and rescale
232 if (doc.documentElement().hasAttribute("width") && doc.documentElement().hasAttribute("height")) {
233 int doc_width = doc.documentElement().attribute("width").toInt();
234 int doc_height = doc.documentElement().attribute("height").toInt();
235 if (doc_width != m_width || doc_height != m_height) {
236 KMessageBox::information(kapp->activeWindow(), i18n("This title clip was created with a different frame size."), i18n("Title Profile"));
237 //TODO: convert using QTransform
239 m_height = doc_height;
242 //TODO: get default title duration instead of hardcoded one
243 if (doc.documentElement().hasAttribute("out"))
244 *out = doc.documentElement().attribute("out").toInt();
251 QDomNodeList items = titles.item(0).childNodes();
252 for (int i = 0; i < items.count(); i++) {
253 QGraphicsItem *gitem = NULL;
254 kDebug() << items.item(i).attributes().namedItem("type").nodeValue();
255 int zValue = items.item(i).attributes().namedItem("z-index").nodeValue().toInt();
256 if (zValue > -1000) {
257 if (items.item(i).attributes().namedItem("type").nodeValue() == "QGraphicsTextItem") {
258 QDomNamedNodeMap txtProperties = items.item(i).namedItem("content").attributes();
259 QFont font(txtProperties.namedItem("font").nodeValue());
261 QDomNode node = txtProperties.namedItem("font-bold");
262 if (!node.isNull()) {
263 // Old: Bold/Not bold.
264 font.setBold(node.nodeValue().toInt());
266 // New: Font weight (QFont::)
267 font.setWeight(txtProperties.namedItem("font-weight").nodeValue().toInt());
269 //font.setBold(txtProperties.namedItem("font-bold").nodeValue().toInt());
270 font.setItalic(txtProperties.namedItem("font-italic").nodeValue().toInt());
271 font.setUnderline(txtProperties.namedItem("font-underline").nodeValue().toInt());
272 // Older Kdenlive version did not store pixel size but point size
273 if (txtProperties.namedItem("font-pixel-size").isNull()) {
274 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"));
276 f2.setPointSize(txtProperties.namedItem("font-size").nodeValue().toInt());
277 font.setPixelSize(QFontInfo(f2).pixelSize());
279 font.setPixelSize(txtProperties.namedItem("font-pixel-size").nodeValue().toInt());
280 QColor col(stringToColor(txtProperties.namedItem("font-color").nodeValue()));
281 QGraphicsTextItem *txt = m_scene->addText(items.item(i).namedItem("content").firstChild().nodeValue(), font);
282 QTextCursor cursor(txt->document());
283 cursor.select(QTextCursor::Document);
284 QTextCharFormat format;
285 if (txtProperties.namedItem("font-outline").nodeValue().toDouble() > 0.0) {
286 txt->setData(101, txtProperties.namedItem("font-outline").nodeValue().toDouble());
287 txt->setData(102, stringToColor(txtProperties.namedItem("font-outline-color").nodeValue()));
288 format.setTextOutline(
289 QPen(QColor(stringToColor(txtProperties.namedItem("font-outline-color").nodeValue())),
290 txtProperties.namedItem("font-outline").nodeValue().toDouble(),
291 Qt::SolidLine,Qt::RoundCap,Qt::RoundJoin)
295 format.setForeground(QBrush(col));
296 cursor.mergeCharFormat(format);
297 txt->setTextInteractionFlags(Qt::NoTextInteraction);
298 if (txtProperties.namedItem("alignment").isNull() == false) {
299 txt->setTextWidth(txt->boundingRect().width());
300 QTextCursor cur = txt->textCursor();
301 QTextBlockFormat format = cur.blockFormat();
302 format.setAlignment((Qt::Alignment) txtProperties.namedItem("alignment").nodeValue().toInt());
303 cur.select(QTextCursor::Document);
304 cur.setBlockFormat(format);
305 txt->setTextCursor(cur);
306 cur.clearSelection();
307 txt->setTextCursor(cur);
310 if (!txtProperties.namedItem("kdenlive-axis-x-inverted").isNull()) {
311 txt->setData(OriginXLeft, txtProperties.namedItem("kdenlive-axis-x-inverted").nodeValue().toInt());
313 if (!txtProperties.namedItem("kdenlive-axis-y-inverted").isNull()) {
314 txt->setData(OriginYTop, txtProperties.namedItem("kdenlive-axis-y-inverted").nodeValue().toInt());
318 if (!txtProperties.namedItem("typewriter").isNull()) {
319 QStringList effData = QStringList() << "typewriter" << txtProperties.namedItem("typewriter").nodeValue();
320 txt->setData(100, effData);
324 } else if (items.item(i).attributes().namedItem("type").nodeValue() == "QGraphicsRectItem") {
325 QString rect = items.item(i).namedItem("content").attributes().namedItem("rect").nodeValue();
326 QString br_str = items.item(i).namedItem("content").attributes().namedItem("brushcolor").nodeValue();
327 QString pen_str = items.item(i).namedItem("content").attributes().namedItem("pencolor").nodeValue();
328 double penwidth = items.item(i).namedItem("content").attributes().namedItem("penwidth").nodeValue().toDouble();
329 QGraphicsRectItem *rec = m_scene->addRect(stringToRect(rect), QPen(QBrush(stringToColor(pen_str)), penwidth), QBrush(stringToColor(br_str)));
331 } else if (items.item(i).attributes().namedItem("type").nodeValue() == "QGraphicsPixmapItem") {
332 QString url = items.item(i).namedItem("content").attributes().namedItem("url").nodeValue();
334 QGraphicsPixmapItem *rec = m_scene->addPixmap(pix);
335 rec->setData(Qt::UserRole, url);
337 } else if (items.item(i).attributes().namedItem("type").nodeValue() == "QGraphicsSvgItem") {
338 QString url = items.item(i).namedItem("content").attributes().namedItem("url").nodeValue();
339 QGraphicsSvgItem *rec = new QGraphicsSvgItem(url);
340 m_scene->addItem(rec);
341 rec->setData(Qt::UserRole, url);
347 QPointF p(items.item(i).namedItem("position").attributes().namedItem("x").nodeValue().toDouble(),
348 items.item(i).namedItem("position").attributes().namedItem("y").nodeValue().toDouble());
350 gitem->setTransform(stringToTransform(items.item(i).namedItem("position").firstChild().firstChild().nodeValue()));
351 int zValue = items.item(i).attributes().namedItem("z-index").nodeValue().toInt();
352 if (zValue > maxZValue) maxZValue = zValue;
353 gitem->setZValue(zValue);
354 gitem->setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable);
356 #if QT_VERSION >= 0x040600
358 QDomNode eff = items.item(i).namedItem("effect");
360 QDomElement e = eff.toElement();
361 if (e.attribute("type") == "blur") {
362 QGraphicsBlurEffect *blur = new QGraphicsBlurEffect();
363 blur->setBlurRadius(e.attribute("blurradius").toInt());
364 gitem->setGraphicsEffect(blur);
365 } else if (e.attribute("type") == "shadow") {
366 QGraphicsDropShadowEffect *shadow = new QGraphicsDropShadowEffect();
367 shadow->setBlurRadius(e.attribute("blurradius").toInt());
368 shadow->setOffset(e.attribute("xoffset").toInt(), e.attribute("yoffset").toInt());
369 gitem->setGraphicsEffect(shadow);
375 if (items.item(i).nodeName() == "background") {
376 kDebug() << items.item(i).attributes().namedItem("color").nodeValue();
377 QColor color = QColor(stringToColor(items.item(i).attributes().namedItem("color").nodeValue()));
378 //color.setAlpha(items.item(i).attributes().namedItem("alpha").nodeValue().toInt());
379 QList<QGraphicsItem *> items = m_scene->items();
380 for (int i = 0; i < items.size(); i++) {
381 if (items.at(i)->zValue() == -1100) {
382 ((QGraphicsRectItem *)items.at(i))->setBrush(QBrush(color));
386 } else if (items.item(i).nodeName() == "startviewport" && startv) {
387 QString rect = items.item(i).attributes().namedItem("rect").nodeValue();
388 QRectF r = stringToRect(rect);
389 startv->setRect(0, 0, r.width(), r.height());
390 startv->setPos(r.topLeft());
391 } else if (items.item(i).nodeName() == "endviewport" && endv) {
392 QString rect = items.item(i).attributes().namedItem("rect").nodeValue();
393 QRectF r = stringToRect(rect);
394 endv->setRect(0, 0, r.width(), r.height());
395 endv->setPos(r.topLeft());
402 QString TitleDocument::colorToString(const QColor& c)
404 QString ret = "%1,%2,%3,%4";
405 ret = ret.arg(c.red()).arg(c.green()).arg(c.blue()).arg(c.alpha());
409 QString TitleDocument::rectFToString(const QRectF& c)
411 QString ret = "%1,%2,%3,%4";
412 ret = ret.arg(c.left()).arg(c.top()).arg(c.width()).arg(c.height());
416 QRectF TitleDocument::stringToRect(const QString & s)
419 QStringList l = s.split(',');
422 return QRectF(l.at(0).toDouble(), l.at(1).toDouble(), l.at(2).toDouble(), l.at(3).toDouble()).normalized();
425 QColor TitleDocument::stringToColor(const QString & s)
427 QStringList l = s.split(',');
430 return QColor(l.at(0).toInt(), l.at(1).toInt(), l.at(2).toInt(), l.at(3).toInt());;
432 QTransform TitleDocument::stringToTransform(const QString& s)
434 QStringList l = s.split(',');
438 l.at(0).toDouble(), l.at(1).toDouble(), l.at(2).toDouble(),
439 l.at(3).toDouble(), l.at(4).toDouble(), l.at(5).toDouble(),
440 l.at(6).toDouble(), l.at(7).toDouble(), l.at(8).toDouble()
444 int TitleDocument::frameWidth() const
449 int TitleDocument::frameHeight() const