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());
99 content.setAttribute("font-color", colorToString(t->defaultTextColor()));
101 if (!t->data(100).isNull()) {
102 QStringList effectParams = t->data(100).toStringList();
103 content.setAttribute(effectParams.takeFirst(), effectParams.join(";"));
106 // Only save when necessary.
107 if (t->data(OriginXLeft).toInt() == AxisInverted) {
108 content.setAttribute("kdenlive-axis-x-inverted", t->data(OriginXLeft).toInt());
110 if (t->data(OriginYTop).toInt() == AxisInverted) {
111 content.setAttribute("kdenlive-axis-y-inverted", t->data(OriginYTop).toInt());
113 if (t->textWidth() != -1) {
114 content.setAttribute("alignment", t->textCursor().blockFormat().alignment());
122 QDomElement pos = doc.createElement("position");
123 pos.setAttribute("x", item->pos().x());
124 pos.setAttribute("y", item->pos().y());
125 QTransform transform = item->transform();
126 QDomElement tr = doc.createElement("transform");
127 tr.appendChild(doc.createTextNode(
128 QString("%1,%2,%3,%4,%5,%6,%7,%8,%9").arg(
129 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())
132 e.setAttribute("z-index", item->zValue());
135 #if QT_VERSION >= 0x040600
137 QGraphicsEffect *eff = item->graphicsEffect();
139 QGraphicsBlurEffect *blur = static_cast <QGraphicsBlurEffect *>(eff);
140 QDomElement effect = doc.createElement("effect");
142 effect.setAttribute("type", "blur");
143 effect.setAttribute("blurradius", blur->blurRadius());
145 QGraphicsDropShadowEffect *shadow = static_cast <QGraphicsDropShadowEffect *>(eff);
147 effect.setAttribute("type", "shadow");
148 effect.setAttribute("blurradius", shadow->blurRadius());
149 effect.setAttribute("xoffset", shadow->xOffset());
150 effect.setAttribute("yoffset", shadow->yOffset());
153 e.appendChild(effect);
158 e.appendChild(content);
159 if (item->zValue() > -1000) main.appendChild(e);
161 if (startv && endv) {
162 QDomElement endp = doc.createElement("endviewport");
163 QDomElement startp = doc.createElement("startviewport");
164 QRectF r(endv->pos().x(), endv->pos().y(), endv->rect().width(), endv->rect().height());
165 endp.setAttribute("rect", rectFToString(r));
166 QRectF r2(startv->pos().x(), startv->pos().y(), startv->rect().width(), startv->rect().height());
167 startp.setAttribute("rect", rectFToString(r2));
169 main.appendChild(startp);
170 main.appendChild(endp);
172 QDomElement backgr = doc.createElement("background");
173 QColor color = getBackgroundColor();
174 backgr.setAttribute("color", colorToString(color));
175 main.appendChild(backgr);
180 /** \brief Get the background color (incl. alpha) from the document, if possibly
181 * \returns The background color of the document, inclusive alpha. If none found, returns (0,0,0,0) */
182 QColor TitleDocument::getBackgroundColor()
184 QColor color(0, 0, 0, 0);
186 QList<QGraphicsItem *> items = m_scene->items();
187 for (int i = 0; i < items.size(); i++) {
188 if (items.at(i)->zValue() == -1100) {
189 color = ((QGraphicsRectItem *)items.at(i))->brush().color();
198 bool TitleDocument::saveDocument(const KUrl& url, QGraphicsRectItem* startv, QGraphicsRectItem* endv, int out)
203 QDomDocument doc = xml(startv, endv);
204 doc.documentElement().setAttribute("out", out);
205 KTemporaryFile tmpfile;
206 if (!tmpfile.open()) {
207 kWarning() << "///// CANNOT CREATE TMP FILE in: " << tmpfile.fileName();
210 QFile xmlf(tmpfile.fileName());
211 xmlf.open(QIODevice::WriteOnly);
212 xmlf.write(doc.toString().toUtf8());
213 if (xmlf.error() != QFile::NoError) {
218 return KIO::NetAccess::upload(tmpfile.fileName(), url, 0);
221 int TitleDocument::loadFromXml(QDomDocument doc, QGraphicsRectItem* startv, QGraphicsRectItem* endv, int *out)
223 QDomNodeList titles = doc.elementsByTagName("kdenlivetitle");
224 //TODO: Check if the opened title size is equal to project size, otherwise warn user and rescale
225 if (doc.documentElement().hasAttribute("width") && doc.documentElement().hasAttribute("height")) {
226 int doc_width = doc.documentElement().attribute("width").toInt();
227 int doc_height = doc.documentElement().attribute("height").toInt();
228 if (doc_width != m_width || doc_height != m_height) {
229 KMessageBox::information(kapp->activeWindow(), i18n("This title clip was created with a different frame size."), i18n("Title Profile"));
230 //TODO: convert using QTransform
232 m_height = doc_height;
235 //TODO: get default title duration instead of hardcoded one
236 if (doc.documentElement().hasAttribute("out"))
237 *out = doc.documentElement().attribute("out").toInt();
244 QDomNodeList items = titles.item(0).childNodes();
245 for (int i = 0; i < items.count(); i++) {
246 QGraphicsItem *gitem = NULL;
247 kDebug() << items.item(i).attributes().namedItem("type").nodeValue();
248 int zValue = items.item(i).attributes().namedItem("z-index").nodeValue().toInt();
249 if (zValue > -1000) {
250 if (items.item(i).attributes().namedItem("type").nodeValue() == "QGraphicsTextItem") {
251 QDomNamedNodeMap txtProperties = items.item(i).namedItem("content").attributes();
252 QFont font(txtProperties.namedItem("font").nodeValue());
254 QDomNode node = txtProperties.namedItem("font-bold");
255 if (!node.isNull()) {
256 // Old: Bold/Not bold.
257 font.setBold(node.nodeValue().toInt());
259 // New: Font weight (QFont::)
260 font.setWeight(txtProperties.namedItem("font-weight").nodeValue().toInt());
262 //font.setBold(txtProperties.namedItem("font-bold").nodeValue().toInt());
263 font.setItalic(txtProperties.namedItem("font-italic").nodeValue().toInt());
264 font.setUnderline(txtProperties.namedItem("font-underline").nodeValue().toInt());
265 // Older Kdenlive version did not store pixel size but point size
266 if (txtProperties.namedItem("font-pixel-size").isNull()) {
267 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 f2.setPointSize(txtProperties.namedItem("font-size").nodeValue().toInt());
270 font.setPixelSize(QFontInfo(f2).pixelSize());
272 font.setPixelSize(txtProperties.namedItem("font-pixel-size").nodeValue().toInt());
273 QColor col(stringToColor(txtProperties.namedItem("font-color").nodeValue()));
274 QGraphicsTextItem *txt = m_scene->addText(items.item(i).namedItem("content").firstChild().nodeValue(), font);
275 txt->setDefaultTextColor(col);
276 txt->setTextInteractionFlags(Qt::NoTextInteraction);
277 if (txtProperties.namedItem("alignment").isNull() == false) {
278 txt->setTextWidth(txt->boundingRect().width());
279 QTextCursor cur = txt->textCursor();
280 QTextBlockFormat format = cur.blockFormat();
281 format.setAlignment((Qt::Alignment) txtProperties.namedItem("alignment").nodeValue().toInt());
282 cur.select(QTextCursor::Document);
283 cur.setBlockFormat(format);
284 txt->setTextCursor(cur);
285 cur.clearSelection();
286 txt->setTextCursor(cur);
289 if (!txtProperties.namedItem("kdenlive-axis-x-inverted").isNull()) {
290 txt->setData(OriginXLeft, txtProperties.namedItem("kdenlive-axis-x-inverted").nodeValue().toInt());
292 if (!txtProperties.namedItem("kdenlive-axis-y-inverted").isNull()) {
293 txt->setData(OriginYTop, txtProperties.namedItem("kdenlive-axis-y-inverted").nodeValue().toInt());
297 if (!txtProperties.namedItem("typewriter").isNull()) {
298 QStringList effData = QStringList() << "typewriter" << txtProperties.namedItem("typewriter").nodeValue();
299 txt->setData(100, effData);
303 } else if (items.item(i).attributes().namedItem("type").nodeValue() == "QGraphicsRectItem") {
304 QString rect = items.item(i).namedItem("content").attributes().namedItem("rect").nodeValue();
305 QString br_str = items.item(i).namedItem("content").attributes().namedItem("brushcolor").nodeValue();
306 QString pen_str = items.item(i).namedItem("content").attributes().namedItem("pencolor").nodeValue();
307 double penwidth = items.item(i).namedItem("content").attributes().namedItem("penwidth").nodeValue().toDouble();
308 QGraphicsRectItem *rec = m_scene->addRect(stringToRect(rect), QPen(QBrush(stringToColor(pen_str)), penwidth), QBrush(stringToColor(br_str)));
310 } else if (items.item(i).attributes().namedItem("type").nodeValue() == "QGraphicsPixmapItem") {
311 QString url = items.item(i).namedItem("content").attributes().namedItem("url").nodeValue();
313 QGraphicsPixmapItem *rec = m_scene->addPixmap(pix);
314 rec->setData(Qt::UserRole, url);
316 } else if (items.item(i).attributes().namedItem("type").nodeValue() == "QGraphicsSvgItem") {
317 QString url = items.item(i).namedItem("content").attributes().namedItem("url").nodeValue();
318 QGraphicsSvgItem *rec = new QGraphicsSvgItem(url);
319 m_scene->addItem(rec);
320 rec->setData(Qt::UserRole, url);
326 QPointF p(items.item(i).namedItem("position").attributes().namedItem("x").nodeValue().toDouble(),
327 items.item(i).namedItem("position").attributes().namedItem("y").nodeValue().toDouble());
329 gitem->setTransform(stringToTransform(items.item(i).namedItem("position").firstChild().firstChild().nodeValue()));
330 int zValue = items.item(i).attributes().namedItem("z-index").nodeValue().toInt();
331 if (zValue > maxZValue) maxZValue = zValue;
332 gitem->setZValue(zValue);
333 gitem->setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable);
335 #if QT_VERSION >= 0x040600
337 QDomNode eff = items.item(i).namedItem("effect");
339 QDomElement e = eff.toElement();
340 if (e.attribute("type") == "blur") {
341 QGraphicsBlurEffect *blur = new QGraphicsBlurEffect();
342 blur->setBlurRadius(e.attribute("blurradius").toInt());
343 gitem->setGraphicsEffect(blur);
344 } else if (e.attribute("type") == "shadow") {
345 QGraphicsDropShadowEffect *shadow = new QGraphicsDropShadowEffect();
346 shadow->setBlurRadius(e.attribute("blurradius").toInt());
347 shadow->setOffset(e.attribute("xoffset").toInt(), e.attribute("yoffset").toInt());
348 gitem->setGraphicsEffect(shadow);
354 if (items.item(i).nodeName() == "background") {
355 kDebug() << items.item(i).attributes().namedItem("color").nodeValue();
356 QColor color = QColor(stringToColor(items.item(i).attributes().namedItem("color").nodeValue()));
357 //color.setAlpha(items.item(i).attributes().namedItem("alpha").nodeValue().toInt());
358 QList<QGraphicsItem *> items = m_scene->items();
359 for (int i = 0; i < items.size(); i++) {
360 if (items.at(i)->zValue() == -1100) {
361 ((QGraphicsRectItem *)items.at(i))->setBrush(QBrush(color));
365 } else if (items.item(i).nodeName() == "startviewport" && startv) {
366 QString rect = items.item(i).attributes().namedItem("rect").nodeValue();
367 QRectF r = stringToRect(rect);
368 startv->setRect(0, 0, r.width(), r.height());
369 startv->setPos(r.topLeft());
370 } else if (items.item(i).nodeName() == "endviewport" && endv) {
371 QString rect = items.item(i).attributes().namedItem("rect").nodeValue();
372 QRectF r = stringToRect(rect);
373 endv->setRect(0, 0, r.width(), r.height());
374 endv->setPos(r.topLeft());
381 QString TitleDocument::colorToString(const QColor& c)
383 QString ret = "%1,%2,%3,%4";
384 ret = ret.arg(c.red()).arg(c.green()).arg(c.blue()).arg(c.alpha());
388 QString TitleDocument::rectFToString(const QRectF& c)
390 QString ret = "%1,%2,%3,%4";
391 ret = ret.arg(c.left()).arg(c.top()).arg(c.width()).arg(c.height());
395 QRectF TitleDocument::stringToRect(const QString & s)
398 QStringList l = s.split(',');
401 return QRectF(l.at(0).toDouble(), l.at(1).toDouble(), l.at(2).toDouble(), l.at(3).toDouble()).normalized();
404 QColor TitleDocument::stringToColor(const QString & s)
406 QStringList l = s.split(',');
409 return QColor(l.at(0).toInt(), l.at(1).toInt(), l.at(2).toInt(), l.at(3).toInt());;
411 QTransform TitleDocument::stringToTransform(const QString& s)
413 QStringList l = s.split(',');
417 l.at(0).toDouble(), l.at(1).toDouble(), l.at(2).toDouble(),
418 l.at(3).toDouble(), l.at(4).toDouble(), l.at(5).toDouble(),
419 l.at(6).toDouble(), l.at(7).toDouble(), l.at(8).toDouble()
423 int TitleDocument::frameWidth() const
428 int TitleDocument::frameHeight() const