]> git.sesse.net Git - kdenlive/blobdiff - src/titlewidget.cpp
Fix dropframe timecode, patch from John T. Mertz
[kdenlive] / src / titlewidget.cpp
index 1b1384b5cdc69ad2194dfbaefab5ab42a994a16d..05c5ad72e29cffa7d4e69193738aeb0a77bec81e 100644 (file)
@@ -17,7 +17,8 @@
 
 #include "titlewidget.h"
 #include "kdenlivesettings.h"
-
+#include "KoSliderCombo.h"
+#include "kthumb.h"
 #include <cmath>
 
 #include <KDebug>
@@ -37,6 +38,8 @@
 #include <QSignalMapper>
 #include <QTextBlockFormat>
 #include <QTextCursor>
+#include <QComboBox>
+#include <QCryptographicHash>
 
 #if QT_VERSION >= 0x040600
 #include <QGraphicsEffect>
@@ -44,6 +47,8 @@
 #include <QGraphicsDropShadowEffect>
 #endif
 
+static QList<TitleTemplate> titletemplates;
+
 int settingUp = false;
 
 const int IMAGEITEM = 7;
@@ -71,33 +76,97 @@ TitleWidget::TitleWidget(KUrl url, Timecode tc, QString projectTitlePath, Render
     frame_properties->setEnabled(false);
     frame_properties->setFixedHeight(frame_toolbar->height());
 
+    // Set combo sliders values
+    textAlpha->setMinimum(0);
+    textAlpha->setMaximum(255);
+    textAlpha->setDecimals(0);
+    textAlpha->setValue(255);
+    textAlpha->setToolTip(i18n("Font color opacity"));
+
+    textOutlineAlpha->setMinimum(0);
+    textOutlineAlpha->setMaximum(255);
+    textOutlineAlpha->setDecimals(0);
+    textOutlineAlpha->setValue(255);
+    textOutlineAlpha->setToolTip(i18n("Outline color opacity"));
+
+    textOutline->setMinimum(0);
+    textOutline->setMaximum(200);
+    textOutline->setDecimals(0);
+    textOutline->setValue(0);
+    textOutline->setToolTip(i18n("Outline width"));
+
+    backgroundAlpha->setMinimum(0);
+    backgroundAlpha->setMaximum(255);
+    backgroundAlpha->setDecimals(0);
+    backgroundAlpha->setValue(0);
+    backgroundAlpha->setToolTip(i18n("Background color opacity"));
+
+    itemrotatex->setMinimum(-360);
+    itemrotatex->setMaximum(360);
+    itemrotatex->setDecimals(0);
+    itemrotatex->setValue(0);
+    itemrotatex->setToolTip(i18n("Rotation around the X axis"));
+
+    itemrotatey->setMinimum(-360);
+    itemrotatey->setMaximum(360);
+    itemrotatey->setDecimals(0);
+    itemrotatey->setValue(0);
+    itemrotatey->setToolTip(i18n("Rotation around the Y axis"));
+
+    itemrotatez->setMinimum(-360);
+    itemrotatez->setMaximum(360);
+    itemrotatez->setDecimals(0);
+    itemrotatez->setValue(0);
+    itemrotatez->setToolTip(i18n("Rotation around the Z axis"));
+
+    rectBAlpha->setMinimum(0);
+    rectBAlpha->setMaximum(255);
+    rectBAlpha->setDecimals(0);
+    rectBAlpha->setValue(255);
+    rectBAlpha->setToolTip(i18n("Color opacity"));
+
+    rectFAlpha->setMinimum(0);
+    rectFAlpha->setMaximum(255);
+    rectFAlpha->setDecimals(0);
+    rectFAlpha->setValue(255);
+    rectFAlpha->setToolTip(i18n("Border opacity"));
+
+    rectLineWidth->setMinimum(0);
+    rectLineWidth->setMaximum(100);
+    rectLineWidth->setDecimals(0);
+    rectLineWidth->setValue(0);
+    rectLineWidth->setToolTip(i18n("Border width"));
+
     itemzoom->setSuffix(i18n("%"));
     m_frameWidth = render->renderWidth();
     m_frameHeight = render->renderHeight();
     showToolbars(TITLE_SELECT);
 
+    //If project is drop frame, set the input mask as such.
+    title_duration->setInputMask(m_tc.inputMask());
+
     //TODO: get default title duration instead of hardcoded one
     title_duration->setText(m_tc.getTimecode(GenTime(5000 / 1000.0)));
 
-    connect(kcolorbutton, SIGNAL(clicked()), this, SLOT(slotChangeBackground())) ;
-    connect(horizontalSlider, SIGNAL(valueChanged(int)), this, SLOT(slotChangeBackground())) ;
+    connect(backgroundColor, SIGNAL(clicked()), this, SLOT(slotChangeBackground())) ;
+    connect(backgroundAlpha, SIGNAL(valueChanged(qreal, bool)), this, SLOT(slotChangeBackground())) ;
 
     connect(fontColorButton, SIGNAL(clicked()), this, SLOT(slotUpdateText())) ;
     connect(textOutlineColor, SIGNAL(clicked()), this, SLOT(slotUpdateText())) ;
     connect(font_family, SIGNAL(currentFontChanged(const QFont &)), this, SLOT(slotUpdateText())) ;
     connect(font_size, SIGNAL(valueChanged(int)), this, SLOT(slotUpdateText())) ;
-    connect(textAlpha, SIGNAL(valueChanged(int)), this, SLOT(slotUpdateText()));
-    connect(textOutline, SIGNAL(valueChanged(int)), this, SLOT(slotUpdateText()));
-    connect(textOutlineAlpha, SIGNAL(valueChanged(int)), this, SLOT(slotUpdateText()));
+    connect(textAlpha, SIGNAL(valueChanged(qreal, bool)), this, SLOT(slotUpdateText()));
+    connect(textOutline, SIGNAL(valueChanged(qreal, bool)), this, SLOT(slotUpdateText()));
+    connect(textOutlineAlpha, SIGNAL(valueChanged(qreal, bool)), this, SLOT(slotUpdateText()));
     connect(font_weight_box, SIGNAL(currentIndexChanged(int)), this, SLOT(slotUpdateText()));
 
     connect(font_family, SIGNAL(editTextChanged(const QString &)), this, SLOT(slotFontText(const QString&)));
 
-    connect(rectFAlpha, SIGNAL(valueChanged(int)), this, SLOT(rectChanged()));
-    connect(rectBAlpha, SIGNAL(valueChanged(int)), this, SLOT(rectChanged()));
+    connect(rectFAlpha, SIGNAL(valueChanged(qreal, bool)), this, SLOT(rectChanged()));
+    connect(rectBAlpha, SIGNAL(valueChanged(qreal, bool)), this, SLOT(rectChanged()));
     connect(rectFColor, SIGNAL(clicked()), this, SLOT(rectChanged()));
     connect(rectBColor, SIGNAL(clicked()), this, SLOT(rectChanged()));
-    connect(rectLineWidth, SIGNAL(valueChanged(int)), this, SLOT(rectChanged()));
+    connect(rectLineWidth, SIGNAL(valueChanged(qreal, bool)), this, SLOT(rectChanged()));
 
     /*connect(startViewportX, SIGNAL(valueChanged(int)), this, SLOT(setupViewports()));
     connect(startViewportY, SIGNAL(valueChanged(int)), this, SLOT(setupViewports()));
@@ -114,7 +183,9 @@ TitleWidget::TitleWidget(KUrl url, Timecode tc, QString projectTitlePath, Render
 
     connect(zValue, SIGNAL(valueChanged(int)), this, SLOT(zIndexChanged(int)));
     connect(itemzoom, SIGNAL(valueChanged(int)), this, SLOT(itemScaled(int)));
-    connect(itemrotate, SIGNAL(valueChanged(int)), this, SLOT(itemRotate(int)));
+    connect(itemrotatex, SIGNAL(valueChanged(qreal, bool)), this, SLOT(itemRotateX(qreal)));
+    connect(itemrotatey, SIGNAL(valueChanged(qreal, bool)), this, SLOT(itemRotateY(qreal)));
+    connect(itemrotatez, SIGNAL(valueChanged(qreal, bool)), this, SLOT(itemRotateZ(qreal)));
     connect(itemhcenter, SIGNAL(clicked()), this, SLOT(itemHCenter()));
     connect(itemvcenter, SIGNAL(clicked()), this, SLOT(itemVCenter()));
     connect(itemtop, SIGNAL(clicked()), this, SLOT(itemTop()));
@@ -153,7 +224,6 @@ TitleWidget::TitleWidget(KUrl url, Timecode tc, QString projectTitlePath, Render
     connect(buttonAlignRight, SIGNAL(clicked()), this, SLOT(slotUpdateText()));
     connect(buttonAlignCenter, SIGNAL(clicked()), this, SLOT(slotUpdateText()));
     connect(buttonAlignNone, SIGNAL(clicked()), this, SLOT(slotUpdateText()));
-    //connect(buttonInsertUnicode, SIGNAL(clicked()), this, SLOT(slotInsertUnicode()));
     connect(displayBg, SIGNAL(stateChanged(int)), this, SLOT(displayBackgroundFrame()));
 
     connect(m_unicodeDialog, SIGNAL(charSelected(QString)), this, SLOT(slotInsertUnicodeString(QString)));
@@ -204,13 +274,16 @@ TitleWidget::TitleWidget(KUrl url, Timecode tc, QString projectTitlePath, Render
     zDown->setDefaultAction(m_zDown);
 
     m_zTop = new QAction(KIcon("kdenlive-zindex-top"), QString(), this);
-    m_zTop->setShortcut(Qt::Key_Home);
+    // TODO mbt 1414: Shortcut should change z index only if
+    // cursor is NOT in a text field ...
+    //m_zTop->setShortcut(Qt::Key_Home);
     m_zTop->setToolTip(i18n("Raise object to top"));
     connect(m_zTop, SIGNAL(triggered()), this, SLOT(slotZIndexTop()));
     zTop->setDefaultAction(m_zTop);
 
     m_zBottom = new QAction(KIcon("kdenlive-zindex-bottom"), QString(), this);
-    m_zBottom->setShortcut(Qt::Key_End);
+    // TODO mbt 1414
+    //m_zBottom->setShortcut(Qt::Key_End);
     m_zBottom->setToolTip(i18n("Lower object to bottom"));
     connect(m_zBottom, SIGNAL(triggered()), this, SLOT(slotZIndexBottom()));
     zBottom->setDefaultAction(m_zBottom);
@@ -226,13 +299,13 @@ TitleWidget::TitleWidget(KUrl url, Timecode tc, QString projectTitlePath, Render
     origin_y_top->setToolTip(i18n("Invert y axis and change 0 point"));
     rectBColor->setToolTip(i18n("Select fill color"));
     rectFColor->setToolTip(i18n("Select border color"));
-    rectBAlpha->setToolTip(i18n("Fill transparency"));
-    rectFAlpha->setToolTip(i18n("Border transparency"));
+    rectBAlpha->setToolTip(i18n("Fill opacity"));
+    rectFAlpha->setToolTip(i18n("Border opacity"));
     zoom_slider->setToolTip(i18n("Zoom"));
     buttonRealSize->setToolTip(i18n("Original size (1:1)"));
     buttonFitZoom->setToolTip(i18n("Fit zoom"));
-    kcolorbutton->setToolTip(i18n("Select background color"));
-    horizontalSlider->setToolTip(i18n("Background Transparency"));
+    backgroundColor->setToolTip(i18n("Select background color"));
+    backgroundAlpha->setToolTip(i18n("Background opacity"));
 
     itemhcenter->setIcon(KIcon("kdenlive-align-hor"));
     itemhcenter->setToolTip(i18n("Align item horizontally"));
@@ -296,6 +369,7 @@ TitleWidget::TitleWidget(KUrl url, Timecode tc, QString projectTitlePath, Render
     // initialize graphic scene
     m_scene = new GraphicsSceneRectMove(this);
     graphicsView->setScene(m_scene);
+    graphicsView->setMouseTracking(true);
     m_titledocument.setScene(m_scene, m_frameWidth, m_frameHeight);
     connect(m_scene, SIGNAL(changed(QList<QRectF>)), this, SLOT(slotChanged()));
     connect(font_size, SIGNAL(valueChanged(int)), m_scene, SLOT(slotUpdateFontSize(int)));
@@ -357,8 +431,18 @@ TitleWidget::TitleWidget(KUrl url, Timecode tc, QString projectTitlePath, Render
     initAnimation();
     connect(anim_start, SIGNAL(toggled(bool)), this, SLOT(slotAnimStart(bool)));
     connect(anim_end, SIGNAL(toggled(bool)), this, SLOT(slotAnimEnd(bool)));
+       connect(templateBox,SIGNAL(currentIndexChanged(int)),this,SLOT(templateIndexChanged(int)));
 
     buttonBox->button(QDialogButtonBox::Ok)->setEnabled(KdenliveSettings::hastitleproducer());
+       refreshTitleTemplates();
+       //templateBox->setIconSize(QSize(60,60));
+       templateBox->clear();
+       templateBox->addItem("");
+       foreach (TitleTemplate t, titletemplates)
+       {
+               templateBox->addItem(t.icon,t.name,t.file);
+       }
+       lastDocumentHash=QCryptographicHash::hash(xml().toString().toAscii(), QCryptographicHash::Md5).toHex();
 }
 
 TitleWidget::~TitleWidget()
@@ -426,7 +510,56 @@ QStringList TitleWidget::extractImageList(QString xml)
     return result;
 }
 
-
+// static
+QStringList TitleWidget::extractFontList(QString xml)
+{
+    QStringList result;
+    if (xml.isEmpty()) return result;
+    QDomDocument doc;
+    doc.setContent(xml);
+    QDomNodeList images = doc.elementsByTagName("content");
+    for (int i = 0; i < images.count(); i++) {
+        if (images.at(i).toElement().hasAttribute("font"))
+            result.append(images.at(i).toElement().attribute("font"));
+    }
+    return result;
+}
+//static
+void TitleWidget::refreshTitleTemplates()
+{
+       QStringList titlenamelist = QStringList() << i18n("None");
+    QStringList titlefiles = QStringList() << QString();
+    QStringList filters;
+    filters << "*.kdenlivetitle" ;
+       titletemplates.clear();
+    QStringList titleTemplates = KGlobal::dirs()->findDirs("appdata", "titles");
+    foreach(const QString &folder, titleTemplates) {
+        QStringList filesnames = QDir(folder).entryList(filters, QDir::Files);
+        foreach(const QString &fname, filesnames) {
+                       //titlenamelist.append(fname);
+            //titlefiles.append(KUrl(folder).path(KUrl::AddTrailingSlash) + fname);
+                       TitleTemplate t;
+                       t.name=fname;
+                       t.file=KUrl(folder).path(KUrl::AddTrailingSlash) + fname;
+                       t.icon=QIcon(KThumb::getImage(t.file,0,60,60));
+                       titletemplates.append(t);               
+        }
+    }
+       kDebug()  << titlenamelist << titlefiles;
+}
+void TitleWidget::templateIndexChanged(int index )
+{
+       QString item=templateBox->itemData(index).toString();
+       if (item!="")
+       {
+               if (lastDocumentHash!=QCryptographicHash::hash(xml().toString().toAscii(), QCryptographicHash::Md5).toHex())
+               {
+                        if (KMessageBox::questionYesNo(this, i18n("Title was changed !\nDo you really want to load a new template?\nAll changes in this document are lost !!")) == KMessageBox::No) return;
+               }
+               loadTitle(item);
+               lastDocumentHash=QCryptographicHash::hash(xml().toString().toAscii(), QCryptographicHash::Md5).toHex();
+       }
+}
 //virtual
 void TitleWidget::resizeEvent(QResizeEvent * /*event*/)
 {
@@ -644,11 +777,13 @@ void TitleWidget::slotNewRect(QGraphicsRectItem * rect)
     f.setAlpha(rectFAlpha->value());
     QPen penf(f);
     penf.setWidth(rectLineWidth->value());
+    penf.setJoinStyle(Qt::RoundJoin);
     rect->setPen(penf);
     QColor b = rectBColor->color();
     b.setAlpha(rectBAlpha->value());
     rect->setBrush(QBrush(b));
     rect->setZValue(m_count++);
+    rect->setData(ZOOMFACTOR, 100);
     //setCurrentItem(rect);
     //graphicsView->setFocus();
 }
@@ -668,6 +803,23 @@ void TitleWidget::slotNewText(QGraphicsTextItem *tt)
     QColor color = fontColorButton->color();
     color.setAlpha(textAlpha->value());
     tt->setDefaultTextColor(color);
+
+    QTextCursor cur(tt->document());
+    cur.select(QTextCursor::Document);
+    QTextBlockFormat format = cur.blockFormat();
+    QTextCharFormat cformat = cur.charFormat();
+    QColor outlineColor = textOutlineColor->color();
+    outlineColor.setAlpha(textOutlineAlpha->value());
+    double outlineWidth = textOutline->value() / 10.0;
+
+    tt->setData(101, outlineWidth);
+    tt->setData(102, outlineColor);
+    if (outlineWidth > 0.0) cformat.setTextOutline(QPen(outlineColor, outlineWidth));
+
+    cformat.setForeground(QBrush(color));
+    cur.setCharFormat(cformat);
+    cur.setBlockFormat(format);
+    tt->setTextCursor(cur);
     tt->setZValue(m_count++);
     setCurrentItem(tt);
 }
@@ -705,7 +857,9 @@ void TitleWidget::selectionChanged()
     value_w->blockSignals(true);
     value_h->blockSignals(true);
     itemzoom->blockSignals(true);
-    itemrotate->blockSignals(true);
+    itemrotatex->blockSignals(true);
+    itemrotatey->blockSignals(true);
+    itemrotatez->blockSignals(true);
     if (l.size() == 0) {
         effect_stack->setHidden(true);
         effect_frame->setEnabled(false);
@@ -722,17 +876,23 @@ void TitleWidget::selectionChanged()
         if (blockX) origin_x_left->blockSignals(false);
         if (blockY) origin_y_top->blockSignals(false);
         itemzoom->setEnabled(false);
-        itemrotate->setEnabled(false);
+        itemrotatex->setEnabled(false);
+        itemrotatey->setEnabled(false);
+        itemrotatez->setEnabled(false);
         frame_properties->setEnabled(false);
     } else if (l.size() == 1) {
         effect_frame->setEnabled(true);
         frame_properties->setEnabled(true);
         if (l.at(0) != m_startViewport && l.at(0) != m_endViewport) {
             itemzoom->setEnabled(true);
-            itemrotate->setEnabled(true);
+            itemrotatex->setEnabled(true);
+            itemrotatey->setEnabled(true);
+            itemrotatez->setEnabled(true);
         } else {
             itemzoom->setEnabled(false);
-            itemrotate->setEnabled(false);
+            itemrotatex->setEnabled(false);
+            itemrotatey->setEnabled(false);
+            itemrotatez->setEnabled(false);
             updateInfoText();
         }
         if (l.at(0)->type() == TEXTITEM) {
@@ -804,12 +964,12 @@ void TitleWidget::selectionChanged()
             buttonUnder->setChecked(font.underline());
             setFontBoxWeight(font.weight());
 
-            QColor color = i->defaultTextColor();
             QTextCursor cursor(i->document());
             cursor.select(QTextCursor::Document);
-            color = cursor.charFormat().foreground().color();
-            fontColorButton->setColor(color);
+            QColor color = cursor.charFormat().foreground().color();
             textAlpha->setValue(color.alpha());
+            color.setAlpha(255);
+            fontColorButton->setColor(color);
 
             if (!i->data(101).isNull()) {
                 textOutline->blockSignals(true);
@@ -819,8 +979,10 @@ void TitleWidget::selectionChanged()
             if (!i->data(102).isNull()) {
                 textOutlineColor->blockSignals(true);
                 textOutlineAlpha->blockSignals(true);
-                textOutlineColor->setColor(i->data(102).toString());
-                textOutlineAlpha->setValue(QColor(i->data(102).toString()).alpha());
+                color = QColor(i->data(102).toString());
+                textOutlineAlpha->setValue(color.alpha());
+                color.setAlpha(255);
+                textOutlineColor->setColor(color);
                 textOutlineColor->blockSignals(false);
                 textOutlineAlpha->blockSignals(false);
             }
@@ -895,14 +1057,19 @@ void TitleWidget::selectionChanged()
             frame_properties->setEnabled(false);
         }
         zValue->setValue((int)l.at(0)->zValue());
-        itemzoom->setValue((int)(m_transformations.value(l.at(0)).scalex * 100.0 + 0.5));
-        itemrotate->setValue((int)(m_transformations.value(l.at(0)).rotate));
+        if (!l.at(0)->data(ZOOMFACTOR).isNull()) itemzoom->setValue(l.at(0)->data(ZOOMFACTOR).toInt());
+        else itemzoom->setValue((int)(m_transformations.value(l.at(0)).scalex * 100.0 + 0.5));
+        itemrotatex->setValue((int)(m_transformations.value(l.at(0)).rotatex));
+        itemrotatey->setValue((int)(m_transformations.value(l.at(0)).rotatey));
+        itemrotatez->setValue((int)(m_transformations.value(l.at(0)).rotatez));
         value_x->blockSignals(false);
         value_y->blockSignals(false);
         value_w->blockSignals(false);
         value_h->blockSignals(false);
         itemzoom->blockSignals(false);
-        itemrotate->blockSignals(false);
+        itemrotatex->blockSignals(false);
+        itemrotatey->blockSignals(false);
+        itemrotatez->blockSignals(false);
     }
 }
 
@@ -926,7 +1093,8 @@ void TitleWidget::slotValueChanged(int type)
 
         // Ratio width:height
         double phi = (double) i->boundingRect().width() / i->boundingRect().height();
-        double alpha = (double) t.rotate / 180.0 * M_PI;
+        // TODO: proper calculation for rotation around 3 axes
+        double alpha = (double) t.rotatez / 180.0 * M_PI;
 
         // New length
         double length = val;
@@ -950,7 +1118,9 @@ void TitleWidget::slotValueChanged(int type)
         t.scaley = scale;
         QTransform qtrans;
         qtrans.scale(scale, scale);
-        qtrans.rotate(t.rotate);
+        qtrans.rotate(t.rotatex, Qt::XAxis);
+        qtrans.rotate(t.rotatey, Qt::YAxis);
+        qtrans.rotate(t.rotatez, Qt::ZAxis);
         i->setTransform(qtrans);
         m_transformations[i] = t;
 
@@ -1084,14 +1254,23 @@ void TitleWidget::updateCoordinates(QGraphicsItem *i)
 void TitleWidget::updateRotZoom(QGraphicsItem *i)
 {
     itemzoom->blockSignals(true);
-    itemrotate->blockSignals(false);
+    itemrotatex->blockSignals(true);
+    itemrotatey->blockSignals(true);
+    itemrotatez->blockSignals(true);
 
     Transform t = m_transformations.value(i);
-    itemzoom->setValue((int)(t.scalex * 100.0 + 0.5));
-    itemrotate->setValue((int)(t.rotate));
+
+    if (!i->data(ZOOMFACTOR).isNull()) itemzoom->setValue(i->data(ZOOMFACTOR).toInt());
+    else itemzoom->setValue((int)(t.scalex * 100.0 + 0.5));
+
+    itemrotatex->setValue((int)(t.rotatex));
+    itemrotatey->setValue((int)(t.rotatey));
+    itemrotatez->setValue((int)(t.rotatez));
 
     itemzoom->blockSignals(false);
-    itemrotate->blockSignals(false);
+    itemrotatex->blockSignals(false);
+    itemrotatey->blockSignals(false);
+    itemrotatez->blockSignals(false);
 }
 
 /** \brief Updates the position of an item by reading coordinates from the text fields */
@@ -1241,8 +1420,8 @@ void TitleWidget::updateAxisButtons(QGraphicsItem *i)
 
 void TitleWidget::slotChangeBackground()
 {
-    QColor color = kcolorbutton->color();
-    color.setAlpha(horizontalSlider->value());
+    QColor color = backgroundColor->color();
+    color.setAlpha(backgroundAlpha->value());
     m_frameBorder->setBrush(QBrush(color));
 }
 
@@ -1322,7 +1501,8 @@ void TitleWidget::slotUpdateText()
     }
     if (!item) return;
     //if (item->textCursor().selection ().isEmpty())
-    QTextCursor cur = item->textCursor();
+    QTextCursor cur(item->document());
+    cur.select(QTextCursor::Document);
     QTextBlockFormat format = cur.blockFormat();
     if (buttonAlignLeft->isChecked() || buttonAlignCenter->isChecked() || buttonAlignRight->isChecked()) {
         item->setTextWidth(item->boundingRect().width());
@@ -1334,27 +1514,19 @@ void TitleWidget::slotUpdateText()
         item->setTextWidth(-1);
     }
 
-    {
-        item->setFont(font);
-        if (outlineWidth > 0.0) {
-            item->setData(101, outlineWidth);
-            item->setData(102, outlineColor);
-            QTextCursor cursor(item->document());
-            cursor.select(QTextCursor::Document);
-            QTextCharFormat format;
-            format.setTextOutline(QPen(outlineColor, outlineWidth));
-            format.setForeground(QBrush(color));
-            cursor.mergeCharFormat(format);
-        } else {
-            item->setDefaultTextColor(color);
-        }
-        cur.select(QTextCursor::Document);
-        cur.setBlockFormat(format);
-        item->setTextCursor(cur);
-        cur.clearSelection();
-        item->setTextCursor(cur);
+    item->setFont(font);
+    QTextCharFormat cformat = cur.charFormat();
 
-    }
+    item->setData(101, outlineWidth);
+    item->setData(102, outlineColor);
+    if (outlineWidth > 0.0) cformat.setTextOutline(QPen(outlineColor, outlineWidth, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
+
+    cformat.setForeground(QBrush(color));
+    cur.setCharFormat(cformat);
+    cur.setBlockFormat(format);
+    item->setTextCursor(cur);
+    cur.clearSelection();
+    item->setTextCursor(cur);
 }
 
 void TitleWidget::rectChanged()
@@ -1366,6 +1538,7 @@ void TitleWidget::rectChanged()
         f.setAlpha(rectFAlpha->value());
         QPen penf(f);
         penf.setWidth(rectLineWidth->value());
+        penf.setJoinStyle(Qt::RoundJoin);
         rec->setPen(penf);
         QColor b = rectBColor->color();
         b.setAlpha(rectBAlpha->value());
@@ -1382,24 +1555,58 @@ void TitleWidget::itemScaled(int val)
         x.scaley = (double)val / 100.0;
         QTransform qtrans;
         qtrans.scale(x.scalex, x.scaley);
-        qtrans.rotate(x.rotate);
+        qtrans.rotate(x.rotatex, Qt::XAxis);
+        qtrans.rotate(x.rotatey, Qt::YAxis);
+        qtrans.rotate(x.rotatez, Qt::ZAxis);
         l[0]->setTransform(qtrans);
+        l[0]->setData(ZOOMFACTOR, val);
         m_transformations[l.at(0)] = x;
         updateDimension(l.at(0));
     }
 }
 
-void TitleWidget::itemRotate(int val)
+void TitleWidget::itemRotateX(qreal val)
+{
+    itemRotate(val, 0);
+}
+
+void TitleWidget::itemRotateY(qreal val)
+{
+    itemRotate(val, 1);
+}
+
+void TitleWidget::itemRotateZ(qreal val)
+{
+    itemRotate(val, 2);
+}
+
+void TitleWidget::itemRotate(qreal val, int axis)
 {
     QList<QGraphicsItem*> l = graphicsView->scene()->selectedItems();
     if (l.size() == 1) {
         Transform x = m_transformations[l.at(0)];
-        x.rotate = (double)val;
+        switch (axis) {
+        case 0:
+            x.rotatex = val;
+            break;
+        case 1:
+            x.rotatey = val;
+            break;
+        case 2:
+            x.rotatez = val;
+            break;
+        }
+
+        l[0]->setData(ROTATEFACTOR, QList<QVariant>() << QVariant(x.rotatex) << QVariant(x.rotatey) << QVariant(x.rotatez));
+
         QTransform qtrans;
         qtrans.scale(x.scalex, x.scaley);
-        qtrans.rotate(x.rotate);
+        qtrans.rotate(x.rotatex, Qt::XAxis);
+        qtrans.rotate(x.rotatey, Qt::YAxis);
+        qtrans.rotate(x.rotatez, Qt::ZAxis);
         l[0]->setTransform(qtrans);
         m_transformations[l.at(0)] = x;
+        if (l[0]->data(ZOOMFACTOR).isNull()) l[0]->setData(ZOOMFACTOR, 100);
         updateDimension(l.at(0));
     }
 }
@@ -1607,18 +1814,40 @@ void TitleWidget::setXml(QDomDocument doc)
         Transform x;
         x.scalex = t.m11();
         x.scaley = t.m22();
-        x.rotate = 180. / PI * atan2(-t.m21(), t.m11());
+        if (!items.at(i)->data(ROTATEFACTOR).isNull()) {
+            QList<QVariant> rotlist = items.at(i)->data(ROTATEFACTOR).toList();
+            if (rotlist.count() >= 3) {
+                x.rotatex = rotlist[0].toDouble();
+                x.rotatey = rotlist[1].toDouble();
+                x.rotatez = rotlist[2].toDouble();
+
+                // Try to adjust zoom
+                t.rotate(x.rotatex *(-1), Qt::XAxis);
+                t.rotate(x.rotatey *(-1), Qt::YAxis);
+                t.rotate(x.rotatez *(-1), Qt::ZAxis);
+                x.scalex = t.m11();
+                x.scaley = t.m22();
+            } else {
+                x.rotatex = 0;
+                x.rotatey = 0;
+                x.rotatez = 0;
+            }
+        } else {
+            x.rotatex = 0;
+            x.rotatey = 0;
+            x.rotatez = 180. / PI * atan2(-t.m21(), t.m11());
+        }
         m_transformations[items.at(i)] = x;
     }
     // mbd: Update the GUI color selectors to match the stuff from the loaded document
     QColor background_color = m_titledocument.getBackgroundColor();
-    horizontalSlider->blockSignals(true);
-    kcolorbutton->blockSignals(true);
-    horizontalSlider->setValue(background_color.alpha());
+    backgroundAlpha->blockSignals(true);
+    backgroundColor->blockSignals(true);
+    backgroundAlpha->setValue(background_color.alpha());
     background_color.setAlpha(255);
-    kcolorbutton->setColor(background_color);
-    horizontalSlider->blockSignals(false);
-    kcolorbutton->blockSignals(false);
+    backgroundColor->setColor(background_color);
+    backgroundAlpha->blockSignals(false);
+    backgroundColor->blockSignals(false);
 
     /*startViewportX->setValue(m_startViewport->data(0).toInt());
     startViewportY->setValue(m_startViewport->data(1).toInt());
@@ -1629,6 +1858,7 @@ void TitleWidget::setXml(QDomDocument doc)
 
     QTimer::singleShot(200, this, SLOT(slotAdjustZoom()));
     slotSelectTool();
+    selectionChanged();
 }
 
 /** \brief Connected to the accepted signal - calls writeChoices */
@@ -1651,6 +1881,9 @@ void TitleWidget::writeChoices()
     titleConfig.writeEntry("font_pixel_size", font_size->value());
     titleConfig.writeEntry("font_color", fontColorButton->color());
     titleConfig.writeEntry("font_alpha", textAlpha->value());
+    titleConfig.writeEntry("font_outline", textOutline->value());
+    titleConfig.writeEntry("font_outline_color", textOutlineColor->color());
+    titleConfig.writeEntry("font_outline_alpha", textOutlineAlpha->value());
     titleConfig.writeEntry("font_weight", font_weight_box->itemData(font_weight_box->currentIndex()).toInt());
     titleConfig.writeEntry("font_italic", buttonItalic->isChecked());
     titleConfig.writeEntry("font_underlined", buttonUnder->isChecked());
@@ -1661,8 +1894,8 @@ void TitleWidget::writeChoices()
     titleConfig.writeEntry("rect_background_alpha", rectBAlpha->value());
     titleConfig.writeEntry("rect_line_width", rectLineWidth->value());
 
-    titleConfig.writeEntry("background_color", kcolorbutton->color());
-    titleConfig.writeEntry("background_alpha", horizontalSlider->value());
+    titleConfig.writeEntry("background_color", backgroundColor->color());
+    titleConfig.writeEntry("background_alpha", backgroundAlpha->value());
 
     //! \todo Not sure if I should sync - it is probably safe to do it
     config->sync();
@@ -1681,6 +1914,11 @@ void TitleWidget::readChoices()
     m_scene->slotUpdateFontSize(font_size->value());
     fontColorButton->setColor(titleConfig.readEntry("font_color", fontColorButton->color()));
     textAlpha->setValue(titleConfig.readEntry("font_alpha", textAlpha->value()));
+
+    textOutlineColor->setColor(titleConfig.readEntry("font_outline_color", textOutlineColor->color()));
+    textOutlineAlpha->setValue(titleConfig.readEntry("font_outline_alpha", textOutlineAlpha->value()));
+    textOutline->setValue(titleConfig.readEntry("font_outline", textOutline->value()));
+
     int weight;
     if (titleConfig.readEntry("font_bold", false)) weight = QFont::Bold;
     else weight = titleConfig.readEntry("font_weight", font_weight_box->itemData(font_weight_box->currentIndex()).toInt());
@@ -1694,8 +1932,8 @@ void TitleWidget::readChoices()
     rectBAlpha->setValue(titleConfig.readEntry("rect_background_alpha", rectBAlpha->value()));
     rectLineWidth->setValue(titleConfig.readEntry("rect_line_width", rectLineWidth->value()));
 
-    kcolorbutton->setColor(titleConfig.readEntry("background_color", kcolorbutton->color()));
-    horizontalSlider->setValue(titleConfig.readEntry("background_alpha", horizontalSlider->value()));
+    backgroundColor->setColor(titleConfig.readEntry("background_color", backgroundColor->color()));
+    backgroundAlpha->setValue(titleConfig.readEntry("background_alpha", backgroundAlpha->value()));
 }
 
 void TitleWidget::adjustFrameSize()
@@ -1723,7 +1961,9 @@ void TitleWidget::slotAnimStart(bool anim)
     }
     align_box->setEnabled(anim);
     itemzoom->setEnabled(!anim);
-    itemrotate->setEnabled(!anim);
+    itemrotatex->setEnabled(!anim);
+    itemrotatey->setEnabled(!anim);
+    itemrotatez->setEnabled(!anim);
     frame_toolbar->setEnabled(!anim);
     toolbar_stack->setEnabled(!anim);
     if (anim) {
@@ -1763,7 +2003,9 @@ void TitleWidget::slotAnimEnd(bool anim)
     }
     align_box->setEnabled(anim);
     itemzoom->setEnabled(!anim);
-    itemrotate->setEnabled(!anim);
+    itemrotatex->setEnabled(!anim);
+    itemrotatey->setEnabled(!anim);
+    itemrotatez->setEnabled(!anim);
     frame_toolbar->setEnabled(!anim);
     toolbar_stack->setEnabled(!anim);
     if (anim) {
@@ -1921,7 +2163,7 @@ void TitleWidget::slotFontText(const QString& s)
     // in older Qt versions.
 }
 
-void TitleWidget::slotEditTypewriter(int ix)
+void TitleWidget::slotEditTypewriter(int /*ix*/)
 {
     QList<QGraphicsItem*> l = graphicsView->scene()->selectedItems();
     if (l.size() == 1) {
@@ -1961,12 +2203,18 @@ void TitleWidget::slotEditShadow()
 #endif
 }
 
-qreal TitleWidget::zIndexBounds(bool maxBound)
+qreal TitleWidget::zIndexBounds(bool maxBound, bool intersectingOnly)
 {
     qreal bound = maxBound ? -99 : 99;
     QList<QGraphicsItem*> l = graphicsView->scene()->selectedItems();
     if (l.size() > 0) {
-        QList<QGraphicsItem*> lItems = graphicsView->scene()->items(l[0]->sceneBoundingRect(), Qt::IntersectsItemShape);
+        QList<QGraphicsItem*> lItems;
+        // Get items (all or intersecting only)
+        if (intersectingOnly) {
+            lItems = graphicsView->scene()->items(l[0]->sceneBoundingRect(), Qt::IntersectsItemShape);
+        } else {
+            lItems = graphicsView->scene()->items();
+        }
         if (lItems.size() > 0) {
             int n = lItems.size();
             qreal z;
@@ -1997,7 +2245,7 @@ void TitleWidget::slotZIndexUp()
     QList<QGraphicsItem*> l = graphicsView->scene()->selectedItems();
     if (l.size() >= 1) {
         qreal currentZ = l[0]->zValue();
-        qreal max = zIndexBounds(true);
+        qreal max = zIndexBounds(true, true);
         if (currentZ <= max) {
             l[0]->setZValue(currentZ + 1);
             updateDimension(l[0]);
@@ -2010,7 +2258,7 @@ void TitleWidget::slotZIndexTop()
     QList<QGraphicsItem*> l = graphicsView->scene()->selectedItems();
     if (l.size() >= 1) {
         qreal currentZ = l[0]->zValue();
-        qreal max = zIndexBounds(true);
+        qreal max = zIndexBounds(true, false);
         if (currentZ <= max) {
             l[0]->setZValue(max + 1);
             updateDimension(l[0]);
@@ -2023,7 +2271,7 @@ void TitleWidget::slotZIndexDown()
     QList<QGraphicsItem*> l = graphicsView->scene()->selectedItems();
     if (l.size() >= 1) {
         qreal currentZ = l[0]->zValue();
-        qreal min = zIndexBounds(false);
+        qreal min = zIndexBounds(false, true);
         if (currentZ >= min) {
             l[0]->setZValue(currentZ - 1);
             updateDimension(l[0]);
@@ -2036,7 +2284,7 @@ void TitleWidget::slotZIndexBottom()
     QList<QGraphicsItem*> l = graphicsView->scene()->selectedItems();
     if (l.size() >= 1) {
         qreal currentZ = l[0]->zValue();
-        qreal min = zIndexBounds(false);
+        qreal min = zIndexBounds(false, false);
         if (currentZ >= min) {
             l[0]->setZValue(min - 1);
             updateDimension(l[0]);