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 QString effectName = effectParams.takeFirst();
104 content.setAttribute(effectName, effectParams.join(";"));
107 // Only save when necessary.
108 if (t->data(OriginXLeft).toInt() == AxisInverted) {
109 content.setAttribute("kdenlive-axis-x-inverted", t->data(OriginXLeft).toInt());
111 if (t->data(OriginYTop).toInt() == AxisInverted) {
112 content.setAttribute("kdenlive-axis-y-inverted", t->data(OriginYTop).toInt());
114 if (t->textWidth() != -1) {
115 content.setAttribute("alignment", t->textCursor().blockFormat().alignment());
123 QDomElement pos = doc.createElement("position");
124 pos.setAttribute("x", item->pos().x());
125 pos.setAttribute("y", item->pos().y());
126 QTransform transform = item->transform();
127 QDomElement tr = doc.createElement("transform");
128 tr.appendChild(doc.createTextNode(
129 QString("%1,%2,%3,%4,%5,%6,%7,%8,%9").arg(
130 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())
133 e.setAttribute("z-index", item->zValue());
136 #if QT_VERSION >= 0x040600
138 QGraphicsEffect *eff = item->graphicsEffect();
140 QGraphicsBlurEffect *blur = static_cast <QGraphicsBlurEffect *>(eff);
141 QDomElement effect = doc.createElement("effect");
143 effect.setAttribute("type", "blur");
144 effect.setAttribute("blurradius", blur->blurRadius());
146 QGraphicsDropShadowEffect *shadow = static_cast <QGraphicsDropShadowEffect *>(eff);
148 effect.setAttribute("type", "shadow");
149 effect.setAttribute("blurradius", shadow->blurRadius());
150 effect.setAttribute("xoffset", shadow->xOffset());
151 effect.setAttribute("yoffset", shadow->yOffset());
154 e.appendChild(effect);
159 e.appendChild(content);
160 if (item->zValue() > -1000) main.appendChild(e);
162 if (startv && endv) {
163 QDomElement endp = doc.createElement("endviewport");
164 QDomElement startp = doc.createElement("startviewport");
165 QRectF r(endv->pos().x(), endv->pos().y(), endv->rect().width(), endv->rect().height());
166 endp.setAttribute("rect", rectFToString(r));
167 QRectF r2(startv->pos().x(), startv->pos().y(), startv->rect().width(), startv->rect().height());
168 startp.setAttribute("rect", rectFToString(r2));
170 main.appendChild(startp);
171 main.appendChild(endp);
173 QDomElement backgr = doc.createElement("background");
174 QColor color = getBackgroundColor();
175 backgr.setAttribute("color", colorToString(color));
176 main.appendChild(backgr);
181 /** \brief Get the background color (incl. alpha) from the document, if possibly
182 * \returns The background color of the document, inclusive alpha. If none found, returns (0,0,0,0) */
183 QColor TitleDocument::getBackgroundColor()
185 QColor color(0, 0, 0, 0);
187 QList<QGraphicsItem *> items = m_scene->items();
188 for (int i = 0; i < items.size(); i++) {
189 if (items.at(i)->zValue() == -1100) {
190 color = ((QGraphicsRectItem *)items.at(i))->brush().color();
199 bool TitleDocument::saveDocument(const KUrl& url, QGraphicsRectItem* startv, QGraphicsRectItem* endv, int out)
204 QDomDocument doc = xml(startv, endv);
205 doc.documentElement().setAttribute("out", out);
206 KTemporaryFile tmpfile;
207 if (!tmpfile.open()) {
208 kWarning() << "///// CANNOT CREATE TMP FILE in: " << tmpfile.fileName();
211 QFile xmlf(tmpfile.fileName());
212 xmlf.open(QIODevice::WriteOnly);
213 xmlf.write(doc.toString().toUtf8());
214 if (xmlf.error() != QFile::NoError) {
219 return KIO::NetAccess::upload(tmpfile.fileName(), url, 0);
222 int TitleDocument::loadFromXml(QDomDocument doc, QGraphicsRectItem* startv, QGraphicsRectItem* endv, int *out)
224 QDomNodeList titles = doc.elementsByTagName("kdenlivetitle");
225 //TODO: Check if the opened title size is equal to project size, otherwise warn user and rescale
226 if (doc.documentElement().hasAttribute("width") && doc.documentElement().hasAttribute("height")) {
227 int doc_width = doc.documentElement().attribute("width").toInt();
228 int doc_height = doc.documentElement().attribute("height").toInt();
229 if (doc_width != m_width || doc_height != m_height) {
230 KMessageBox::information(kapp->activeWindow(), i18n("This title clip was created with a different frame size."), i18n("Title Profile"));
231 //TODO: convert using QTransform
233 m_height = doc_height;
236 //TODO: get default title duration instead of hardcoded one
237 if (doc.documentElement().hasAttribute("out"))
238 *out = doc.documentElement().attribute("out").toInt();
245 QDomNodeList items = titles.item(0).childNodes();
246 for (int i = 0; i < items.count(); i++) {
247 QGraphicsItem *gitem = NULL;
248 kDebug() << items.item(i).attributes().namedItem("type").nodeValue();
249 int zValue = items.item(i).attributes().namedItem("z-index").nodeValue().toInt();
250 if (zValue > -1000) {
251 if (items.item(i).attributes().namedItem("type").nodeValue() == "QGraphicsTextItem") {
252 QDomNamedNodeMap txtProperties = items.item(i).namedItem("content").attributes();
253 QFont font(txtProperties.namedItem("font").nodeValue());
255 QDomNode node = txtProperties.namedItem("font-bold");
256 if (!node.isNull()) {
257 // Old: Bold/Not bold.
258 font.setBold(node.nodeValue().toInt());
260 // New: Font weight (QFont::)
261 font.setWeight(txtProperties.namedItem("font-weight").nodeValue().toInt());
263 //font.setBold(txtProperties.namedItem("font-bold").nodeValue().toInt());
264 font.setItalic(txtProperties.namedItem("font-italic").nodeValue().toInt());
265 font.setUnderline(txtProperties.namedItem("font-underline").nodeValue().toInt());
266 // Older Kdenlive version did not store pixel size but point size
267 if (txtProperties.namedItem("font-pixel-size").isNull()) {
268 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"));
270 f2.setPointSize(txtProperties.namedItem("font-size").nodeValue().toInt());
271 font.setPixelSize(QFontInfo(f2).pixelSize());
273 font.setPixelSize(txtProperties.namedItem("font-pixel-size").nodeValue().toInt());
274 QColor col(stringToColor(txtProperties.namedItem("font-color").nodeValue()));
275 QGraphicsTextItem *txt = m_scene->addText(items.item(i).namedItem("content").firstChild().nodeValue(), font);
276 txt->setDefaultTextColor(col);
277 txt->setTextInteractionFlags(Qt::NoTextInteraction);
278 if (txtProperties.namedItem("alignment").isNull() == false) {
279 txt->setTextWidth(txt->boundingRect().width());
280 QTextCursor cur = txt->textCursor();
281 QTextBlockFormat format = cur.blockFormat();
282 format.setAlignment((Qt::Alignment) txtProperties.namedItem("alignment").nodeValue().toInt());
283 cur.select(QTextCursor::Document);
284 cur.setBlockFormat(format);
285 txt->setTextCursor(cur);
286 cur.clearSelection();
287 txt->setTextCursor(cur);
290 if (!txtProperties.namedItem("kdenlive-axis-x-inverted").isNull()) {
291 txt->setData(OriginXLeft, txtProperties.namedItem("kdenlive-axis-x-inverted").nodeValue().toInt());
293 if (!txtProperties.namedItem("kdenlive-axis-y-inverted").isNull()) {
294 txt->setData(OriginYTop, txtProperties.namedItem("kdenlive-axis-y-inverted").nodeValue().toInt());
298 if (!txtProperties.namedItem("typewriter").isNull()) {
299 QStringList effData = QStringList() << "typewriter" << txtProperties.namedItem("typewriter").nodeValue();
300 txt->setData(100, effData);
304 } else if (items.item(i).attributes().namedItem("type").nodeValue() == "QGraphicsRectItem") {
305 QString rect = items.item(i).namedItem("content").attributes().namedItem("rect").nodeValue();
306 QString br_str = items.item(i).namedItem("content").attributes().namedItem("brushcolor").nodeValue();
307 QString pen_str = items.item(i).namedItem("content").attributes().namedItem("pencolor").nodeValue();
308 double penwidth = items.item(i).namedItem("content").attributes().namedItem("penwidth").nodeValue().toDouble();
309 QGraphicsRectItem *rec = m_scene->addRect(stringToRect(rect), QPen(QBrush(stringToColor(pen_str)), penwidth), QBrush(stringToColor(br_str)));
311 } else if (items.item(i).attributes().namedItem("type").nodeValue() == "QGraphicsPixmapItem") {
312 QString url = items.item(i).namedItem("content").attributes().namedItem("url").nodeValue();
314 QGraphicsPixmapItem *rec = m_scene->addPixmap(pix);
315 rec->setData(Qt::UserRole, url);
317 } else if (items.item(i).attributes().namedItem("type").nodeValue() == "QGraphicsSvgItem") {
318 QString url = items.item(i).namedItem("content").attributes().namedItem("url").nodeValue();
319 QGraphicsSvgItem *rec = new QGraphicsSvgItem(url);
320 m_scene->addItem(rec);
321 rec->setData(Qt::UserRole, url);
327 QPointF p(items.item(i).namedItem("position").attributes().namedItem("x").nodeValue().toDouble(),
328 items.item(i).namedItem("position").attributes().namedItem("y").nodeValue().toDouble());
330 gitem->setTransform(stringToTransform(items.item(i).namedItem("position").firstChild().firstChild().nodeValue()));
331 int zValue = items.item(i).attributes().namedItem("z-index").nodeValue().toInt();
332 if (zValue > maxZValue) maxZValue = zValue;
333 gitem->setZValue(zValue);
334 gitem->setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable);
336 #if QT_VERSION >= 0x040600
338 QDomNode eff = items.item(i).namedItem("effect");
340 QDomElement e = eff.toElement();
341 if (e.attribute("type") == "blur") {
342 QGraphicsBlurEffect *blur = new QGraphicsBlurEffect();
343 blur->setBlurRadius(e.attribute("blurradius").toInt());
344 gitem->setGraphicsEffect(blur);
345 } else if (e.attribute("type") == "shadow") {
346 QGraphicsDropShadowEffect *shadow = new QGraphicsDropShadowEffect();
347 shadow->setBlurRadius(e.attribute("blurradius").toInt());
348 shadow->setOffset(e.attribute("xoffset").toInt(), e.attribute("yoffset").toInt());
349 gitem->setGraphicsEffect(shadow);
355 if (items.item(i).nodeName() == "background") {
356 kDebug() << items.item(i).attributes().namedItem("color").nodeValue();
357 QColor color = QColor(stringToColor(items.item(i).attributes().namedItem("color").nodeValue()));
358 //color.setAlpha(items.item(i).attributes().namedItem("alpha").nodeValue().toInt());
359 QList<QGraphicsItem *> items = m_scene->items();
360 for (int i = 0; i < items.size(); i++) {
361 if (items.at(i)->zValue() == -1100) {
362 ((QGraphicsRectItem *)items.at(i))->setBrush(QBrush(color));
366 } else if (items.item(i).nodeName() == "startviewport" && startv) {
367 QString rect = items.item(i).attributes().namedItem("rect").nodeValue();
368 QRectF r = stringToRect(rect);
369 startv->setRect(0, 0, r.width(), r.height());
370 startv->setPos(r.topLeft());
371 } else if (items.item(i).nodeName() == "endviewport" && endv) {
372 QString rect = items.item(i).attributes().namedItem("rect").nodeValue();
373 QRectF r = stringToRect(rect);
374 endv->setRect(0, 0, r.width(), r.height());
375 endv->setPos(r.topLeft());
382 QString TitleDocument::colorToString(const QColor& c)
384 QString ret = "%1,%2,%3,%4";
385 ret = ret.arg(c.red()).arg(c.green()).arg(c.blue()).arg(c.alpha());
389 QString TitleDocument::rectFToString(const QRectF& c)
391 QString ret = "%1,%2,%3,%4";
392 ret = ret.arg(c.left()).arg(c.top()).arg(c.width()).arg(c.height());
396 QRectF TitleDocument::stringToRect(const QString & s)
399 QStringList l = s.split(',');
402 return QRectF(l.at(0).toDouble(), l.at(1).toDouble(), l.at(2).toDouble(), l.at(3).toDouble()).normalized();
405 QColor TitleDocument::stringToColor(const QString & s)
407 QStringList l = s.split(',');
410 return QColor(l.at(0).toInt(), l.at(1).toInt(), l.at(2).toInt(), l.at(3).toInt());;
412 QTransform TitleDocument::stringToTransform(const QString& s)
414 QStringList l = s.split(',');
418 l.at(0).toDouble(), l.at(1).toDouble(), l.at(2).toDouble(),
419 l.at(3).toDouble(), l.at(4).toDouble(), l.at(5).toDouble(),
420 l.at(6).toDouble(), l.at(7).toDouble(), l.at(8).toDouble()
424 int TitleDocument::frameWidth() const
429 int TitleDocument::frameHeight() const