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())
294 format.setForeground(QBrush(col));
295 cursor.mergeCharFormat(format);
296 txt->setTextInteractionFlags(Qt::NoTextInteraction);
297 if (txtProperties.namedItem("alignment").isNull() == false) {
298 txt->setTextWidth(txt->boundingRect().width());
299 QTextCursor cur = txt->textCursor();
300 QTextBlockFormat format = cur.blockFormat();
301 format.setAlignment((Qt::Alignment) txtProperties.namedItem("alignment").nodeValue().toInt());
302 cur.select(QTextCursor::Document);
303 cur.setBlockFormat(format);
304 txt->setTextCursor(cur);
305 cur.clearSelection();
306 txt->setTextCursor(cur);
309 if (!txtProperties.namedItem("kdenlive-axis-x-inverted").isNull()) {
310 txt->setData(OriginXLeft, txtProperties.namedItem("kdenlive-axis-x-inverted").nodeValue().toInt());
312 if (!txtProperties.namedItem("kdenlive-axis-y-inverted").isNull()) {
313 txt->setData(OriginYTop, txtProperties.namedItem("kdenlive-axis-y-inverted").nodeValue().toInt());
317 if (!txtProperties.namedItem("typewriter").isNull()) {
318 QStringList effData = QStringList() << "typewriter" << txtProperties.namedItem("typewriter").nodeValue();
319 txt->setData(100, effData);
323 } else if (items.item(i).attributes().namedItem("type").nodeValue() == "QGraphicsRectItem") {
324 QString rect = items.item(i).namedItem("content").attributes().namedItem("rect").nodeValue();
325 QString br_str = items.item(i).namedItem("content").attributes().namedItem("brushcolor").nodeValue();
326 QString pen_str = items.item(i).namedItem("content").attributes().namedItem("pencolor").nodeValue();
327 double penwidth = items.item(i).namedItem("content").attributes().namedItem("penwidth").nodeValue().toDouble();
328 QGraphicsRectItem *rec = m_scene->addRect(stringToRect(rect), QPen(QBrush(stringToColor(pen_str)), penwidth), QBrush(stringToColor(br_str)));
330 } else if (items.item(i).attributes().namedItem("type").nodeValue() == "QGraphicsPixmapItem") {
331 QString url = items.item(i).namedItem("content").attributes().namedItem("url").nodeValue();
333 QGraphicsPixmapItem *rec = m_scene->addPixmap(pix);
334 rec->setData(Qt::UserRole, url);
336 } else if (items.item(i).attributes().namedItem("type").nodeValue() == "QGraphicsSvgItem") {
337 QString url = items.item(i).namedItem("content").attributes().namedItem("url").nodeValue();
338 QGraphicsSvgItem *rec = new QGraphicsSvgItem(url);
339 m_scene->addItem(rec);
340 rec->setData(Qt::UserRole, url);
346 QPointF p(items.item(i).namedItem("position").attributes().namedItem("x").nodeValue().toDouble(),
347 items.item(i).namedItem("position").attributes().namedItem("y").nodeValue().toDouble());
349 gitem->setTransform(stringToTransform(items.item(i).namedItem("position").firstChild().firstChild().nodeValue()));
350 int zValue = items.item(i).attributes().namedItem("z-index").nodeValue().toInt();
351 if (zValue > maxZValue) maxZValue = zValue;
352 gitem->setZValue(zValue);
353 gitem->setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable);
355 #if QT_VERSION >= 0x040600
357 QDomNode eff = items.item(i).namedItem("effect");
359 QDomElement e = eff.toElement();
360 if (e.attribute("type") == "blur") {
361 QGraphicsBlurEffect *blur = new QGraphicsBlurEffect();
362 blur->setBlurRadius(e.attribute("blurradius").toInt());
363 gitem->setGraphicsEffect(blur);
364 } else if (e.attribute("type") == "shadow") {
365 QGraphicsDropShadowEffect *shadow = new QGraphicsDropShadowEffect();
366 shadow->setBlurRadius(e.attribute("blurradius").toInt());
367 shadow->setOffset(e.attribute("xoffset").toInt(), e.attribute("yoffset").toInt());
368 gitem->setGraphicsEffect(shadow);
374 if (items.item(i).nodeName() == "background") {
375 kDebug() << items.item(i).attributes().namedItem("color").nodeValue();
376 QColor color = QColor(stringToColor(items.item(i).attributes().namedItem("color").nodeValue()));
377 //color.setAlpha(items.item(i).attributes().namedItem("alpha").nodeValue().toInt());
378 QList<QGraphicsItem *> items = m_scene->items();
379 for (int i = 0; i < items.size(); i++) {
380 if (items.at(i)->zValue() == -1100) {
381 ((QGraphicsRectItem *)items.at(i))->setBrush(QBrush(color));
385 } else if (items.item(i).nodeName() == "startviewport" && startv) {
386 QString rect = items.item(i).attributes().namedItem("rect").nodeValue();
387 QRectF r = stringToRect(rect);
388 startv->setRect(0, 0, r.width(), r.height());
389 startv->setPos(r.topLeft());
390 } else if (items.item(i).nodeName() == "endviewport" && endv) {
391 QString rect = items.item(i).attributes().namedItem("rect").nodeValue();
392 QRectF r = stringToRect(rect);
393 endv->setRect(0, 0, r.width(), r.height());
394 endv->setPos(r.topLeft());
401 QString TitleDocument::colorToString(const QColor& c)
403 QString ret = "%1,%2,%3,%4";
404 ret = ret.arg(c.red()).arg(c.green()).arg(c.blue()).arg(c.alpha());
408 QString TitleDocument::rectFToString(const QRectF& c)
410 QString ret = "%1,%2,%3,%4";
411 ret = ret.arg(c.left()).arg(c.top()).arg(c.width()).arg(c.height());
415 QRectF TitleDocument::stringToRect(const QString & s)
418 QStringList l = s.split(',');
421 return QRectF(l.at(0).toDouble(), l.at(1).toDouble(), l.at(2).toDouble(), l.at(3).toDouble()).normalized();
424 QColor TitleDocument::stringToColor(const QString & s)
426 QStringList l = s.split(',');
429 return QColor(l.at(0).toInt(), l.at(1).toInt(), l.at(2).toInt(), l.at(3).toInt());;
431 QTransform TitleDocument::stringToTransform(const QString& s)
433 QStringList l = s.split(',');
437 l.at(0).toDouble(), l.at(1).toDouble(), l.at(2).toDouble(),
438 l.at(3).toDouble(), l.at(4).toDouble(), l.at(5).toDouble(),
439 l.at(6).toDouble(), l.at(7).toDouble(), l.at(8).toDouble()
443 int TitleDocument::frameWidth() const
448 int TitleDocument::frameHeight() const