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 if (txtProperties.namedItem("font-outline").nodeValue().toDouble()>0.0){
277 txt->setData(101,txtProperties.namedItem("font-outline").nodeValue().toDouble());
278 txt->setData(102,stringToColor( txtProperties.namedItem( "font-outline-color" ).nodeValue() ));
279 QTextCursor cursor(txt->document());
280 cursor.select(QTextCursor::Document);
281 QTextCharFormat format;
282 format.setTextOutline(
283 QPen(QColor( stringToColor( txtProperties.namedItem( "font-outline-color" ).nodeValue() ) ),
284 txtProperties.namedItem("font-outline").nodeValue().toDouble())
286 format.setForeground(QBrush(col));
288 cursor.mergeCharFormat(format);
290 txt->setDefaultTextColor( col );
292 txt->setTextInteractionFlags(Qt::NoTextInteraction);
293 if (txtProperties.namedItem("alignment").isNull() == false) {
294 txt->setTextWidth(txt->boundingRect().width());
295 QTextCursor cur = txt->textCursor();
296 QTextBlockFormat format = cur.blockFormat();
297 format.setAlignment((Qt::Alignment) txtProperties.namedItem("alignment").nodeValue().toInt());
298 cur.select(QTextCursor::Document);
299 cur.setBlockFormat(format);
300 txt->setTextCursor(cur);
301 cur.clearSelection();
302 txt->setTextCursor(cur);
305 if (!txtProperties.namedItem("kdenlive-axis-x-inverted").isNull()) {
306 txt->setData(OriginXLeft, txtProperties.namedItem("kdenlive-axis-x-inverted").nodeValue().toInt());
308 if (!txtProperties.namedItem("kdenlive-axis-y-inverted").isNull()) {
309 txt->setData(OriginYTop, txtProperties.namedItem("kdenlive-axis-y-inverted").nodeValue().toInt());
313 if (!txtProperties.namedItem("typewriter").isNull()) {
314 QStringList effData = QStringList() << "typewriter" << txtProperties.namedItem("typewriter").nodeValue();
315 txt->setData(100, effData);
319 } else if (items.item(i).attributes().namedItem("type").nodeValue() == "QGraphicsRectItem") {
320 QString rect = items.item(i).namedItem("content").attributes().namedItem("rect").nodeValue();
321 QString br_str = items.item(i).namedItem("content").attributes().namedItem("brushcolor").nodeValue();
322 QString pen_str = items.item(i).namedItem("content").attributes().namedItem("pencolor").nodeValue();
323 double penwidth = items.item(i).namedItem("content").attributes().namedItem("penwidth").nodeValue().toDouble();
324 QGraphicsRectItem *rec = m_scene->addRect(stringToRect(rect), QPen(QBrush(stringToColor(pen_str)), penwidth), QBrush(stringToColor(br_str)));
326 } else if (items.item(i).attributes().namedItem("type").nodeValue() == "QGraphicsPixmapItem") {
327 QString url = items.item(i).namedItem("content").attributes().namedItem("url").nodeValue();
329 QGraphicsPixmapItem *rec = m_scene->addPixmap(pix);
330 rec->setData(Qt::UserRole, url);
332 } else if (items.item(i).attributes().namedItem("type").nodeValue() == "QGraphicsSvgItem") {
333 QString url = items.item(i).namedItem("content").attributes().namedItem("url").nodeValue();
334 QGraphicsSvgItem *rec = new QGraphicsSvgItem(url);
335 m_scene->addItem(rec);
336 rec->setData(Qt::UserRole, url);
342 QPointF p(items.item(i).namedItem("position").attributes().namedItem("x").nodeValue().toDouble(),
343 items.item(i).namedItem("position").attributes().namedItem("y").nodeValue().toDouble());
345 gitem->setTransform(stringToTransform(items.item(i).namedItem("position").firstChild().firstChild().nodeValue()));
346 int zValue = items.item(i).attributes().namedItem("z-index").nodeValue().toInt();
347 if (zValue > maxZValue) maxZValue = zValue;
348 gitem->setZValue(zValue);
349 gitem->setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable);
351 #if QT_VERSION >= 0x040600
353 QDomNode eff = items.item(i).namedItem("effect");
355 QDomElement e = eff.toElement();
356 if (e.attribute("type") == "blur") {
357 QGraphicsBlurEffect *blur = new QGraphicsBlurEffect();
358 blur->setBlurRadius(e.attribute("blurradius").toInt());
359 gitem->setGraphicsEffect(blur);
360 } else if (e.attribute("type") == "shadow") {
361 QGraphicsDropShadowEffect *shadow = new QGraphicsDropShadowEffect();
362 shadow->setBlurRadius(e.attribute("blurradius").toInt());
363 shadow->setOffset(e.attribute("xoffset").toInt(), e.attribute("yoffset").toInt());
364 gitem->setGraphicsEffect(shadow);
370 if (items.item(i).nodeName() == "background") {
371 kDebug() << items.item(i).attributes().namedItem("color").nodeValue();
372 QColor color = QColor(stringToColor(items.item(i).attributes().namedItem("color").nodeValue()));
373 //color.setAlpha(items.item(i).attributes().namedItem("alpha").nodeValue().toInt());
374 QList<QGraphicsItem *> items = m_scene->items();
375 for (int i = 0; i < items.size(); i++) {
376 if (items.at(i)->zValue() == -1100) {
377 ((QGraphicsRectItem *)items.at(i))->setBrush(QBrush(color));
381 } else if (items.item(i).nodeName() == "startviewport" && startv) {
382 QString rect = items.item(i).attributes().namedItem("rect").nodeValue();
383 QRectF r = stringToRect(rect);
384 startv->setRect(0, 0, r.width(), r.height());
385 startv->setPos(r.topLeft());
386 } else if (items.item(i).nodeName() == "endviewport" && endv) {
387 QString rect = items.item(i).attributes().namedItem("rect").nodeValue();
388 QRectF r = stringToRect(rect);
389 endv->setRect(0, 0, r.width(), r.height());
390 endv->setPos(r.topLeft());
397 QString TitleDocument::colorToString(const QColor& c)
399 QString ret = "%1,%2,%3,%4";
400 ret = ret.arg(c.red()).arg(c.green()).arg(c.blue()).arg(c.alpha());
404 QString TitleDocument::rectFToString(const QRectF& c)
406 QString ret = "%1,%2,%3,%4";
407 ret = ret.arg(c.left()).arg(c.top()).arg(c.width()).arg(c.height());
411 QRectF TitleDocument::stringToRect(const QString & s)
414 QStringList l = s.split(',');
417 return QRectF(l.at(0).toDouble(), l.at(1).toDouble(), l.at(2).toDouble(), l.at(3).toDouble()).normalized();
420 QColor TitleDocument::stringToColor(const QString & s)
422 QStringList l = s.split(',');
425 return QColor(l.at(0).toInt(), l.at(1).toInt(), l.at(2).toInt(), l.at(3).toInt());;
427 QTransform TitleDocument::stringToTransform(const QString& s)
429 QStringList l = s.split(',');
433 l.at(0).toDouble(), l.at(1).toDouble(), l.at(2).toDouble(),
434 l.at(3).toDouble(), l.at(4).toDouble(), l.at(5).toDouble(),
435 l.at(6).toDouble(), l.at(7).toDouble(), l.at(8).toDouble()
439 int TitleDocument::frameWidth() const
444 int TitleDocument::frameHeight() const