titlewidget.cpp 106 KB
Newer Older
1
/***************************************************************************
2
                          titlewidget.cpp  -  description
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
                             -------------------
    begin                : Feb 28 2008
    copyright            : (C) 2008 by Marco Gittler
    email                : g.marco@freenet.de
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

18
#include "titlewidget.h"
19
#include "gradientwidget.h"
20
#include "kdenlivesettings.h"
Vincent Pinon's avatar
Vincent Pinon committed
21
#include "doc/kthumb.h"
22
#include "KoSliderCombo.h"
23
#include "utils/KoIconUtils.h"
Vincent Pinon's avatar
Vincent Pinon committed
24

Simon Eugster's avatar
Simon Eugster committed
25
26
#include <cmath>

27
#include <KMessageBox>
28
#include <klocalizedstring.h>
29
#include <KRecentDirs>
30

31
32
33
#include <QDebug>
#include <QFontDatabase>
#include <QFileDialog>
34
#include <QDomDocument>
Marco Gittler's avatar
Marco Gittler committed
35
#include <QGraphicsItem>
36
#include <QGraphicsSvgItem>
37
#include <QTimer>
38
#include <QToolBar>
39
#include <QSignalMapper>
40
41
#include <QTextBlockFormat>
#include <QTextCursor>
42
#include <QCryptographicHash>
43
#include <QKeyEvent>
44
#include <QImageReader>
45

46
47
48
49
#include <QGraphicsEffect>
#include <QGraphicsBlurEffect>
#include <QGraphicsDropShadowEffect>

50
#include <iostream>
51
#include <QStandardPaths>
52

53
54
static QList<TitleTemplate> titletemplates;

55
// What exactly is this variable good for?
56
57
int settingUp = false;

58
59
60
61
const int IMAGEITEM = 7;
const int RECTITEM = 3;
const int TEXTITEM = 8;

62
63
64
65
66
const int NOEFFECT = 0;
const int BLUREFFECT = 1;
const int SHADOWEFFECT = 2;
const int TYPEWRITEREFFECT = 3;

67
TitleWidget::TitleWidget(const QUrl &url, const Timecode &tc, const QString &projectTitlePath, Render *render, QWidget *parent) :
Laurent Montel's avatar
Laurent Montel committed
68
69
70
71
72
73
74
75
76
    QDialog(parent),
    Ui::TitleWidget_UI(),
    m_startViewport(NULL),
    m_endViewport(NULL),
    m_render(render),
    m_count(0),
    m_unicodeDialog(new UnicodeDialog(UnicodeDialog::InputHex)),
    m_projectTitlePath(projectTitlePath),
    m_tc(tc)
77
{
78
    setupUi(this);
79
    setMinimumSize(200, 200);
80
    setFont(QFontDatabase::systemFont(QFontDatabase::SmallestReadableFont));
81
    frame_properties->setEnabled(false);
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
82
    frame_properties->setFixedHeight(frame_toolbar->height());
83
84
    int size = style()->pixelMetric(QStyle::PM_SmallIconSize);
    QSize iconSize(size, size);
85

86
87
88
89
    rectBColor->setAlphaChannelEnabled(true);
    rectFColor->setAlphaChannelEnabled(true);
    fontColorButton->setAlphaChannelEnabled(true);
    textOutlineColor->setAlphaChannelEnabled(true);
90
    shadowColor->setAlphaChannelEnabled(true);
91

92
93
94
95
96
97
98
99
100
    QButtonGroup *colorGroup = new QButtonGroup(this);
    colorGroup->addButton(gradient_color);
    colorGroup->addButton(plain_color);

    QButtonGroup *alignGroup = new QButtonGroup(this);
    alignGroup->addButton(buttonAlignLeft);
    alignGroup->addButton(buttonAlignCenter);
    alignGroup->addButton(buttonAlignRight);

101
102
    textOutline->setMinimum(0);
    textOutline->setMaximum(200);
103
    //textOutline->setDecimals(0);
104
105
    textOutline->setValue(0);
    textOutline->setToolTip(i18n("Outline width"));
106
107
108

    backgroundAlpha->setMinimum(0);
    backgroundAlpha->setMaximum(255);
109
   //backgroundAlpha->setDecimals(0);
110
111
112
    backgroundAlpha->setValue(0);
    backgroundAlpha->setToolTip(i18n("Background color opacity"));

113
114
    itemrotatex->setMinimum(-360);
    itemrotatex->setMaximum(360);
115
    //itemrotatex->setDecimals(0);
116
117
118
119
120
    itemrotatex->setValue(0);
    itemrotatex->setToolTip(i18n("Rotation around the X axis"));

    itemrotatey->setMinimum(-360);
    itemrotatey->setMaximum(360);
121
    //itemrotatey->setDecimals(0);
122
123
124
125
126
    itemrotatey->setValue(0);
    itemrotatey->setToolTip(i18n("Rotation around the Y axis"));

    itemrotatez->setMinimum(-360);
    itemrotatez->setMaximum(360);
127
    //itemrotatez->setDecimals(0);
128
129
    itemrotatez->setValue(0);
    itemrotatez->setToolTip(i18n("Rotation around the Z axis"));
130
131
132

    rectLineWidth->setMinimum(0);
    rectLineWidth->setMaximum(100);
133
    //rectLineWidth->setDecimals(0);
134
135
136
    rectLineWidth->setValue(0);
    rectLineWidth->setToolTip(i18n("Border width"));

137
    itemzoom->setSuffix(i18n("%"));
138
139
    m_frameWidth = render->renderWidth();
    m_frameHeight = render->renderHeight();
140
    showToolbars(TITLE_SELECT);
141

142
143
    splitter->setStretchFactor(0, 20);

144
    //If project is drop frame, set the input mask as such.
145
    title_duration->setInputMask(m_tc.mask());
146
    title_duration->setText(m_tc.reformatSeparators(KdenliveSettings::title_duration()));
147

Laurent Montel's avatar
Laurent Montel committed
148
    connect(backgroundColor, SIGNAL(changed(QColor)), this, SLOT(slotChangeBackground())) ;
149
    connect(backgroundAlpha, SIGNAL(valueChanged(int)), this, SLOT(slotChangeBackground())) ;
150

151
152
153
154
155
156
    connect(shadowBox, SIGNAL(toggled(bool)), this, SLOT(slotUpdateShadow()));
    connect(shadowColor, SIGNAL(changed(QColor)), this, SLOT(slotUpdateShadow()));
    connect(blur_radius, SIGNAL(valueChanged(int)), this, SLOT(slotUpdateShadow()));
    connect(shadowX, SIGNAL(valueChanged(int)), this, SLOT(slotUpdateShadow()));
    connect(shadowY, SIGNAL(valueChanged(int)), this, SLOT(slotUpdateShadow()));

157
158
159
160
161
    connect(fontColorButton, SIGNAL(changed(QColor)), this, SLOT(slotUpdateText()));
    connect(plain_color, SIGNAL(clicked(bool)), this, SLOT(slotUpdateText()));
    connect(gradient_color, SIGNAL(clicked(bool)), this, SLOT(slotUpdateText()));
    connect(gradients_combo, SIGNAL(currentIndexChanged(int)), this, SLOT(slotUpdateText()));

Laurent Montel's avatar
Laurent Montel committed
162
163
    connect(textOutlineColor, SIGNAL(changed(QColor)), this, SLOT(slotUpdateText())) ;
    connect(font_family, SIGNAL(currentFontChanged(QFont)), this, SLOT(slotUpdateText())) ;
164
165
166
    connect(font_size, SIGNAL(valueChanged(int)), this, SLOT(slotUpdateText()));
    connect(letter_spacing, SIGNAL(valueChanged(int)), this, SLOT(slotUpdateText())) ;
    connect(line_spacing, SIGNAL(valueChanged(int)), this, SLOT(slotUpdateText())) ;
167
    connect(textOutline, SIGNAL(valueChanged(int)), this, SLOT(slotUpdateText()));
Simon Eugster's avatar
Titler:    
Simon Eugster committed
168
    connect(font_weight_box, SIGNAL(currentIndexChanged(int)), this, SLOT(slotUpdateText()));
169

Laurent Montel's avatar
Laurent Montel committed
170
    connect(font_family, SIGNAL(editTextChanged(QString)), this, SLOT(slotFontText(QString)));
171

Laurent Montel's avatar
Laurent Montel committed
172
173
    connect(rectFColor, SIGNAL(changed(QColor)), this, SLOT(rectChanged()));
    connect(rectBColor, SIGNAL(changed(QColor)), this, SLOT(rectChanged()));
174
175
176
    connect(plain_rect, SIGNAL(clicked(bool)), this, SLOT(rectChanged()));
    connect(gradient_rect, SIGNAL(clicked(bool)), this, SLOT(rectChanged()));
    connect(gradients_rect_combo, SIGNAL(currentIndexChanged(int)), this, SLOT(rectChanged()));
177
    connect(rectLineWidth, SIGNAL(valueChanged(int)), this, SLOT(rectChanged()));
178

179
180
    // Fill effects
    effect_list->addItem(i18n("None"), NOEFFECT);
181
    if (render->getMltVersionInfo(QStringLiteral("kdenlivetitle")) > 1.0) {
182
183
184
	// there was a bug in MLT's kdenlivetitle module version 1 that crashed on typewriter effect
	effect_list->addItem(i18n("Typewriter"), TYPEWRITEREFFECT);
    }
185
    effect_list->addItem(i18n("Blur"), BLUREFFECT);
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
186

187

188
189
    connect(zValue, SIGNAL(valueChanged(int)), this, SLOT(zIndexChanged(int)));
    connect(itemzoom, SIGNAL(valueChanged(int)), this, SLOT(itemScaled(int)));
190
191
192
    connect(itemrotatex, SIGNAL(valueChanged(int)), this, SLOT(itemRotateX(int)));
    connect(itemrotatey, SIGNAL(valueChanged(int)), this, SLOT(itemRotateY(int)));
    connect(itemrotatez, SIGNAL(valueChanged(int)), this, SLOT(itemRotateZ(int)));
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
193
194
    connect(itemhcenter, SIGNAL(clicked()), this, SLOT(itemHCenter()));
    connect(itemvcenter, SIGNAL(clicked()), this, SLOT(itemVCenter()));
195
196
197
198
    connect(itemtop, SIGNAL(clicked()), this, SLOT(itemTop()));
    connect(itembottom, SIGNAL(clicked()), this, SLOT(itemBottom()));
    connect(itemleft, SIGNAL(clicked()), this, SLOT(itemLeft()));
    connect(itemright, SIGNAL(clicked()), this, SLOT(itemRight()));
199
    connect(effect_list, SIGNAL(currentIndexChanged(int)), this, SLOT(slotAddEffect(int)));
200
    connect(typewriter_delay, SIGNAL(valueChanged(int)), this, SLOT(slotEditTypewriter(int)));
201
    connect(typewriter_start, SIGNAL(valueChanged(int)), this, SLOT(slotEditTypewriter(int)));
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
202

Alberto Villa's avatar
Alberto Villa committed
203
204
    connect(origin_x_left, SIGNAL(clicked()), this, SLOT(slotOriginXClicked()));
    connect(origin_y_top, SIGNAL(clicked()), this, SLOT(slotOriginYClicked()));
205

206
    connect(render, SIGNAL(frameUpdated(QImage)), this, SLOT(slotGotBackground(QImage)));
Simon Eugster's avatar
Simon Eugster committed
207

208
    // Position and size
209
210
211
    m_signalMapper = new QSignalMapper(this);
    m_signalMapper->setMapping(value_w, ValueWidth);
    m_signalMapper->setMapping(value_h, ValueHeight);
212
213
    m_signalMapper->setMapping(value_x, ValueX);
    m_signalMapper->setMapping(value_y, ValueY);
214
215
    connect(value_w, SIGNAL(valueChanged(int)), m_signalMapper, SLOT(map()));
    connect(value_h, SIGNAL(valueChanged(int)), m_signalMapper, SLOT(map()));
216
217
    connect(value_x, SIGNAL(valueChanged(int)), m_signalMapper, SLOT(map()));
    connect(value_y, SIGNAL(valueChanged(int)), m_signalMapper, SLOT(map()));
218
219
    connect(m_signalMapper, SIGNAL(mapped(int)), this, SLOT(slotValueChanged(int)));

220
221
    connect(buttonFitZoom, SIGNAL(clicked()), this, SLOT(slotAdjustZoom()));
    connect(buttonRealSize, SIGNAL(clicked()), this, SLOT(slotZoomOneToOne()));
222
223
    connect(buttonItalic, SIGNAL(clicked()), this, SLOT(slotUpdateText()));
    connect(buttonUnder, SIGNAL(clicked()), this, SLOT(slotUpdateText()));
224
225
226
    connect(buttonAlignLeft, SIGNAL(clicked()), this, SLOT(slotUpdateText()));
    connect(buttonAlignRight, SIGNAL(clicked()), this, SLOT(slotUpdateText()));
    connect(buttonAlignCenter, SIGNAL(clicked()), this, SLOT(slotUpdateText()));
227
    connect(edit_gradient, SIGNAL(clicked()), this, SLOT(slotEditGradient()));
228
    connect(edit_rect_gradient, SIGNAL(clicked()), this, SLOT(slotEditGradient()));
229
    connect(displayBg, SIGNAL(stateChanged(int)), this, SLOT(displayBackgroundFrame()));
230

Simon Eugster's avatar
Simon Eugster committed
231
232
    connect(m_unicodeDialog, SIGNAL(charSelected(QString)), this, SLOT(slotInsertUnicodeString(QString)));

233
234
    // mbd
    connect(this, SIGNAL(accepted()), this, SLOT(slotAccepted()));
Mads Bondo Dydensborg's avatar
Mads Bondo Dydensborg committed
235

Simon Eugster's avatar
Titler:    
Simon Eugster committed
236
237
238
239
240
241
242
243
244
245
    font_weight_box->blockSignals(true);
    font_weight_box->addItem(i18nc("Font style", "Light"), QFont::Light);
    font_weight_box->addItem(i18nc("Font style", "Normal"), QFont::Normal);
    font_weight_box->addItem(i18nc("Font style", "Demi-Bold"), QFont::DemiBold);
    font_weight_box->addItem(i18nc("Font style", "Bold"), QFont::Bold);
    font_weight_box->addItem(i18nc("Font style", "Black"), QFont::Black);
    font_weight_box->setToolTip(i18n("Font weight"));
    font_weight_box->setCurrentIndex(1);
    font_weight_box->blockSignals(false);

246
247
248
249
250
251
252
    buttonFitZoom->setIconSize(iconSize);
    buttonRealSize->setIconSize(iconSize);
    buttonItalic->setIconSize(iconSize);
    buttonUnder->setIconSize(iconSize);
    buttonAlignCenter->setIconSize(iconSize);
    buttonAlignLeft->setIconSize(iconSize);
    buttonAlignRight->setIconSize(iconSize);
253
254
255
256
257
258
259
    buttonFitZoom->setIcon(KoIconUtils::themedIcon(QStringLiteral("zoom-fit-best")));
    buttonRealSize->setIcon(KoIconUtils::themedIcon(QStringLiteral("zoom-original")));
    buttonItalic->setIcon(KoIconUtils::themedIcon(QStringLiteral("format-text-italic")));
    buttonUnder->setIcon(KoIconUtils::themedIcon(QStringLiteral("format-text-underline")));
    buttonAlignCenter->setIcon(KoIconUtils::themedIcon(QStringLiteral("format-justify-center")));
    buttonAlignLeft->setIcon(KoIconUtils::themedIcon(QStringLiteral("format-justify-left")));
    buttonAlignRight->setIcon(KoIconUtils::themedIcon(QStringLiteral("format-justify-right")));
260
261
    edit_gradient->setIcon(KoIconUtils::themedIcon(QStringLiteral("list-add")));
    edit_rect_gradient->setIcon(KoIconUtils::themedIcon(QStringLiteral("list-add")));
262

263
264
265
    buttonAlignRight->setToolTip(i18n("Align right"));
    buttonAlignLeft->setToolTip(i18n("Align left"));
    buttonAlignCenter->setToolTip(i18n("Align center"));
266

267
    m_unicodeAction = new QAction(KoIconUtils::themedIcon(QStringLiteral("kdenlive-insert-unicode")), QString(), this);
268
    m_unicodeAction->setShortcut(Qt::SHIFT + Qt::CTRL + Qt::Key_U);
269
    m_unicodeAction->setToolTip(getTooltipWithShortcut(i18n("Insert Unicode character"), m_unicodeAction));
270
271
    connect(m_unicodeAction, SIGNAL(triggered()), this, SLOT(slotInsertUnicode()));
    buttonInsertUnicode->setDefaultAction(m_unicodeAction);
272

273
    m_zUp = new QAction(KoIconUtils::themedIcon(QStringLiteral("kdenlive-zindex-up")), QString(), this);
Simon Eugster's avatar
Simon Eugster committed
274
275
276
277
    m_zUp->setShortcut(Qt::Key_PageUp);
    m_zUp->setToolTip(i18n("Raise object"));
    connect(m_zUp, SIGNAL(triggered()), this, SLOT(slotZIndexUp()));
    zUp->setDefaultAction(m_zUp);
278

279
    m_zDown = new QAction(KoIconUtils::themedIcon(QStringLiteral("kdenlive-zindex-down")), QString(), this);
Simon Eugster's avatar
Simon Eugster committed
280
281
282
283
    m_zDown->setShortcut(Qt::Key_PageDown);
    m_zDown->setToolTip(i18n("Lower object"));
    connect(m_zDown, SIGNAL(triggered()), this, SLOT(slotZIndexDown()));
    zDown->setDefaultAction(m_zDown);
284

285
    m_zTop = new QAction(KoIconUtils::themedIcon(QStringLiteral("kdenlive-zindex-top")), QString(), this);
Alberto Villa's avatar
Alberto Villa committed
286
    // TODO mbt 1414: Shortcut should change z index only if
287
288
    // cursor is NOT in a text field ...
    //m_zTop->setShortcut(Qt::Key_Home);
Simon Eugster's avatar
Simon Eugster committed
289
290
291
    m_zTop->setToolTip(i18n("Raise object to top"));
    connect(m_zTop, SIGNAL(triggered()), this, SLOT(slotZIndexTop()));
    zTop->setDefaultAction(m_zTop);
292

293
    m_zBottom = new QAction(KoIconUtils::themedIcon(QStringLiteral("kdenlive-zindex-bottom")), QString(), this);
294
295
    // TODO mbt 1414
    //m_zBottom->setShortcut(Qt::Key_End);
Simon Eugster's avatar
Simon Eugster committed
296
297
298
    m_zBottom->setToolTip(i18n("Lower object to bottom"));
    connect(m_zBottom, SIGNAL(triggered()), this, SLOT(slotZIndexBottom()));
    zBottom->setDefaultAction(m_zBottom);
299

300
    m_selectAll = new QAction(KoIconUtils::themedIcon(QStringLiteral("kdenlive-select-all")), QString(), this);
301
302
303
    m_selectAll->setShortcut(Qt::CTRL + Qt::Key_A);
    connect(m_selectAll, SIGNAL(triggered()), this, SLOT(slotSelectAll()));
    buttonSelectAll->setDefaultAction(m_selectAll);
304

305
    m_selectText = new QAction(KoIconUtils::themedIcon(QStringLiteral("kdenlive-select-texts")), QString(), this);
306
307
308
    m_selectText->setShortcut(Qt::CTRL + Qt::Key_T);
    connect(m_selectText, SIGNAL(triggered()), this, SLOT(slotSelectText()));
    buttonSelectText->setDefaultAction(m_selectText);
309
310
    buttonSelectText->setEnabled(false);

311
    m_selectRects = new QAction(KoIconUtils::themedIcon(QStringLiteral("kdenlive-select-rects")), QString(), this);
312
313
314
    m_selectRects->setShortcut(Qt::CTRL + Qt::Key_R);
    connect(m_selectRects, SIGNAL(triggered()), this, SLOT(slotSelectRects()));
    buttonSelectRects->setDefaultAction(m_selectRects);
315
316
    buttonSelectRects->setEnabled(false);

317
    m_selectImages = new QAction(KoIconUtils::themedIcon(QStringLiteral("kdenlive-select-images")), QString(), this);
318
319
320
    m_selectImages->setShortcut(Qt::CTRL + Qt::Key_I);
    connect(m_selectImages, SIGNAL(triggered()), this, SLOT(slotSelectImages()));
    buttonSelectImages->setDefaultAction(m_selectImages);
321
322
    buttonSelectImages->setEnabled(false);

323
    m_unselectAll = new QAction(KoIconUtils::themedIcon(QStringLiteral("kdenlive-unselect-all")), QString(), this);
324
325
326
    m_unselectAll->setShortcut(Qt::SHIFT + Qt::CTRL + Qt::Key_A);
    connect(m_unselectAll, SIGNAL(triggered()), this, SLOT(slotSelectNone()));
    buttonUnselectAll->setDefaultAction(m_unselectAll);
327
    buttonUnselectAll->setEnabled(false);
328

329
330
331
    zDown->setIconSize(iconSize);
    zTop->setIconSize(iconSize);
    zBottom->setIconSize(iconSize);
332
333
334
    zDown->setIcon(KoIconUtils::themedIcon(QStringLiteral("kdenlive-zindex-down")));
    zTop->setIcon(KoIconUtils::themedIcon(QStringLiteral("kdenlive-zindex-top")));
    zBottom->setIcon(KoIconUtils::themedIcon(QStringLiteral("kdenlive-zindex-bottom")));
Simon Eugster's avatar
Simon Eugster committed
335
336
337
    connect(zDown, SIGNAL(clicked()), this, SLOT(slotZIndexDown()));
    connect(zTop, SIGNAL(clicked()), this, SLOT(slotZIndexTop()));
    connect(zBottom, SIGNAL(clicked()), this, SLOT(slotZIndexBottom()));
338

Alberto Villa's avatar
Alberto Villa committed
339
340
341
342
343
344
345
    origin_x_left->setToolTip(i18n("Invert x axis and change 0 point"));
    origin_y_top->setToolTip(i18n("Invert y axis and change 0 point"));
    rectBColor->setToolTip(i18n("Select fill color"));
    rectFColor->setToolTip(i18n("Select border color"));
    zoom_slider->setToolTip(i18n("Zoom"));
    buttonRealSize->setToolTip(i18n("Original size (1:1)"));
    buttonFitZoom->setToolTip(i18n("Fit zoom"));
346
    backgroundColor->setToolTip(i18n("Select background color"));
Simon Eugster's avatar
Simon Eugster committed
347
    backgroundAlpha->setToolTip(i18n("Background opacity"));
348
349
350
351
352
    buttonSelectAll->setToolTip(getTooltipWithShortcut(i18n("Select all"), m_selectAll));
    buttonSelectText->setToolTip(getTooltipWithShortcut(i18n("Select text items in current selection"), m_selectText));
    buttonSelectRects->setToolTip(getTooltipWithShortcut(i18n("Select rect items in current selection"), m_selectRects));
    buttonSelectImages->setToolTip(getTooltipWithShortcut(i18n("Select image items in current selection"), m_selectImages));
    buttonUnselectAll->setToolTip(getTooltipWithShortcut(i18n("Unselect all"), m_unselectAll));
353

354
355
356
357
358
359
360
    itemhcenter->setIconSize(iconSize);
    itemvcenter->setIconSize(iconSize);
    itemtop->setIconSize(iconSize);
    itembottom->setIconSize(iconSize);
    itemright->setIconSize(iconSize);
    itemleft->setIconSize(iconSize);
    
361
    itemhcenter->setIcon(KoIconUtils::themedIcon(QStringLiteral("kdenlive-align-hor")));
362
    itemhcenter->setToolTip(i18n("Align item horizontally"));
363
    itemvcenter->setIcon(KoIconUtils::themedIcon(QStringLiteral("kdenlive-align-vert")));
364
    itemvcenter->setToolTip(i18n("Align item vertically"));
365
    itemtop->setIcon(KoIconUtils::themedIcon(QStringLiteral("kdenlive-align-top")));
366
    itemtop->setToolTip(i18n("Align item to top"));
367
    itembottom->setIcon(KoIconUtils::themedIcon(QStringLiteral("kdenlive-align-bottom")));
368
    itembottom->setToolTip(i18n("Align item to bottom"));
369
    itemright->setIcon(KoIconUtils::themedIcon(QStringLiteral("kdenlive-align-right")));
370
    itemright->setToolTip(i18n("Align item to right"));
371
    itemleft->setIcon(KoIconUtils::themedIcon(QStringLiteral("kdenlive-align-left")));
372
    itemleft->setToolTip(i18n("Align item to left"));
373

Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
374

375
376
    QHBoxLayout *layout = new QHBoxLayout;
    frame_toolbar->setLayout(layout);
377
    layout->setContentsMargins(0, 0, 0, 0);
378
    QToolBar *m_toolbar = new QToolBar(QStringLiteral("titleToolBar"), this);
379
    m_toolbar->setIconSize(iconSize);
380

381
    m_buttonCursor = m_toolbar->addAction(KoIconUtils::themedIcon(QStringLiteral("transform-move")), i18n("Selection Tool"));
382
383
    m_buttonCursor->setCheckable(true);
    m_buttonCursor->setShortcut(Qt::ALT + Qt::Key_S);
384
    m_buttonCursor->setToolTip(i18n("Selection Tool") + ' ' + m_buttonCursor->shortcut().toString());
385
    connect(m_buttonCursor, SIGNAL(triggered()), this, SLOT(slotSelectTool()));
386

387
    m_buttonText = m_toolbar->addAction(KoIconUtils::themedIcon(QStringLiteral("insert-text")), i18n("Add Text"));
388
    m_buttonText->setCheckable(true);
Alberto Villa's avatar
Alberto Villa committed
389
    m_buttonText->setShortcut(Qt::ALT + Qt::Key_T);
390
    m_buttonText->setToolTip(i18n("Add Text") + ' ' + m_buttonText->shortcut().toString());
391
    connect(m_buttonText, SIGNAL(triggered()), this, SLOT(slotTextTool()));
392

393
    m_buttonRect = m_toolbar->addAction(KoIconUtils::themedIcon(QStringLiteral("kdenlive-insert-rect")), i18n("Add Rectangle"));
394
395
    m_buttonRect->setCheckable(true);
    m_buttonRect->setShortcut(Qt::ALT + Qt::Key_R);
396
    m_buttonRect->setToolTip(i18n("Add Rectangle") + ' ' + m_buttonRect->shortcut().toString());
397
398
    connect(m_buttonRect, SIGNAL(triggered()), this, SLOT(slotRectTool()));

399
    m_buttonImage = m_toolbar->addAction(KoIconUtils::themedIcon(QStringLiteral("insert-image")), i18n("Add Image"));
400
    m_buttonImage->setCheckable(false);
Alberto Villa's avatar
Alberto Villa committed
401
    m_buttonImage->setShortcut(Qt::ALT + Qt::Key_I);
402
    m_buttonImage->setToolTip(i18n("Add Image") + ' ' + m_buttonImage->shortcut().toString());
403
    connect(m_buttonImage, SIGNAL(triggered()), this, SLOT(slotImageTool()));
404

Simon Eugster's avatar
Simon Eugster committed
405
    m_toolbar->addSeparator();
406

407
    m_buttonLoad = m_toolbar->addAction(KoIconUtils::themedIcon(QStringLiteral("document-open")), i18n("Open Document"));
408
    m_buttonLoad->setCheckable(false);
Alberto Villa's avatar
Alberto Villa committed
409
    m_buttonLoad->setShortcut(Qt::CTRL + Qt::Key_O);
410
    m_buttonLoad->setToolTip(i18n("Open Document") + ' ' + m_buttonLoad->shortcut().toString());
411
412
    connect(m_buttonLoad, SIGNAL(triggered()), this, SLOT(loadTitle()));

413
    m_buttonSave = m_toolbar->addAction(KoIconUtils::themedIcon(QStringLiteral("document-save-as")), i18n("Save As"));
414
    m_buttonSave->setCheckable(false);
Alberto Villa's avatar
Alberto Villa committed
415
    m_buttonSave->setShortcut(Qt::CTRL + Qt::Key_S);
416
    m_buttonSave->setToolTip(i18n("Save As") + ' ' + m_buttonSave->shortcut().toString());
417
418
419
    connect(m_buttonSave, SIGNAL(triggered()), this, SLOT(saveTitle()));

    layout->addWidget(m_toolbar);
420

421
    // initialize graphic scene
422
    m_scene = new GraphicsSceneRectMove(this);
423
    graphicsView->setScene(m_scene);
424
    graphicsView->setMouseTracking(true);
425
426
    graphicsView->setDragMode(QGraphicsView::RubberBandDrag);
    graphicsView->setRubberBandSelectionMode(Qt::ContainsItemBoundingRect);
427
    m_titledocument.setScene(m_scene, m_frameWidth, m_frameHeight);
Alberto Villa's avatar
Alberto Villa committed
428
    connect(m_scene, SIGNAL(changed(QList<QRectF>)), this, SLOT(slotChanged()));
429
    connect(font_size, SIGNAL(valueChanged(int)), m_scene, SLOT(slotUpdateFontSize(int)));
430

431
432
433
434
435
436
437
438
439
    QPen framepen(Qt::DotLine);
    framepen.setColor(Qt::red);

    m_frameBorder = new QGraphicsRectItem(QRectF(0, 0, m_frameWidth, m_frameHeight));
    m_frameBorder->setPen(framepen);
    m_frameBorder->setZValue(-1100);
    m_frameBorder->setBrush(Qt::transparent);
    m_frameBorder->setFlags(0);
    graphicsView->scene()->addItem(m_frameBorder);
440

441
442
443
444
445
    m_frameImage = new QGraphicsPixmapItem();
    QTransform qtrans;
    qtrans.scale(2.0, 2.0);
    m_frameImage->setTransform(qtrans);
    m_frameImage->setZValue(-1200);
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
446
    m_frameImage->setFlags(0);
447
448
449
    displayBackgroundFrame();
    graphicsView->scene()->addItem(m_frameImage);

450
    connect(m_scene, SIGNAL(selectionChanged()), this , SLOT(selectionChanged()));
451
452
    connect(m_scene, SIGNAL(itemMoved()), this , SLOT(selectionChanged()));
    connect(m_scene, SIGNAL(sceneZoom(bool)), this , SLOT(slotZoom(bool)));
453
    connect(m_scene, SIGNAL(actionFinished()), this , SLOT(slotSelectTool()));
Laurent Montel's avatar
Laurent Montel committed
454
    connect(m_scene, SIGNAL(newRect(QGraphicsRectItem*)), this , SLOT(slotNewRect(QGraphicsRectItem*)));
455
    connect(m_scene, SIGNAL(newText(MyTextItem*)), this , SLOT(slotNewText(MyTextItem*)));
456
    connect(zoom_slider, SIGNAL(valueChanged(int)), this , SLOT(slotUpdateZoom(int)));
457
    connect(zoom_spin, SIGNAL(valueChanged(int)), this, SLOT(slotUpdateZoom(int)));
458

459
    // mbd: load saved settings
460
    loadGradients();
461
    readChoices();
Mads Bondo Dydensborg's avatar
Mads Bondo Dydensborg committed
462

463
464
465
    // Hide effects not implemented
    tabWidget->removeTab(3);

466
467
    graphicsView->show();
    graphicsView->setInteractive(true);
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
468
    //qDebug() << "// TITLE WIDGWT: " << graphicsView->viewport()->width() << 'x' << graphicsView->viewport()->height();
469
    m_startViewport = new QGraphicsRectItem(QRectF(0, 0, m_frameWidth, m_frameHeight));
470
471
    // Setting data at -1 so that the item is recognized as undeletable by graphicsscenerectmove
    m_startViewport->setData(-1, -1);
472
    m_endViewport = new QGraphicsRectItem(QRectF(0, 0, m_frameWidth, m_frameHeight));
473
    m_endViewport->setData(-1, -1);
474
475
476
477
    m_startViewport->setData(0, m_frameWidth);
    m_startViewport->setData(1, m_frameHeight);
    m_endViewport->setData(0, m_frameWidth);
    m_endViewport->setData(1, m_frameHeight);
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
478

479
480
    // scale the view so that the title widget is not too big at startup
    graphicsView->scale(.5, .5);
481
    if (url.isValid()) {
Laurent Montel's avatar
Laurent Montel committed
482
483
        loadTitle(url);
    } else {
Laurent Montel's avatar
Laurent Montel committed
484
485
        prepareTools(NULL);
        slotTextTool();
486
        QTimer::singleShot(200, this, SLOT(slotAdjustZoom()));
487
    }
488
489
490
    initAnimation();
    connect(anim_start, SIGNAL(toggled(bool)), this, SLOT(slotAnimStart(bool)));
    connect(anim_end, SIGNAL(toggled(bool)), this, SLOT(slotAnimEnd(bool)));
491
    connect(templateBox, SIGNAL(currentIndexChanged(int)), this, SLOT(templateIndexChanged(int)));
492
493

    buttonBox->button(QDialogButtonBox::Ok)->setEnabled(KdenliveSettings::hastitleproducer());
494
495
496
    if (titletemplates.isEmpty()) {
        refreshTitleTemplates(m_projectTitlePath);
    }
497
498
    //templateBox->setIconSize(QSize(60,60));
    templateBox->clear();
499
    templateBox->addItem(QLatin1String(""));
500
    foreach(const TitleTemplate &t, titletemplates) {
501
502
        templateBox->addItem(t.icon, t.name, t.file);
    }
503
    lastDocumentHash = QCryptographicHash::hash(xml().toString().toLatin1(), QCryptographicHash::Md5).toHex();
Marco Gittler's avatar
Marco Gittler committed
504
}
505

506
507
TitleWidget::~TitleWidget()
{
508
    m_scene->blockSignals(true);
509
510
511
512
513
514
    delete m_buttonRect;
    delete m_buttonText;
    delete m_buttonImage;
    delete m_buttonCursor;
    delete m_buttonSave;
    delete m_buttonLoad;
515
    delete m_unicodeAction;
Simon Eugster's avatar
Simon Eugster committed
516
517
518
519
    delete m_zUp;
    delete m_zDown;
    delete m_zTop;
    delete m_zBottom;
520
521
522
523
524
    delete m_selectAll;
    delete m_selectText;
    delete m_selectRects;
    delete m_selectImages;
    delete m_unselectAll;
525

Alberto Villa's avatar
Alberto Villa committed
526
    delete m_unicodeDialog;
527
528
    delete m_frameBorder;
    delete m_frameImage;
529
530
    delete m_startViewport;
    delete m_endViewport;
531
    delete m_scene;
532
    delete m_signalMapper;
533
534
}

535
// static
Laurent Montel's avatar
Laurent Montel committed
536
QStringList TitleWidget::extractImageList(const QString& xml)
537
538
{
    QStringList result;
539
    if (xml.isEmpty()) return result;
540
541
    QDomDocument doc;
    doc.setContent(xml);
542
    QDomNodeList images = doc.elementsByTagName(QStringLiteral("content"));
543
    for (int i = 0; i < images.count(); ++i) {
544
545
        if (images.at(i).toElement().hasAttribute(QStringLiteral("url")))
            result.append(images.at(i).toElement().attribute(QStringLiteral("url")));
546
547
548
549
    }
    return result;
}

550
// static
Laurent Montel's avatar
Laurent Montel committed
551
QStringList TitleWidget::extractFontList(const QString& xml)
552
553
{
    QStringList result;
554
    if (xml.isEmpty()) return result;
555
556
    QDomDocument doc;
    doc.setContent(xml);
557
    QDomNodeList images = doc.elementsByTagName(QStringLiteral("content"));
558
    for (int i = 0; i < images.count(); ++i) {
559
560
        if (images.at(i).toElement().hasAttribute(QStringLiteral("font")))
            result.append(images.at(i).toElement().attribute(QStringLiteral("font")));
561
562
563
    }
    return result;
}
564
//static
565
void TitleWidget::refreshTitleTemplates(const QString &projectPath)
566
{
567
    QStringList filters = QStringList() << QStringLiteral("*.kdenlivetitle") ;
568
    titletemplates.clear();
569
570
571
572
573
574
575
576
577
578
579
580
581
582

    // project templates
    QDir dir(projectPath);
    QStringList templateFiles = dir.entryList(filters, QDir::Files);
    foreach(const QString & fname, templateFiles) {
        TitleTemplate t;
            t.name = fname;
            t.file = dir.absoluteFilePath(fname);
            t.icon = QIcon(KThumb::getImage(QUrl::fromLocalFile(t.file), 0, 60, 60));
            titletemplates.append(t);
    }

    // system templates
    QStringList titleTemplates = QStandardPaths::locateAll(QStandardPaths::DataLocation, QStringLiteral("titles/"), QStandardPaths::LocateDirectory);
583
    foreach(const QString & folderpath, titleTemplates) {
584
        QDir folder(folderpath);
585
        QStringList filesnames = folder.entryList(filters, QDir::Files);
586
        foreach(const QString & fname, filesnames) {
587
588
            TitleTemplate t;
            t.name = fname;
589
590
            t.file = folder.absoluteFilePath(fname);
            t.icon = QIcon(KThumb::getImage(QUrl::fromLocalFile(t.file), 0, 60, 60));
591
            titletemplates.append(t);
592
593
594
        }
    }
}
Laurent Montel's avatar
Laurent Montel committed
595

596
void TitleWidget::templateIndexChanged(int index)
597
{
598
    QString item = templateBox->itemData(index).toString();
599
    if (!item.isEmpty()) {
600
        if (lastDocumentHash != QCryptographicHash::hash(xml().toString().toLatin1(), QCryptographicHash::Md5).toHex()) {
Laurent Montel's avatar
Laurent Montel committed
601
602
            if (KMessageBox::questionYesNo(this, i18n("Do you really want to load a new template? Changes in this title will be lost!")) == KMessageBox::No)
                return;
603
        }
604
        loadTitle(QUrl::fromLocalFile(item));
605
606
607
608
609

        // mbt 1607: Add property to distinguish between unchanged template titles and user titles.
        // Text of unchanged template titles should be selected when clicked.
        QList<QGraphicsItem *> list = graphicsView->scene()->items();
        foreach(QGraphicsItem * qgItem, list) {
610
            if (qgItem->type() == TEXTITEM) {
611
                MyTextItem *i = static_cast<MyTextItem *>(qgItem);
612
613
614
615
616
                i->setProperty("isTemplate", "true");
                i->setProperty("templateText", i->toHtml());
            }

        }
617
        lastDocumentHash = QCryptographicHash::hash(xml().toString().toLatin1(), QCryptographicHash::Md5).toHex();
618
    }
619
}
620
//virtual
621
622
void TitleWidget::resizeEvent(QResizeEvent * /*event*/)
{
623
    //slotAdjustZoom();
624
}
625
//virtual
Laurent Montel's avatar
Laurent Montel committed
626
627
void TitleWidget::keyPressEvent(QKeyEvent *e)
{
628
629
    if(e->key()!=Qt::Key_Escape && e->key()!=Qt::Key_Return && e->key()!=Qt::Key_Enter) QDialog::keyPressEvent(e);
}
Laurent Montel's avatar
Laurent Montel committed
630

631
632
void TitleWidget::slotTextTool()
{
633
    m_scene->setTool(TITLE_TEXT);
Simon Eugster's avatar
Simon Eugster committed
634
    showToolbars(TITLE_TEXT);
Simon Eugster's avatar
Simon Eugster committed
635
    checkButton(TITLE_TEXT);
636
637
}

638
639
void TitleWidget::slotRectTool()
{
640
    m_scene->setTool(TITLE_RECTANGLE);
Simon Eugster's avatar
Simon Eugster committed
641
    showToolbars(TITLE_RECTANGLE);
Simon Eugster's avatar
Simon Eugster committed
642
    checkButton(TITLE_RECTANGLE);
643

644
645
    // Disable dragging mode, would make dragging a rect impossible otherwise ;)
    graphicsView->setDragMode(QGraphicsView::NoDrag);
646
647
}

648
649
void TitleWidget::slotSelectTool()
{
650
    m_scene->setTool(TITLE_SELECT);
651

652
653
    // Enable rubberband selecting mode.
    graphicsView->setDragMode(QGraphicsView::RubberBandDrag);
Simon Eugster's avatar
Simon Eugster committed
654
655
656
657

    // Find out which toolbars need to be shown, depending on selected item
    TITLETOOL t = TITLE_SELECT;
    QList<QGraphicsItem *> l = graphicsView->scene()->selectedItems();
658
659
    if (l.size() > 0) {
        switch (l.at(0)->type()) {
Simon Eugster's avatar
Simon Eugster committed
660
661
662
663
664
665
666
667
668
669
670
        case TEXTITEM:
            t = TITLE_TEXT;
            break;
        case RECTITEM:
            t = TITLE_RECTANGLE;
            break;
        case IMAGEITEM:
            t = TITLE_IMAGE;
            break;
        }
    }
671

Simon Eugster's avatar
Simon Eugster committed
672
    enableToolbars(t);
673
    if (t == TITLE_RECTANGLE && (l.at(0) == m_endViewport || l.at(0) == m_startViewport)) {
674
        //graphicsView->centerOn(l.at(0));
675
        t = TITLE_SELECT;
676
    }
Simon Eugster's avatar
Simon Eugster committed
677
678
    showToolbars(t);

679
    if (l.size() > 0) {
680
681
682
683
684
        updateCoordinates(l.at(0));
        updateDimension(l.at(0));
        updateRotZoom(l.at(0));
    }

Simon Eugster's avatar
Simon Eugster committed
685
    checkButton(TITLE_SELECT);
686
687
}

688
689
void TitleWidget::slotImageTool()
{
690
691
692
693
694
695
696
697
698
699
    QList<QByteArray> supported = QImageReader::supportedImageFormats();
    QStringList mimeTypeFilters;
    QString allExtensions = i18n("All Images") + " (";
    foreach (const QByteArray &mimeType, supported) {
        mimeTypeFilters.append(i18n("%1 Image", QString(mimeType)) + "( *." + QString(mimeType) + ")");
        allExtensions.append("*." + mimeType + " ");
    }
    mimeTypeFilters.sort();
    allExtensions.append(")");
    mimeTypeFilters.prepend(allExtensions);
700
    QString clipFolder = KRecentDirs::dir(QStringLiteral(":KdenliveImageFolder"));
701
702
703
704
705
706
707
    if (clipFolder.isEmpty()) {
        clipFolder = QDir::homePath();
    }
    QFileDialog dialog(this, i18n("Add Image"), clipFolder);
    dialog.setAcceptMode(QFileDialog::AcceptOpen);
    dialog.setNameFilters(mimeTypeFilters);
    if (dialog.exec() != QDialog::Accepted) return;
708
    QUrl url = QUrl::fromLocalFile(dialog.selectedFiles().at(0));
709
    if (url.isValid()) {
710
        KRecentDirs::add(QStringLiteral(":KdenliveImageFolder"), url.adjusted(QUrl::RemoveFilename).path());
711
        if (url.path().endsWith(QLatin1String(".svg"))) {
712
713
714
715
716
            QGraphicsSvgItem *svg = new QGraphicsSvgItem(url.toLocalFile());
            svg->setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable);
            svg->setZValue(m_count++);
            svg->setData(Qt::UserRole, url.path());
            graphicsView->scene()->addItem(svg);
Laurent Montel's avatar
Laurent Montel committed
717
            prepareTools(svg);
718
719
720
721
722
723
724
725
        } else {
            QPixmap pix(url.path());
            QGraphicsPixmapItem *image = new QGraphicsPixmapItem(pix);
            image->setShapeMode(QGraphicsPixmapItem::BoundingRectShape);
            image->setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable);
            image->setData(Qt::UserRole, url.path());
            image->setZValue(m_count++);
            graphicsView->scene()->addItem(image);
Laurent Montel's avatar
Laurent Montel committed
726
            prepareTools(image);
727
728
729
        }
    }
    m_scene->setTool(TITLE_SELECT);
Simon Eugster's avatar
Simon Eugster committed
730
    showToolbars(TITLE_SELECT);
731
    checkButton(TITLE_SELECT);
732
733
}

Simon Eugster's avatar
Simon Eugster committed
734
735
void TitleWidget::showToolbars(TITLETOOL toolType)
{
736
    //toolbar_stack->setEnabled(toolType != TITLE_SELECT);
737
    switch (toolType) {
738
    case TITLE_IMAGE:
739
        toolbar_stack->setCurrentIndex(2);
740
741
742
743
744
        break;
    case TITLE_RECTANGLE:
        toolbar_stack->setCurrentIndex(1);
        break;
    case TITLE_TEXT:
745
746
    default:
        toolbar_stack->setCurrentIndex(0);
747
        break;
748
    }
Simon Eugster's avatar
Simon Eugster committed
749
750
751
752
}

void TitleWidget::enableToolbars(TITLETOOL toolType)
{
Vincent Pinon's avatar
Vincent Pinon committed
753
    // TITLETOOL is defined in effectstack/graphicsscenerectmove.h
754
    bool enable = false;
755
    if (toolType == TITLE_RECTANGLE || toolType == TITLE_IMAGE) enable = true;
756
757
    value_w->setEnabled(enable);
    value_h->setEnabled(enable);
Simon Eugster's avatar
Simon Eugster committed
758
759
}

760
761
void TitleWidget::checkButton(TITLETOOL toolType)
{
Simon Eugster's avatar
Simon Eugster committed
762
763
764
765
766
    bool bSelect = false;
    bool bText = false;
    bool bRect = false;
    bool bImage = false;

767
    switch (toolType) {
Simon Eugster's avatar
Simon Eugster committed
768
769
770
771
772
773
774
775
776
777
778
779
    case TITLE_SELECT:
        bSelect = true;
        break;
    case TITLE_TEXT:
        bText = true;
        break;
    case TITLE_RECTANGLE:
        bRect = true;
        break;
    case TITLE_IMAGE:
        bImage = true;
        break;
780
    default:
Simon Eugster's avatar
Simon Eugster committed
781
782
783
784
785
786
787
        break;
    }

    m_buttonCursor->setChecked(bSelect);
    m_buttonText->setChecked(bText);
    m_buttonRect->setChecked(bRect);
    m_buttonImage->setChecked(bImage);
788
789
}

790
791
void TitleWidget::displayBackgroundFrame()
{
792
    QRectF r = m_frameBorder->sceneBoundingRect();
793
    if (!displayBg->isChecked()) {
794
795
        QPixmap pattern(20, 20);
        pattern.fill();
796
        QColor bgcolor(210, 210, 210);
797
        QPainter p(&pattern);
798
799
        p.fillRect(QRect(0, 0, 10, 10), bgcolor);
        p.fillRect(QRect(10, 10, 20, 20), bgcolor);
800
801
        p.end();
        QBrush br(pattern);
802
803
804
805
        QPixmap bg((int) (r.width() / 2), (int) (r.height()/ 2));
        QPainter p2(&bg);
        p2.fillRect(bg.rect(), br);
        p2.end();
806
807
        m_frameImage->setPixmap(bg);
    } else {
808
        emit requestBackgroundFrame();