titlewidget.cpp 120 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"
Nicolas Carion's avatar
Nicolas Carion committed
19
#include "KoSliderCombo.h"
20
#include "core.h"
Nicolas Carion's avatar
Nicolas Carion committed
21
#include "doc/kthumb.h"
22
#include "gradientwidget.h"
23
#include "kdenlivesettings.h"
24
#include "monitor/monitor.h"
25

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

28
#include <KMessageBox>
29
#include <KMessageWidget>
30
#include <KRecentDirs>
Nicolas Carion's avatar
Nicolas Carion committed
31
#include <klocalizedstring.h>
32
#include <kns3/downloaddialog.h>
33

Laurent Montel's avatar
Laurent Montel committed
34
#include "kdenlive_debug.h"
35
#include <QButtonGroup>
Nicolas Carion's avatar
Nicolas Carion committed
36
#include <QCryptographicHash>
37
#include <QDomDocument>
Nicolas Carion's avatar
Nicolas Carion committed
38 39
#include <QFileDialog>
#include <QFontDatabase>
40 41 42
#include <QGraphicsBlurEffect>
#include <QGraphicsDropShadowEffect>
#include <QGraphicsEffect>
Marco Gittler's avatar
Marco Gittler committed
43
#include <QGraphicsItem>
44
#include <QGraphicsSvgItem>
Nicolas Carion's avatar
Nicolas Carion committed
45 46
#include <QImageReader>
#include <QKeyEvent>
47
#include <QSignalMapper>
48
#include <QSpinBox>
49 50
#include <QTextBlockFormat>
#include <QTextCursor>
Nicolas Carion's avatar
Nicolas Carion committed
51 52
#include <QTimer>
#include <QToolBar>
53

54
#include <QStandardPaths>
Nicolas Carion's avatar
Nicolas Carion committed
55
#include <iostream>
56
#include <mlt++/MltProfile.h>
Nicolas Carion's avatar
Nicolas Carion committed
57
#include <utility>
58

59 60
static QList<TitleTemplate> titletemplates;

61
// What exactly is this variable good for?
Nicolas Carion's avatar
Nicolas Carion committed
62
int settingUp = 0;
63

64 65 66 67
const int IMAGEITEM = 7;
const int RECTITEM = 3;
const int TEXTITEM = 8;

68 69 70 71 72
const int NOEFFECT = 0;
const int BLUREFFECT = 1;
const int SHADOWEFFECT = 2;
const int TYPEWRITEREFFECT = 3;

73 74 75 76 77 78 79 80 81
void TitleWidget::refreshTemplateBoxContents()
{
    templateBox->clear();
    templateBox->addItem(QString());
    for (const TitleTemplate &t : titletemplates) {
        templateBox->addItem(t.icon, t.name, t.file);
    }
}

Nicolas Carion's avatar
Nicolas Carion committed
82
TitleWidget::TitleWidget(const QUrl &url, const Timecode &tc, QString projectTitlePath, Monitor *monitor, QWidget *parent)
83 84 85 86 87 88
    : QDialog(parent)
    , Ui::TitleWidget_UI()
    , m_startViewport(nullptr)
    , m_endViewport(nullptr)
    , m_count(0)
    , m_unicodeDialog(new UnicodeDialog(UnicodeDialog::InputHex))
89
    , m_missingMessage(nullptr)
Nicolas Carion's avatar
Nicolas Carion committed
90
    , m_projectTitlePath(std::move(projectTitlePath))
91
    , m_tc(tc)
92
    , m_fps(monitor->fps())
93
    , m_guides(QList<QGraphicsLineItem *>())
94
{
95
    setupUi(this);
96
    setMinimumSize(200, 200);
97
    setFont(QFontDatabase::systemFont(QFontDatabase::SmallestReadableFont));
98
    frame_properties->setEnabled(false);
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
99
    frame_properties->setFixedHeight(frame_toolbar->height());
100 101
    int size = style()->pixelMetric(QStyle::PM_SmallIconSize);
    QSize iconSize(size, size);
102

103 104 105 106
    rectBColor->setAlphaChannelEnabled(true);
    rectFColor->setAlphaChannelEnabled(true);
    fontColorButton->setAlphaChannelEnabled(true);
    textOutlineColor->setAlphaChannelEnabled(true);
107
    shadowColor->setAlphaChannelEnabled(true);
108

Nicolas Carion's avatar
Nicolas Carion committed
109
    auto *colorGroup = new QButtonGroup(this);
110 111 112
    colorGroup->addButton(gradient_color);
    colorGroup->addButton(plain_color);

Nicolas Carion's avatar
Nicolas Carion committed
113
    auto *alignGroup = new QButtonGroup(this);
114 115 116 117
    alignGroup->addButton(buttonAlignLeft);
    alignGroup->addButton(buttonAlignCenter);
    alignGroup->addButton(buttonAlignRight);

118 119
    textOutline->setMinimum(0);
    textOutline->setMaximum(200);
Nicolas Carion's avatar
Nicolas Carion committed
120
    // textOutline->setDecimals(0);
121 122
    textOutline->setValue(0);
    textOutline->setToolTip(i18n("Outline width"));
123 124 125

    backgroundAlpha->setMinimum(0);
    backgroundAlpha->setMaximum(255);
126 127
    bgAlphaSlider->setMinimum(0);
    bgAlphaSlider->setMaximum(255);
128 129 130
    backgroundAlpha->setValue(0);
    backgroundAlpha->setToolTip(i18n("Background color opacity"));

131 132
    itemrotatex->setMinimum(-360);
    itemrotatex->setMaximum(360);
Nicolas Carion's avatar
Nicolas Carion committed
133
    // itemrotatex->setDecimals(0);
134 135 136 137 138
    itemrotatex->setValue(0);
    itemrotatex->setToolTip(i18n("Rotation around the X axis"));

    itemrotatey->setMinimum(-360);
    itemrotatey->setMaximum(360);
Nicolas Carion's avatar
Nicolas Carion committed
139
    // itemrotatey->setDecimals(0);
140 141 142 143 144
    itemrotatey->setValue(0);
    itemrotatey->setToolTip(i18n("Rotation around the Y axis"));

    itemrotatez->setMinimum(-360);
    itemrotatez->setMaximum(360);
Nicolas Carion's avatar
Nicolas Carion committed
145
    // itemrotatez->setDecimals(0);
146 147
    itemrotatez->setValue(0);
    itemrotatez->setToolTip(i18n("Rotation around the Z axis"));
148 149

    rectLineWidth->setMinimum(0);
150
    rectLineWidth->setMaximum(500);
Nicolas Carion's avatar
Nicolas Carion committed
151
    // rectLineWidth->setDecimals(0);
152 153 154
    rectLineWidth->setValue(0);
    rectLineWidth->setToolTip(i18n("Border width"));

155
    itemzoom->setSuffix(i18n("%"));
156
    QSize profileSize = monitor->profileSize();
157
    m_frameWidth = (int)(profileSize.height() * pCore->getCurrentDar() + 0.5);
158
    m_frameHeight = profileSize.height();
159
    showToolbars(TITLE_SELECT);
160

161 162
    splitter->setStretchFactor(0, 20);

Nicolas Carion's avatar
Nicolas Carion committed
163
    // If project is drop frame, set the input mask as such.
164
    title_duration->setInputMask(m_tc.mask());
165
    title_duration->setText(m_tc.reformatSeparators(KdenliveSettings::title_duration()));
166

Laurent Montel's avatar
Laurent Montel committed
167
    connect(backgroundColor, &KColorButton::changed, this, &TitleWidget::slotChangeBackground);
168
    connect(backgroundAlpha, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, &TitleWidget::slotChangeBackground);
169

Laurent Montel's avatar
Laurent Montel committed
170 171
    connect(shadowBox, &QGroupBox::toggled, this, &TitleWidget::slotUpdateShadow);
    connect(shadowColor, &KColorButton::changed, this, &TitleWidget::slotUpdateShadow);
172 173 174
    connect(blur_radius, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, &TitleWidget::slotUpdateShadow);
    connect(shadowX, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, &TitleWidget::slotUpdateShadow);
    connect(shadowY, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, &TitleWidget::slotUpdateShadow);
175

Laurent Montel's avatar
Laurent Montel committed
176 177 178
    connect(fontColorButton, &KColorButton::changed, this, &TitleWidget::slotUpdateText);
    connect(plain_color, &QAbstractButton::clicked, this, &TitleWidget::slotUpdateText);
    connect(gradient_color, &QAbstractButton::clicked, this, &TitleWidget::slotUpdateText);
179 180
    connect(gradients_combo, SIGNAL(currentIndexChanged(int)), this, SLOT(slotUpdateText()));

Laurent Montel's avatar
Laurent Montel committed
181 182
    connect(textOutlineColor, &KColorButton::changed, this, &TitleWidget::slotUpdateText);
    connect(font_family, &QFontComboBox::currentFontChanged, this, &TitleWidget::slotUpdateText);
183 184 185 186
    connect(font_size, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, &TitleWidget::slotUpdateText);
    connect(letter_spacing, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, &TitleWidget::slotUpdateText);
    connect(line_spacing, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, &TitleWidget::slotUpdateText);
    connect(textOutline, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, &TitleWidget::slotUpdateText);
Simon Eugster's avatar
Titler:  
Simon Eugster committed
187
    connect(font_weight_box, SIGNAL(currentIndexChanged(int)), this, SLOT(slotUpdateText()));
188

Laurent Montel's avatar
Laurent Montel committed
189 190 191 192
    connect(rectFColor, &KColorButton::changed, this, &TitleWidget::rectChanged);
    connect(rectBColor, &KColorButton::changed, this, &TitleWidget::rectChanged);
    connect(plain_rect, &QAbstractButton::clicked, this, &TitleWidget::rectChanged);
    connect(gradient_rect, &QAbstractButton::clicked, this, &TitleWidget::rectChanged);
193
    connect(gradients_rect_combo, SIGNAL(currentIndexChanged(int)), this, SLOT(rectChanged()));
194
    connect(rectLineWidth, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, &TitleWidget::rectChanged);
195

196 197 198 199 200
    connect(zValue, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, &TitleWidget::zIndexChanged);
    connect(itemzoom, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, &TitleWidget::itemScaled);
    connect(itemrotatex, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, &TitleWidget::itemRotateX);
    connect(itemrotatey, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, &TitleWidget::itemRotateY);
    connect(itemrotatez, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, &TitleWidget::itemRotateZ);
Laurent Montel's avatar
Laurent Montel committed
201 202 203 204 205 206
    connect(itemhcenter, &QAbstractButton::clicked, this, &TitleWidget::itemHCenter);
    connect(itemvcenter, &QAbstractButton::clicked, this, &TitleWidget::itemVCenter);
    connect(itemtop, &QAbstractButton::clicked, this, &TitleWidget::itemTop);
    connect(itembottom, &QAbstractButton::clicked, this, &TitleWidget::itemBottom);
    connect(itemleft, &QAbstractButton::clicked, this, &TitleWidget::itemLeft);
    connect(itemright, &QAbstractButton::clicked, this, &TitleWidget::itemRight);
207
    connect(effect_list, SIGNAL(currentIndexChanged(int)), this, SLOT(slotAddEffect(int)));
208 209
    connect(typewriter_delay, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, &TitleWidget::slotEditTypewriter);
    connect(typewriter_start, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, &TitleWidget::slotEditTypewriter);
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
210

Laurent Montel's avatar
Laurent Montel committed
211 212
    connect(origin_x_left, &QAbstractButton::clicked, this, &TitleWidget::slotOriginXClicked);
    connect(origin_y_top, &QAbstractButton::clicked, this, &TitleWidget::slotOriginYClicked);
213

214
    connect(monitor, &Monitor::frameUpdated, this, &TitleWidget::slotGotBackground);
Simon Eugster's avatar
Simon Eugster committed
215

216
    // Position and size
217 218 219
    m_signalMapper = new QSignalMapper(this);
    m_signalMapper->setMapping(value_w, ValueWidth);
    m_signalMapper->setMapping(value_h, ValueHeight);
220 221
    m_signalMapper->setMapping(value_x, ValueX);
    m_signalMapper->setMapping(value_y, ValueY);
Nicolas Carion's avatar
Nicolas Carion committed
222 223 224 225 226 227 228 229
    connect(value_w, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), m_signalMapper,
            static_cast<void (QSignalMapper::*)()>(&QSignalMapper::map));
    connect(value_h, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), m_signalMapper,
            static_cast<void (QSignalMapper::*)()>(&QSignalMapper::map));
    connect(value_x, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), m_signalMapper,
            static_cast<void (QSignalMapper::*)()>(&QSignalMapper::map));
    connect(value_y, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), m_signalMapper,
            static_cast<void (QSignalMapper::*)()>(&QSignalMapper::map));
230 231
    connect(m_signalMapper, SIGNAL(mapped(int)), this, SLOT(slotValueChanged(int)));

Laurent Montel's avatar
Laurent Montel committed
232 233 234 235 236 237 238 239 240 241
    connect(buttonFitZoom, &QAbstractButton::clicked, this, &TitleWidget::slotAdjustZoom);
    connect(buttonRealSize, &QAbstractButton::clicked, this, &TitleWidget::slotZoomOneToOne);
    connect(buttonItalic, &QAbstractButton::clicked, this, &TitleWidget::slotUpdateText);
    connect(buttonUnder, &QAbstractButton::clicked, this, &TitleWidget::slotUpdateText);
    connect(buttonAlignLeft, &QAbstractButton::clicked, this, &TitleWidget::slotUpdateText);
    connect(buttonAlignRight, &QAbstractButton::clicked, this, &TitleWidget::slotUpdateText);
    connect(buttonAlignCenter, &QAbstractButton::clicked, this, &TitleWidget::slotUpdateText);
    connect(edit_gradient, &QAbstractButton::clicked, this, &TitleWidget::slotEditGradient);
    connect(edit_rect_gradient, &QAbstractButton::clicked, this, &TitleWidget::slotEditGradient);
    connect(displayBg, &QCheckBox::stateChanged, this, &TitleWidget::displayBackgroundFrame);
242

Laurent Montel's avatar
Laurent Montel committed
243
    connect(m_unicodeDialog, &UnicodeDialog::charSelected, this, &TitleWidget::slotInsertUnicodeString);
Simon Eugster's avatar
Simon Eugster committed
244

245
    // mbd
Laurent Montel's avatar
Laurent Montel committed
246
    connect(this, &QDialog::accepted, this, &TitleWidget::slotAccepted);
Mads Bondo Dydensborg's avatar
Mads Bondo Dydensborg committed
247

Simon Eugster's avatar
Titler:  
Simon Eugster committed
248 249 250 251 252 253 254 255 256 257
    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);

258 259 260 261 262 263 264
    buttonFitZoom->setIconSize(iconSize);
    buttonRealSize->setIconSize(iconSize);
    buttonItalic->setIconSize(iconSize);
    buttonUnder->setIconSize(iconSize);
    buttonAlignCenter->setIconSize(iconSize);
    buttonAlignLeft->setIconSize(iconSize);
    buttonAlignRight->setIconSize(iconSize);
265 266 267 268 269 270 271 272 273
    buttonFitZoom->setIcon(QIcon::fromTheme(QStringLiteral("zoom-fit-best")));
    buttonRealSize->setIcon(QIcon::fromTheme(QStringLiteral("zoom-original")));
    buttonItalic->setIcon(QIcon::fromTheme(QStringLiteral("format-text-italic")));
    buttonUnder->setIcon(QIcon::fromTheme(QStringLiteral("format-text-underline")));
    buttonAlignCenter->setIcon(QIcon::fromTheme(QStringLiteral("format-justify-center")));
    buttonAlignLeft->setIcon(QIcon::fromTheme(QStringLiteral("format-justify-left")));
    buttonAlignRight->setIcon(QIcon::fromTheme(QStringLiteral("format-justify-right")));
    edit_gradient->setIcon(QIcon::fromTheme(QStringLiteral("document-edit")));
    edit_rect_gradient->setIcon(QIcon::fromTheme(QStringLiteral("document-edit")));
274

275 276 277
    buttonAlignRight->setToolTip(i18n("Align right"));
    buttonAlignLeft->setToolTip(i18n("Align left"));
    buttonAlignCenter->setToolTip(i18n("Align center"));
278
    buttonAlignLeft->setChecked(true);
279

280
    m_unicodeAction = new QAction(QIcon::fromTheme(QStringLiteral("kdenlive-insert-unicode")), QString(), this);
281
    m_unicodeAction->setShortcut(Qt::SHIFT + Qt::CTRL + Qt::Key_U);
282
    m_unicodeAction->setToolTip(getTooltipWithShortcut(i18n("Insert Unicode character"), m_unicodeAction));
Laurent Montel's avatar
Laurent Montel committed
283
    connect(m_unicodeAction, &QAction::triggered, this, &TitleWidget::slotInsertUnicode);
284
    buttonInsertUnicode->setDefaultAction(m_unicodeAction);
285

286
    m_zUp = new QAction(QIcon::fromTheme(QStringLiteral("kdenlive-zindex-up")), QString(), this);
Simon Eugster's avatar
Simon Eugster committed
287 288
    m_zUp->setShortcut(Qt::Key_PageUp);
    m_zUp->setToolTip(i18n("Raise object"));
Laurent Montel's avatar
Laurent Montel committed
289
    connect(m_zUp, &QAction::triggered, this, &TitleWidget::slotZIndexUp);
Simon Eugster's avatar
Simon Eugster committed
290
    zUp->setDefaultAction(m_zUp);
291

292
    m_zDown = new QAction(QIcon::fromTheme(QStringLiteral("kdenlive-zindex-down")), QString(), this);
Simon Eugster's avatar
Simon Eugster committed
293 294
    m_zDown->setShortcut(Qt::Key_PageDown);
    m_zDown->setToolTip(i18n("Lower object"));
Laurent Montel's avatar
Laurent Montel committed
295
    connect(m_zDown, &QAction::triggered, this, &TitleWidget::slotZIndexDown);
Simon Eugster's avatar
Simon Eugster committed
296
    zDown->setDefaultAction(m_zDown);
297

298
    m_zTop = new QAction(QIcon::fromTheme(QStringLiteral("kdenlive-zindex-top")), QString(), this);
Alberto Villa's avatar
Alberto Villa committed
299
    // TODO mbt 1414: Shortcut should change z index only if
300
    // cursor is NOT in a text field ...
Nicolas Carion's avatar
Nicolas Carion committed
301
    // m_zTop->setShortcut(Qt::Key_Home);
Simon Eugster's avatar
Simon Eugster committed
302
    m_zTop->setToolTip(i18n("Raise object to top"));
Laurent Montel's avatar
Laurent Montel committed
303
    connect(m_zTop, &QAction::triggered, this, &TitleWidget::slotZIndexTop);
Simon Eugster's avatar
Simon Eugster committed
304
    zTop->setDefaultAction(m_zTop);
305

306
    m_zBottom = new QAction(QIcon::fromTheme(QStringLiteral("kdenlive-zindex-bottom")), QString(), this);
307
    // TODO mbt 1414
Nicolas Carion's avatar
Nicolas Carion committed
308
    // m_zBottom->setShortcut(Qt::Key_End);
Simon Eugster's avatar
Simon Eugster committed
309
    m_zBottom->setToolTip(i18n("Lower object to bottom"));
Laurent Montel's avatar
Laurent Montel committed
310
    connect(m_zBottom, &QAction::triggered, this, &TitleWidget::slotZIndexBottom);
Simon Eugster's avatar
Simon Eugster committed
311
    zBottom->setDefaultAction(m_zBottom);
312

313
    m_selectAll = new QAction(QIcon::fromTheme(QStringLiteral("kdenlive-select-all")), QString(), this);
314
    m_selectAll->setShortcut(Qt::CTRL + Qt::Key_A);
Laurent Montel's avatar
Laurent Montel committed
315
    connect(m_selectAll, &QAction::triggered, this, &TitleWidget::slotSelectAll);
316
    buttonSelectAll->setDefaultAction(m_selectAll);
317

318
    m_selectText = new QAction(QIcon::fromTheme(QStringLiteral("kdenlive-select-texts")), QString(), this);
319
    m_selectText->setShortcut(Qt::CTRL + Qt::Key_T);
Laurent Montel's avatar
Laurent Montel committed
320
    connect(m_selectText, &QAction::triggered, this, &TitleWidget::slotSelectText);
321
    buttonSelectText->setDefaultAction(m_selectText);
322 323
    buttonSelectText->setEnabled(false);

324
    m_selectRects = new QAction(QIcon::fromTheme(QStringLiteral("kdenlive-select-rects")), QString(), this);
325
    m_selectRects->setShortcut(Qt::CTRL + Qt::Key_R);
Laurent Montel's avatar
Laurent Montel committed
326
    connect(m_selectRects, &QAction::triggered, this, &TitleWidget::slotSelectRects);
327
    buttonSelectRects->setDefaultAction(m_selectRects);
328 329
    buttonSelectRects->setEnabled(false);

330
    m_selectImages = new QAction(QIcon::fromTheme(QStringLiteral("kdenlive-select-images")), QString(), this);
331
    m_selectImages->setShortcut(Qt::CTRL + Qt::Key_I);
Laurent Montel's avatar
Laurent Montel committed
332
    connect(m_selectImages, &QAction::triggered, this, &TitleWidget::slotSelectImages);
333
    buttonSelectImages->setDefaultAction(m_selectImages);
334 335
    buttonSelectImages->setEnabled(false);

336
    m_unselectAll = new QAction(QIcon::fromTheme(QStringLiteral("kdenlive-unselect-all")), QString(), this);
337
    m_unselectAll->setShortcut(Qt::SHIFT + Qt::CTRL + Qt::Key_A);
Laurent Montel's avatar
Laurent Montel committed
338
    connect(m_unselectAll, &QAction::triggered, this, &TitleWidget::slotSelectNone);
339
    buttonUnselectAll->setDefaultAction(m_unselectAll);
340
    buttonUnselectAll->setEnabled(false);
341

342 343 344
    zDown->setIconSize(iconSize);
    zTop->setIconSize(iconSize);
    zBottom->setIconSize(iconSize);
345 346 347
    zDown->setIcon(QIcon::fromTheme(QStringLiteral("kdenlive-zindex-down")));
    zTop->setIcon(QIcon::fromTheme(QStringLiteral("kdenlive-zindex-top")));
    zBottom->setIcon(QIcon::fromTheme(QStringLiteral("kdenlive-zindex-bottom")));
Laurent Montel's avatar
Laurent Montel committed
348 349 350
    connect(zDown, &QAbstractButton::clicked, this, &TitleWidget::slotZIndexDown);
    connect(zTop, &QAbstractButton::clicked, this, &TitleWidget::slotZIndexTop);
    connect(zBottom, &QAbstractButton::clicked, this, &TitleWidget::slotZIndexBottom);
351

Alberto Villa's avatar
Alberto Villa committed
352 353 354 355 356 357 358
    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"));
359
    backgroundColor->setToolTip(i18n("Select background color"));
Simon Eugster's avatar
Simon Eugster committed
360
    backgroundAlpha->setToolTip(i18n("Background opacity"));
361 362 363 364 365
    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));
366

367 368 369 370 371 372
    itemhcenter->setIconSize(iconSize);
    itemvcenter->setIconSize(iconSize);
    itemtop->setIconSize(iconSize);
    itembottom->setIconSize(iconSize);
    itemright->setIconSize(iconSize);
    itemleft->setIconSize(iconSize);
373

374
    itemhcenter->setIcon(QIcon::fromTheme(QStringLiteral("kdenlive-align-hor")));
375
    itemhcenter->setToolTip(i18n("Align item horizontally"));
376
    itemvcenter->setIcon(QIcon::fromTheme(QStringLiteral("kdenlive-align-vert")));
377
    itemvcenter->setToolTip(i18n("Align item vertically"));
378
    itemtop->setIcon(QIcon::fromTheme(QStringLiteral("kdenlive-align-top")));
379
    itemtop->setToolTip(i18n("Align item to top"));
380
    itembottom->setIcon(QIcon::fromTheme(QStringLiteral("kdenlive-align-bottom")));
381
    itembottom->setToolTip(i18n("Align item to bottom"));
382
    itemright->setIcon(QIcon::fromTheme(QStringLiteral("kdenlive-align-right")));
383
    itemright->setToolTip(i18n("Align item to right"));
384
    itemleft->setIcon(QIcon::fromTheme(QStringLiteral("kdenlive-align-left")));
385
    itemleft->setToolTip(i18n("Align item to left"));
386

Nicolas Carion's avatar
Nicolas Carion committed
387
    auto *layout = new QHBoxLayout;
388
    frame_toolbar->setLayout(layout);
389
    layout->setContentsMargins(0, 0, 0, 0);
390
    QToolBar *m_toolbar = new QToolBar(QStringLiteral("titleToolBar"), this);
391
    m_toolbar->setIconSize(iconSize);
392

393
    m_buttonCursor = m_toolbar->addAction(QIcon::fromTheme(QStringLiteral("transform-move")), i18n("Selection Tool"));
394 395
    m_buttonCursor->setCheckable(true);
    m_buttonCursor->setShortcut(Qt::ALT + Qt::Key_S);
Laurent Montel's avatar
Laurent Montel committed
396
    m_buttonCursor->setToolTip(i18n("Selection Tool") + QLatin1Char(' ') + m_buttonCursor->shortcut().toString());
Laurent Montel's avatar
Laurent Montel committed
397
    connect(m_buttonCursor, &QAction::triggered, this, &TitleWidget::slotSelectTool);
398

399
    m_buttonText = m_toolbar->addAction(QIcon::fromTheme(QStringLiteral("insert-text")), i18n("Add Text"));
400
    m_buttonText->setCheckable(true);
Alberto Villa's avatar
Alberto Villa committed
401
    m_buttonText->setShortcut(Qt::ALT + Qt::Key_T);
Laurent Montel's avatar
Laurent Montel committed
402
    m_buttonText->setToolTip(i18n("Add Text") + QLatin1Char(' ') + m_buttonText->shortcut().toString());
Laurent Montel's avatar
Laurent Montel committed
403
    connect(m_buttonText, &QAction::triggered, this, &TitleWidget::slotTextTool);
404

405
    m_buttonRect = m_toolbar->addAction(QIcon::fromTheme(QStringLiteral("kdenlive-insert-rect")), i18n("Add Rectangle"));
406 407
    m_buttonRect->setCheckable(true);
    m_buttonRect->setShortcut(Qt::ALT + Qt::Key_R);
Laurent Montel's avatar
Laurent Montel committed
408
    m_buttonRect->setToolTip(i18n("Add Rectangle") + QLatin1Char(' ') + m_buttonRect->shortcut().toString());
Laurent Montel's avatar
Laurent Montel committed
409
    connect(m_buttonRect, &QAction::triggered, this, &TitleWidget::slotRectTool);
410

411
    m_buttonImage = m_toolbar->addAction(QIcon::fromTheme(QStringLiteral("insert-image")), i18n("Add Image"));
412
    m_buttonImage->setCheckable(false);
Alberto Villa's avatar
Alberto Villa committed
413
    m_buttonImage->setShortcut(Qt::ALT + Qt::Key_I);
Laurent Montel's avatar
Laurent Montel committed
414
    m_buttonImage->setToolTip(i18n("Add Image") + QLatin1Char(' ') + m_buttonImage->shortcut().toString());
Laurent Montel's avatar
Laurent Montel committed
415
    connect(m_buttonImage, &QAction::triggered, this, &TitleWidget::slotImageTool);
416

Simon Eugster's avatar
Simon Eugster committed
417
    m_toolbar->addSeparator();
418

419
    m_buttonLoad = m_toolbar->addAction(QIcon::fromTheme(QStringLiteral("document-open")), i18n("Open Document"));
420
    m_buttonLoad->setCheckable(false);
Alberto Villa's avatar
Alberto Villa committed
421
    m_buttonLoad->setShortcut(Qt::CTRL + Qt::Key_O);
Laurent Montel's avatar
Laurent Montel committed
422
    m_buttonLoad->setToolTip(i18n("Open Document") + QLatin1Char(' ') + m_buttonLoad->shortcut().toString());
423 424
    connect(m_buttonLoad, SIGNAL(triggered()), this, SLOT(loadTitle()));

425
    m_buttonSave = m_toolbar->addAction(QIcon::fromTheme(QStringLiteral("document-save-as")), i18n("Save As"));
426
    m_buttonSave->setCheckable(false);
Alberto Villa's avatar
Alberto Villa committed
427
    m_buttonSave->setShortcut(Qt::CTRL + Qt::Key_S);
Laurent Montel's avatar
Laurent Montel committed
428
    m_buttonSave->setToolTip(i18n("Save As") + QLatin1Char(' ') + m_buttonSave->shortcut().toString());
429 430
    connect(m_buttonSave, SIGNAL(triggered()), this, SLOT(saveTitle()));

431 432 433 434 435 436
    m_buttonDownload = m_toolbar->addAction(QIcon::fromTheme(QStringLiteral("edit-download")), i18n("Download New Title Templates..."));
    m_buttonDownload->setCheckable(false);
    m_buttonDownload->setShortcut(Qt::ALT + Qt::Key_D);
    m_buttonDownload->setToolTip(i18n("Download New Title Templates...") + QLatin1Char(' ') + m_buttonDownload->shortcut().toString());
    connect(m_buttonDownload, &QAction::triggered, this, &TitleWidget::downloadTitleTemplates);

437
    layout->addWidget(m_toolbar);
438

439
    // initialize graphic scene
440
    m_scene = new GraphicsSceneRectMove(this);
441
    graphicsView->setScene(m_scene);
442
    graphicsView->setMouseTracking(true);
443
    graphicsView->setViewportUpdateMode(QGraphicsView::FullViewportUpdate);
444 445
    graphicsView->setDragMode(QGraphicsView::RubberBandDrag);
    graphicsView->setRubberBandSelectionMode(Qt::ContainsItemBoundingRect);
446
    m_titledocument.setScene(m_scene, m_frameWidth, m_frameHeight);
Laurent Montel's avatar
Laurent Montel committed
447
    connect(m_scene, &QGraphicsScene::changed, this, &TitleWidget::slotChanged);
448
    connect(font_size, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), m_scene, &GraphicsSceneRectMove::slotUpdateFontSize);
Laurent Montel's avatar
Laurent Montel committed
449
    connect(use_grid, &QAbstractButton::toggled, m_scene, &GraphicsSceneRectMove::slotUseGrid);
450

451 452
    // Video frame rect
    QPen framepen;
453 454 455
    framepen.setColor(Qt::red);
    m_frameBorder = new QGraphicsRectItem(QRectF(0, 0, m_frameWidth, m_frameHeight));
    m_frameBorder->setPen(framepen);
456
    m_frameBorder->setZValue(1000);
457
    m_frameBorder->setBrush(Qt::transparent);
Laurent Montel's avatar
Laurent Montel committed
458
    m_frameBorder->setFlags(nullptr);
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
459
    m_frameBorder->setData(-1, -1);
460
    graphicsView->scene()->addItem(m_frameBorder);
461

462
    // Guides
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
463 464
    connect(show_guides, &QCheckBox::stateChanged, this, &TitleWidget::showGuides);
    show_guides->setChecked(KdenliveSettings::titlerShowGuides());
465 466
    hguides->setValue(KdenliveSettings::titlerHGuides());
    vguides->setValue(KdenliveSettings::titlerVGuides());
467 468
    guideColor->setColor(KdenliveSettings::titleGuideColor());
    connect(guideColor, &KColorButton::changed, this, &TitleWidget::guideColorChanged);
469 470 471
    connect(hguides, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, &TitleWidget::updateGuides);
    connect(vguides, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, &TitleWidget::updateGuides);
    updateGuides(0);
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
472

473
    // semi transparent safe zones
474
    framepen.setColor(QColor(255, 0, 0, 100));
475 476 477
    QGraphicsRectItem *safe1 = new QGraphicsRectItem(QRectF(m_frameWidth * 0.05, m_frameHeight * 0.05, m_frameWidth * 0.9, m_frameHeight * 0.9), m_frameBorder);
    safe1->setBrush(Qt::transparent);
    safe1->setPen(framepen);
Laurent Montel's avatar
Laurent Montel committed
478
    safe1->setFlags(nullptr);
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
479
    safe1->setData(-1, -1);
480 481 482
    QGraphicsRectItem *safe2 = new QGraphicsRectItem(QRectF(m_frameWidth * 0.1, m_frameHeight * 0.1, m_frameWidth * 0.8, m_frameHeight * 0.8), m_frameBorder);
    safe2->setBrush(Qt::transparent);
    safe2->setPen(framepen);
Laurent Montel's avatar
Laurent Montel committed
483
    safe2->setFlags(nullptr);
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
484
    safe2->setData(-1, -1);
485 486 487 488

    m_frameBackground = new QGraphicsRectItem(QRectF(0, 0, m_frameWidth, m_frameHeight));
    m_frameBackground->setZValue(-1100);
    m_frameBackground->setBrush(Qt::transparent);
Laurent Montel's avatar
Laurent Montel committed
489
    m_frameBackground->setFlags(nullptr);
490 491
    graphicsView->scene()->addItem(m_frameBackground);

492 493 494 495 496
    m_frameImage = new QGraphicsPixmapItem();
    QTransform qtrans;
    qtrans.scale(2.0, 2.0);
    m_frameImage->setTransform(qtrans);
    m_frameImage->setZValue(-1200);
Laurent Montel's avatar
Laurent Montel committed
497
    m_frameImage->setFlags(nullptr);
498 499 500
    displayBackgroundFrame();
    graphicsView->scene()->addItem(m_frameImage);

501 502 503 504 505 506
    bgBox->setCurrentIndex(KdenliveSettings::titlerbg());
    connect(bgBox, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), [&] (int ix) {
        KdenliveSettings::setTitlerbg(ix);
        displayBackgroundFrame();
    });

Laurent Montel's avatar
Laurent Montel committed
507 508 509 510 511 512 513
    connect(m_scene, &QGraphicsScene::selectionChanged, this, &TitleWidget::selectionChanged);
    connect(m_scene, &GraphicsSceneRectMove::itemMoved, this, &TitleWidget::selectionChanged);
    connect(m_scene, &GraphicsSceneRectMove::sceneZoom, this, &TitleWidget::slotZoom);
    connect(m_scene, &GraphicsSceneRectMove::actionFinished, this, &TitleWidget::slotSelectTool);
    connect(m_scene, &GraphicsSceneRectMove::newRect, this, &TitleWidget::slotNewRect);
    connect(m_scene, &GraphicsSceneRectMove::newText, this, &TitleWidget::slotNewText);
    connect(zoom_slider, &QAbstractSlider::valueChanged, this, &TitleWidget::slotUpdateZoom);
514
    connect(zoom_spin, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, &TitleWidget::slotUpdateZoom);
515

516
    // mbd: load saved settings
517
    loadGradients();
518
    readChoices();
Mads Bondo Dydensborg's avatar
Mads Bondo Dydensborg committed
519

520 521 522
    // Hide effects not implemented
    tabWidget->removeTab(3);

523 524
    graphicsView->show();
    graphicsView->setInteractive(true);
Nicolas Carion's avatar
Nicolas Carion committed
525
    // qCDebug(KDENLIVE_LOG) << "// TITLE WIDGWT: " << graphicsView->viewport()->width() << 'x' << graphicsView->viewport()->height();
526
    m_startViewport = new QGraphicsRectItem(QRectF(0, 0, m_frameWidth, m_frameHeight));
527 528
    // Setting data at -1 so that the item is recognized as undeletable by graphicsscenerectmove
    m_startViewport->setData(-1, -1);
529
    m_endViewport = new QGraphicsRectItem(QRectF(0, 0, m_frameWidth, m_frameHeight));
530
    m_endViewport->setData(-1, -1);
531 532 533 534
    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
535

536 537
    // scale the view so that the title widget is not too big at startup
    graphicsView->scale(.5, .5);
538
    if (url.isValid()) {
Laurent Montel's avatar
Laurent Montel committed
539 540
        loadTitle(url);
    } else {
Laurent Montel's avatar
Laurent Montel committed
541
        prepareTools(nullptr);
Laurent Montel's avatar
Laurent Montel committed
542
        slotTextTool();
Laurent Montel's avatar
Laurent Montel committed
543
        QTimer::singleShot(200, this, &TitleWidget::slotAdjustZoom);
544
    }
545
    initAnimation();
546 547 548 549
    QColor color = backgroundColor->color();
    m_scene->setBackgroundBrush(QBrush(color));
    color.setAlpha(backgroundAlpha->value());
    m_frameBackground->setBrush(color);
Laurent Montel's avatar
Laurent Montel committed
550 551
    connect(anim_start, &QAbstractButton::toggled, this, &TitleWidget::slotAnimStart);
    connect(anim_end, &QAbstractButton::toggled, this, &TitleWidget::slotAnimEnd);
552
    connect(templateBox, SIGNAL(currentIndexChanged(int)), this, SLOT(templateIndexChanged(int)));
553 554

    buttonBox->button(QDialogButtonBox::Ok)->setEnabled(KdenliveSettings::hastitleproducer());
555 556 557
    if (titletemplates.isEmpty()) {
        refreshTitleTemplates(m_projectTitlePath);
    }
Nicolas Carion's avatar
Nicolas Carion committed
558
    // templateBox->setIconSize(QSize(60,60));
559
    refreshTemplateBoxContents();
Nicolas Carion's avatar
Nicolas Carion committed
560
    m_lastDocumentHash = QCryptographicHash::hash(xml().toString().toLatin1(), QCryptographicHash::Md5).toHex();
Marco Gittler's avatar
Marco Gittler committed
561
}
562

563 564
TitleWidget::~TitleWidget()
{
565
    m_scene->blockSignals(true);
566 567 568 569 570 571
    delete m_buttonRect;
    delete m_buttonText;
    delete m_buttonImage;
    delete m_buttonCursor;
    delete m_buttonSave;
    delete m_buttonLoad;
572
    delete m_unicodeAction;
Simon Eugster's avatar
Simon Eugster committed
573 574 575 576
    delete m_zUp;
    delete m_zDown;
    delete m_zTop;
    delete m_zBottom;
577 578 579 580 581
    delete m_selectAll;
    delete m_selectText;
    delete m_selectRects;
    delete m_selectImages;
    delete m_unselectAll;
582

Alberto Villa's avatar
Alberto Villa committed
583
    delete m_unicodeDialog;
584 585
    delete m_frameBorder;
    delete m_frameImage;
586 587
    delete m_startViewport;
    delete m_endViewport;
588
    delete m_scene;
589
    delete m_signalMapper;
590 591
}

592
// static
Laurent Montel's avatar
Laurent Montel committed
593
QStringList TitleWidget::extractImageList(const QString &xml)
594 595
{
    QStringList result;
Laurent Montel's avatar
Laurent Montel committed
596 597 598
    if (xml.isEmpty()) {
        return result;
    }
599 600
    QDomDocument doc;
    doc.setContent(xml);
601
    QDomNodeList images = doc.elementsByTagName(QStringLiteral("content"));
602
    for (int i = 0; i < images.count(); ++i) {
Laurent Montel's avatar
Laurent Montel committed
603
        if (images.at(i).toElement().hasAttribute(QStringLiteral("url"))) {
604
            result.append(images.at(i).toElement().attribute(QStringLiteral("url")));
Laurent Montel's avatar
Laurent Montel committed
605
        }
606 607 608 609
    }
    return result;
}

610
// static
Laurent Montel's avatar
Laurent Montel committed
611
QStringList TitleWidget::extractFontList(const QString &xml)
612 613
{
    QStringList result;
Laurent Montel's avatar
Laurent Montel committed
614 615 616
    if (xml.isEmpty()) {
        return result;
    }
617 618
    QDomDocument doc;
    doc.setContent(xml);
619
    QDomNodeList images = doc.elementsByTagName(QStringLiteral("content"));
620
    for (int i = 0; i < images.count(); ++i) {
Laurent Montel's avatar
Laurent Montel committed
621
        if (images.at(i).toElement().hasAttribute(QStringLiteral("font"))) {
622
            result.append(images.at(i).toElement().attribute(QStringLiteral("font")));
Laurent Montel's avatar
Laurent Montel committed
623
        }
624 625 626
    }
    return result;
}
Nicolas Carion's avatar
Nicolas Carion committed
627
// static
628
void TitleWidget::refreshTitleTemplates(const QString &projectPath)
629
{
Laurent Montel's avatar
Laurent Montel committed
630
    QStringList filters = QStringList() << QStringLiteral("*.kdenlivetitle");
631
    titletemplates.clear();
632 633 634 635

    // project templates
    QDir dir(projectPath);
    QStringList templateFiles = dir.entryList(filters, QDir::Files);
Nicolas Carion's avatar
Nicolas Carion committed
636
    for (const QString &fname : templateFiles) {
637
        TitleTemplate t;
Laurent Montel's avatar
Laurent Montel committed
638 639
        t.name = fname;
        t.file = dir.absoluteFilePath(fname);
640
        t.icon = QIcon(KThumb::getImage(QUrl::fromLocalFile(t.file), 0, 60, -1));
Laurent Montel's avatar
Laurent Montel committed
641
        titletemplates.append(t);
642 643 644
    }

    // system templates
645
    QStringList titleTemplates = QStandardPaths::locateAll(QStandardPaths::AppDataLocation, QStringLiteral("titles/"), QStandardPaths::LocateDirectory);
Nicolas Carion's avatar
Nicolas Carion committed
646
    for (const QString &folderpath : titleTemplates) {
647
        QDir folder(folderpath);
648
        QStringList filesnames = folder.entryList(filters, QDir::Files);
Nicolas Carion's avatar
Nicolas Carion committed
649
        for (const QString &fname : filesnames) {
650 651
            TitleTemplate t;
            t.name = fname;
652
            t.file = folder.absoluteFilePath(fname);
653
            t.icon = QIcon(KThumb::getImage(QUrl::fromLocalFile(t.file), 0, 60, -1));
654
            titletemplates.append(t);
655 656 657
        }
    }
}
Laurent Montel's avatar
Laurent Montel committed
658

659
void TitleWidget::templateIndexChanged(int index)
660
{
661
    QString item = templateBox->itemData(index).toString();
662
    if (!item.isEmpty()) {
Nicolas Carion's avatar
Nicolas Carion committed
663
        if (m_lastDocumentHash != QCryptographicHash::hash(xml().toString().toLatin1(), QCryptographicHash::Md5).toHex()) {
Laurent Montel's avatar
Laurent Montel committed
664
            if (KMessageBox::questionYesNo(this, i18n("Do you really want to load a new template? Changes in this title will be lost!")) == KMessageBox::No) {
Laurent Montel's avatar
Laurent Montel committed
665
                return;