File indexing completed on 2024-04-14 04:47:24
0001 /* 0002 SPDX-FileCopyrightText: 2008 Marco Gittler <g.marco@freenet.de> 0003 SPDX-FileCopyrightText: Rafał Lalik 0004 0005 SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0006 */ 0007 0008 /* 0009 * * 0010 * Modifications by Rafał Lalik to implement Patterns mechanism * 0011 * * 0012 ***************************************************************************/ 0013 0014 #include "titlewidget.h" 0015 #include "bin/bin.h" 0016 #include "core.h" 0017 #include "doc/kthumb.h" 0018 #include "gradientwidget.h" 0019 #include "kdenlivesettings.h" 0020 #include "mainwindow.h" 0021 #include "monitor/monitor.h" 0022 #include "profiles/profilemodel.hpp" 0023 #include "titler/patternsmodel.h" 0024 #include "widgets/timecodedisplay.h" 0025 #include "xml/xml.hpp" 0026 0027 #include <cmath> 0028 0029 #include "utils/KMessageBox_KdenliveCompat.h" 0030 #include <KLocalizedString> 0031 #include <KMessageBox> 0032 #include <KMessageWidget> 0033 #include <KRecentDirs> 0034 0035 #include "kdenlive_debug.h" 0036 #include <QButtonGroup> 0037 #include <QCryptographicHash> 0038 #include <QDomDocument> 0039 #include <QFileDialog> 0040 #include <QFontDatabase> 0041 #include <QGraphicsBlurEffect> 0042 #include <QGraphicsDropShadowEffect> 0043 #include <QGraphicsEffect> 0044 #include <QGraphicsItem> 0045 #include <QGraphicsSvgItem> 0046 #include <QImageReader> 0047 #include <QKeyEvent> 0048 #include <QMenu> 0049 #include <QSpinBox> 0050 #include <QTextBlockFormat> 0051 #include <QTextCursor> 0052 #include <QTimer> 0053 #include <QToolBar> 0054 0055 #include <QStandardPaths> 0056 #include <iostream> 0057 #include <mlt++/MltProfile.h> 0058 #include <utility> 0059 0060 static QList<TitleTemplate> titleTemplates; 0061 0062 // TODO What exactly is this variable good for? 0063 int settingUp = 0; 0064 0065 static int TITLERVERSION = 0; 0066 const int IMAGEITEM = 7; 0067 const int RECTITEM = QGraphicsRectItem::Type; 0068 const int TEXTITEM = QGraphicsTextItem::Type; 0069 const int ELLIPSEITEM = QGraphicsEllipseItem::Type; 0070 0071 /* 0072 const int NOEFFECT = 0; 0073 const int BLUREFFECT = 1; 0074 const int SHADOWEFFECT = 2; 0075 const int TYPEWRITEREFFECT = 3; 0076 */ 0077 0078 void TitleWidget::refreshTemplateBoxContents() 0079 { 0080 templateBox->clear(); 0081 templateBox->addItem(QString()); 0082 for (const TitleTemplate &t : qAsConst(titleTemplates)) { 0083 templateBox->addItem(t.icon, t.name, t.file); 0084 } 0085 } 0086 0087 TitleWidget::TitleWidget(const QUrl &url, QString projectTitlePath, Monitor *monitor, QWidget *parent) 0088 : QDialog(parent) 0089 , Ui::TitleWidget_UI() 0090 , m_startViewport(nullptr) 0091 , m_endViewport(nullptr) 0092 , m_count(0) 0093 , m_unicodeDialog(new UnicodeDialog(UnicodeDialog::InputHex)) 0094 , m_missingMessage(nullptr) 0095 , m_projectTitlePath(std::move(projectTitlePath)) 0096 , m_fps(pCore->getCurrentFps()) 0097 , m_guides(QList<QGraphicsLineItem *>()) 0098 { 0099 setupUi(this); 0100 if (TITLERVERSION == 0) { 0101 if (KdenliveSettings::titlerVersion() < 300) { 0102 // Check version of the titler module 0103 QScopedPointer<Mlt::Properties> metadata(pCore->getMltRepository()->metadata(mlt_service_producer_type, "kdenlivetitle")); 0104 KdenliveSettings::setTitlerVersion(int(ceil(100 * metadata->get_double("version")))); 0105 } 0106 TITLERVERSION = KdenliveSettings::titlerVersion(); 0107 } 0108 setMinimumSize(200, 200); 0109 setFont(QFontDatabase::systemFont(QFontDatabase::SmallestReadableFont)); 0110 frame_properties->setEnabled(false); 0111 frame_properties->setFixedHeight(frame_toolbar->height()); 0112 int size = style()->pixelMetric(QStyle::PM_SmallIconSize); 0113 QSize iconSize(size, size); 0114 0115 rectBColor->setAlphaChannelEnabled(true); 0116 rectFColor->setAlphaChannelEnabled(true); 0117 fontColorButton->setAlphaChannelEnabled(true); 0118 textOutlineColor->setAlphaChannelEnabled(true); 0119 shadowColor->setAlphaChannelEnabled(true); 0120 0121 auto *colorGroup = new QButtonGroup(this); 0122 colorGroup->addButton(gradient_color); 0123 colorGroup->addButton(plain_color); 0124 0125 m_textAlignGroup = new QButtonGroup(this); 0126 m_textAlignGroup->addButton(buttonAlignLeft, 0); 0127 m_textAlignGroup->addButton(buttonAlignCenter, 1); 0128 m_textAlignGroup->addButton(buttonAlignRight, 2); 0129 QAbstractButton *selectedButton = m_textAlignGroup->button(KdenliveSettings::titlerAlign()); 0130 if (selectedButton) { 0131 selectedButton->setChecked(true); 0132 } 0133 0134 textOutline->setMinimum(0); 0135 textOutline->setMaximum(200); 0136 // textOutline->setDecimals(0); 0137 textOutline->setValue(0); 0138 textOutline->setToolTip(i18n("Outline width")); 0139 0140 backgroundAlpha->setMinimum(0); 0141 backgroundAlpha->setMaximum(255); 0142 bgAlphaSlider->setMinimum(0); 0143 bgAlphaSlider->setMaximum(255); 0144 backgroundAlpha->setValue(0); 0145 backgroundAlpha->setToolTip(i18n("Background color opacity")); 0146 0147 itemrotatex->setMinimum(-360); 0148 itemrotatex->setMaximum(360); 0149 // itemrotatex->setDecimals(0); 0150 itemrotatex->setValue(0); 0151 itemrotatex->setToolTip(i18n("Rotation around the X axis")); 0152 0153 itemrotatey->setMinimum(-360); 0154 itemrotatey->setMaximum(360); 0155 // itemrotatey->setDecimals(0); 0156 itemrotatey->setValue(0); 0157 itemrotatey->setToolTip(i18n("Rotation around the Y axis")); 0158 0159 itemrotatez->setMinimum(-360); 0160 itemrotatez->setMaximum(360); 0161 // itemrotatez->setDecimals(0); 0162 itemrotatez->setValue(0); 0163 itemrotatez->setToolTip(i18n("Rotation around the Z axis")); 0164 0165 rectLineWidth->setMinimum(0); 0166 rectLineWidth->setMaximum(500); 0167 // rectLineWidth->setDecimals(0); 0168 rectLineWidth->setValue(0); 0169 rectLineWidth->setToolTip(i18n("Border width")); 0170 0171 itemzoom->setSuffix(i18n("%")); 0172 QSize profileSize = pCore->getCurrentFrameSize(); 0173 m_frameWidth = qRound(profileSize.height() * pCore->getCurrentDar()); 0174 m_frameHeight = profileSize.height(); 0175 showToolbars(TITLE_SELECT); 0176 0177 splitter->setStretchFactor(0, 20); 0178 0179 m_duration->setValue(KdenliveSettings::title_duration()); 0180 0181 connect(backgroundColor, &KColorButton::changed, this, &TitleWidget::slotChangeBackground); 0182 connect(backgroundAlpha, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, &TitleWidget::slotChangeBackground); 0183 0184 connect(shadowBox, &QGroupBox::toggled, this, &TitleWidget::slotUpdateShadow); 0185 connect(shadowColor, &KColorButton::changed, this, &TitleWidget::slotUpdateShadow); 0186 connect(blur_radius, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, &TitleWidget::slotUpdateShadow); 0187 connect(shadowX, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, &TitleWidget::slotUpdateShadow); 0188 connect(shadowY, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, &TitleWidget::slotUpdateShadow); 0189 0190 connect(typewriterBox, &QGroupBox::toggled, this, &TitleWidget::slotUpdateTW); 0191 connect(tw_sb_step, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, &TitleWidget::slotUpdateTW); 0192 connect(tw_sb_sigma, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, &TitleWidget::slotUpdateTW); 0193 connect(tw_sb_seed, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, &TitleWidget::slotUpdateTW); 0194 connect(tw_rd_char, &QRadioButton::toggled, this, &TitleWidget::slotUpdateTW); 0195 connect(tw_rd_word, &QRadioButton::toggled, this, &TitleWidget::slotUpdateTW); 0196 connect(tw_rd_line, &QRadioButton::toggled, this, &TitleWidget::slotUpdateTW); 0197 connect(tw_rd_custom, &QRadioButton::toggled, this, &TitleWidget::slotUpdateTW); 0198 tw_rd_custom->setEnabled(false); 0199 0200 connect(fontColorButton, &KColorButton::changed, this, &TitleWidget::slotUpdateText); 0201 connect(plain_color, &QAbstractButton::clicked, this, &TitleWidget::slotUpdateText); 0202 connect(gradient_color, &QAbstractButton::clicked, this, &TitleWidget::slotUpdateText); 0203 connect(gradients_combo, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &TitleWidget::slotUpdateText); 0204 0205 connect(textOutlineColor, &KColorButton::changed, this, &TitleWidget::slotUpdateText); 0206 connect(font_family, &QFontComboBox::currentFontChanged, this, &TitleWidget::slotUpdateText); 0207 connect(font_size, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, &TitleWidget::slotUpdateText); 0208 connect(letter_spacing, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, &TitleWidget::slotUpdateText); 0209 connect(line_spacing, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, &TitleWidget::slotUpdateText); 0210 connect(textOutline, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, &TitleWidget::slotUpdateText); 0211 connect(font_weight_box, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &TitleWidget::slotUpdateText); 0212 0213 connect(rectFColor, &KColorButton::changed, this, &TitleWidget::rectChanged); 0214 connect(rectBColor, &KColorButton::changed, this, &TitleWidget::rectChanged); 0215 connect(plain_rect, &QAbstractButton::clicked, this, &TitleWidget::rectChanged); 0216 connect(gradient_rect, &QAbstractButton::clicked, this, &TitleWidget::rectChanged); 0217 connect(gradients_rect_combo, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &TitleWidget::rectChanged); 0218 connect(rectLineWidth, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, &TitleWidget::rectChanged); 0219 0220 connect(zValue, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, &TitleWidget::zIndexChanged); 0221 connect(itemzoom, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, &TitleWidget::itemScaled); 0222 connect(itemrotatex, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, &TitleWidget::itemRotateX); 0223 connect(itemrotatey, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, &TitleWidget::itemRotateY); 0224 connect(itemrotatez, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, &TitleWidget::itemRotateZ); 0225 connect(itemhcenter, &QAbstractButton::clicked, this, &TitleWidget::itemHCenter); 0226 connect(itemvcenter, &QAbstractButton::clicked, this, &TitleWidget::itemVCenter); 0227 connect(itemtop, &QAbstractButton::clicked, this, &TitleWidget::itemTop); 0228 connect(itembottom, &QAbstractButton::clicked, this, &TitleWidget::itemBottom); 0229 connect(itemleft, &QAbstractButton::clicked, this, &TitleWidget::itemLeft); 0230 connect(itemright, &QAbstractButton::clicked, this, &TitleWidget::itemRight); 0231 0232 connect(origin_x_left, &QAbstractButton::clicked, this, &TitleWidget::slotOriginXClicked); 0233 connect(origin_y_top, &QAbstractButton::clicked, this, &TitleWidget::slotOriginYClicked); 0234 0235 connect(monitor, &Monitor::frameUpdated, this, &TitleWidget::slotGotBackground); 0236 connect(this, &TitleWidget::requestBackgroundFrame, monitor, &Monitor::slotGetCurrentImage); 0237 0238 // Position and size 0239 connect(value_w, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, [this](int) { slotValueChanged(ValueWidth); }); 0240 connect(value_h, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, [this](int) { slotValueChanged(ValueHeight); }); 0241 connect(value_x, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, [this](int) { slotValueChanged(ValueX); }); 0242 connect(value_y, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, [this](int) { slotValueChanged(ValueY); }); 0243 0244 connect(buttonFitZoom, &QAbstractButton::clicked, this, &TitleWidget::slotAdjustZoom); 0245 connect(buttonRealSize, &QAbstractButton::clicked, this, &TitleWidget::slotZoomOneToOne); 0246 connect(buttonItalic, &QAbstractButton::clicked, this, &TitleWidget::slotUpdateText); 0247 connect(buttonUnder, &QAbstractButton::clicked, this, &TitleWidget::slotUpdateText); 0248 connect(m_textAlignGroup, QOverload<QAbstractButton *>::of(&QButtonGroup::buttonClicked), this, [this]() { 0249 KdenliveSettings::setTitlerAlign(m_textAlignGroup->checkedId()); 0250 slotUpdateText(); 0251 }); 0252 connect(edit_gradient, &QAbstractButton::clicked, this, &TitleWidget::slotEditGradient); 0253 connect(edit_rect_gradient, &QAbstractButton::clicked, this, &TitleWidget::slotEditGradient); 0254 connect(preserveAspectRatio, static_cast<void (QCheckBox::*)(int)>(&QCheckBox::stateChanged), this, [&]() { slotValueChanged(ValueWidth); }); 0255 0256 displayBg->setChecked(KdenliveSettings::titlerShowbg()); 0257 connect(displayBg, static_cast<void (QCheckBox::*)(int)>(&QCheckBox::stateChanged), this, [&](int state) { 0258 KdenliveSettings::setTitlerShowbg(state == Qt::Checked); 0259 displayBackgroundFrame(); 0260 }); 0261 0262 connect(m_unicodeDialog, &UnicodeDialog::charSelected, this, &TitleWidget::slotInsertUnicodeString); 0263 0264 // mbd 0265 connect(this, &QDialog::accepted, this, &TitleWidget::slotAccepted); 0266 0267 font_weight_box->blockSignals(true); 0268 font_weight_box->addItem(i18nc("Font style", "Light"), QFont::Light); 0269 font_weight_box->addItem(i18nc("Font style", "Normal"), QFont::Normal); 0270 font_weight_box->addItem(i18nc("Font style", "Demi-Bold"), QFont::DemiBold); 0271 font_weight_box->addItem(i18nc("Font style", "Bold"), QFont::Bold); 0272 font_weight_box->addItem(i18nc("Font style", "Black"), QFont::Black); 0273 font_weight_box->setToolTip(i18n("Font weight")); 0274 font_weight_box->setCurrentIndex(1); 0275 font_weight_box->blockSignals(false); 0276 0277 buttonFitZoom->setIconSize(iconSize); 0278 buttonRealSize->setIconSize(iconSize); 0279 buttonItalic->setIconSize(iconSize); 0280 buttonUnder->setIconSize(iconSize); 0281 buttonAlignCenter->setIconSize(iconSize); 0282 buttonAlignLeft->setIconSize(iconSize); 0283 buttonAlignRight->setIconSize(iconSize); 0284 0285 m_unicodeAction = new QAction(QIcon::fromTheme(QStringLiteral("kdenlive-insert-unicode")), QString(), this); 0286 m_unicodeAction->setShortcut(Qt::SHIFT | Qt::CTRL | Qt::Key_U); 0287 m_unicodeAction->setToolTip(getTooltipWithShortcut(i18n("Insert Unicode character"), m_unicodeAction)); 0288 connect(m_unicodeAction, &QAction::triggered, this, &TitleWidget::slotInsertUnicode); 0289 buttonInsertUnicode->setDefaultAction(m_unicodeAction); 0290 0291 m_zUp = new QAction(QIcon::fromTheme(QStringLiteral("object-order-raise")), QString(), this); 0292 m_zUp->setShortcut(Qt::Key_PageUp); 0293 m_zUp->setToolTip(i18n("Raise object")); 0294 connect(m_zUp, &QAction::triggered, this, &TitleWidget::slotZIndexUp); 0295 zUp->setDefaultAction(m_zUp); 0296 0297 m_zDown = new QAction(QIcon::fromTheme(QStringLiteral("object-order-lower")), QString(), this); 0298 m_zDown->setShortcut(Qt::Key_PageDown); 0299 m_zDown->setToolTip(i18n("Lower object")); 0300 connect(m_zDown, &QAction::triggered, this, &TitleWidget::slotZIndexDown); 0301 zDown->setDefaultAction(m_zDown); 0302 0303 m_zTop = new QAction(QIcon::fromTheme(QStringLiteral("object-order-front")), QString(), this); 0304 // TODO mbt 1414: Shortcut should change z index only if 0305 // cursor is NOT in a text field ... 0306 // m_zTop->setShortcut(Qt::Key_Home); 0307 m_zTop->setToolTip(i18n("Raise object to top")); 0308 connect(m_zTop, &QAction::triggered, this, &TitleWidget::slotZIndexTop); 0309 zTop->setDefaultAction(m_zTop); 0310 0311 m_zBottom = new QAction(QIcon::fromTheme(QStringLiteral("object-order-back")), QString(), this); 0312 // TODO mbt 1414 0313 // m_zBottom->setShortcut(Qt::Key_End); 0314 m_zBottom->setToolTip(i18n("Lower object to bottom")); 0315 connect(m_zBottom, &QAction::triggered, this, &TitleWidget::slotZIndexBottom); 0316 zBottom->setDefaultAction(m_zBottom); 0317 0318 m_selectAll = new QAction(QIcon::fromTheme(QStringLiteral("edit-select-all")), QString(), this); 0319 m_selectAll->setShortcut(Qt::CTRL | Qt::Key_A); 0320 m_selectAll->setToolTip(i18n("Select All")); 0321 connect(m_selectAll, &QAction::triggered, this, &TitleWidget::slotSelectAll); 0322 buttonSelectAll->setDefaultAction(m_selectAll); 0323 0324 m_selectText = new QAction(QIcon::fromTheme(QStringLiteral("kdenlive-select-texts")), QString(), this); 0325 m_selectText->setShortcut(Qt::CTRL | Qt::Key_T); 0326 m_selectText->setToolTip(i18n("Keep only text items selected")); 0327 connect(m_selectText, &QAction::triggered, this, &TitleWidget::slotSelectText); 0328 buttonSelectText->setDefaultAction(m_selectText); 0329 buttonSelectText->setEnabled(false); 0330 0331 m_selectRects = new QAction(QIcon::fromTheme(QStringLiteral("kdenlive-select-rects")), QString(), this); 0332 m_selectRects->setShortcut(Qt::CTRL | Qt::Key_R); 0333 m_selectRects->setToolTip(i18n("Keep only rect items selected")); 0334 connect(m_selectRects, &QAction::triggered, this, &TitleWidget::slotSelectRects); 0335 buttonSelectRects->setDefaultAction(m_selectRects); 0336 buttonSelectRects->setEnabled(false); 0337 0338 m_selectImages = new QAction(QIcon::fromTheme(QStringLiteral("kdenlive-select-images")), QString(), this); 0339 m_selectImages->setShortcut(Qt::CTRL | Qt::Key_I); 0340 m_selectImages->setToolTip(i18n("Keep only image items selected")); 0341 connect(m_selectImages, &QAction::triggered, this, &TitleWidget::slotSelectImages); 0342 buttonSelectImages->setDefaultAction(m_selectImages); 0343 buttonSelectImages->setEnabled(false); 0344 0345 m_unselectAll = new QAction(QIcon::fromTheme(QStringLiteral("edit-select-none")), QString(), this); 0346 m_unselectAll->setShortcut(Qt::SHIFT | Qt::CTRL | Qt::Key_A); 0347 m_unselectAll->setToolTip(i18n("Deselect")); 0348 connect(m_unselectAll, &QAction::triggered, this, &TitleWidget::slotSelectNone); 0349 buttonUnselectAll->setDefaultAction(m_unselectAll); 0350 buttonUnselectAll->setEnabled(false); 0351 0352 origin_x_left->setToolTip(i18n("Invert x axis and change 0 point")); 0353 origin_y_top->setToolTip(i18n("Invert y axis and change 0 point")); 0354 rectBColor->setToolTip(i18n("Select fill color")); 0355 rectFColor->setToolTip(i18n("Select border color")); 0356 zoom_slider->setToolTip(i18n("Zoom")); 0357 buttonRealSize->setToolTip(i18n("Original size (1:1)")); 0358 buttonFitZoom->setToolTip(i18n("Fit zoom")); 0359 backgroundColor->setToolTip(i18n("Select background color")); 0360 backgroundAlpha->setToolTip(i18n("Background opacity")); 0361 buttonSelectAll->setToolTip(getTooltipWithShortcut(i18n("Select all"), m_selectAll)); 0362 buttonSelectText->setToolTip(getTooltipWithShortcut(i18n("Select text items in current selection"), m_selectText)); 0363 buttonSelectRects->setToolTip(getTooltipWithShortcut(i18n("Select rect items in current selection"), m_selectRects)); 0364 buttonSelectImages->setToolTip(getTooltipWithShortcut(i18n("Select image items in current selection"), m_selectImages)); 0365 buttonUnselectAll->setToolTip(getTooltipWithShortcut(i18n("Unselect all"), m_unselectAll)); 0366 0367 itemhcenter->setIconSize(iconSize); 0368 itemvcenter->setIconSize(iconSize); 0369 itemtop->setIconSize(iconSize); 0370 itembottom->setIconSize(iconSize); 0371 itemright->setIconSize(iconSize); 0372 itemleft->setIconSize(iconSize); 0373 0374 auto *layout = new QHBoxLayout; 0375 frame_toolbar->setLayout(layout); 0376 layout->setContentsMargins(0, 0, 0, 0); 0377 QToolBar *m_toolbar = new QToolBar(QStringLiteral("titleToolBar"), this); 0378 m_toolbar->setIconSize(iconSize); 0379 0380 m_buttonCursor = m_toolbar->addAction(QIcon::fromTheme(QStringLiteral("transform-move")), i18n("Selection Tool")); 0381 m_buttonCursor->setCheckable(true); 0382 m_buttonCursor->setShortcut(Qt::ALT | Qt::Key_S); 0383 m_buttonCursor->setToolTip(i18n("Selection Tool") + QLatin1Char(' ') + m_buttonCursor->shortcut().toString()); 0384 m_buttonCursor->setWhatsThis(xi18nc("@info:whatsthis", "When selected, a click on an asset in the timeline selects the asset (e.g. clip, composition).")); 0385 connect(m_buttonCursor, &QAction::triggered, this, &TitleWidget::slotSelectTool); 0386 0387 m_buttonText = m_toolbar->addAction(QIcon::fromTheme(QStringLiteral("insert-text")), i18n("Add Text")); 0388 m_buttonText->setCheckable(true); 0389 m_buttonText->setShortcut(Qt::ALT | Qt::Key_T); 0390 m_buttonText->setToolTip(i18n("Add Text") + QLatin1Char(' ') + m_buttonText->shortcut().toString()); 0391 connect(m_buttonText, &QAction::triggered, this, &TitleWidget::slotTextTool); 0392 0393 m_buttonRect = m_toolbar->addAction(QIcon::fromTheme(QStringLiteral("kdenlive-insert-rect")), i18n("Add Rectangle")); 0394 m_buttonRect->setCheckable(true); 0395 m_buttonRect->setShortcut(Qt::ALT | Qt::Key_R); 0396 m_buttonRect->setToolTip(i18n("Add Rectangle") + QLatin1Char(' ') + m_buttonRect->shortcut().toString()); 0397 connect(m_buttonRect, &QAction::triggered, this, &TitleWidget::slotRectTool); 0398 0399 m_buttonEllipse = m_toolbar->addAction(QIcon::fromTheme(QStringLiteral("draw-ellipse")), i18n("Add Ellipse")); 0400 m_buttonEllipse->setCheckable(true); 0401 m_buttonEllipse->setShortcut(Qt::ALT | Qt::Key_E); 0402 m_buttonEllipse->setToolTip(i18n("Add Ellipse") + QLatin1Char(' ') + m_buttonEllipse->shortcut().toString()); 0403 connect(m_buttonEllipse, &QAction::triggered, this, &TitleWidget::slotEllipseTool); 0404 0405 m_buttonImage = m_toolbar->addAction(QIcon::fromTheme(QStringLiteral("insert-image")), i18n("Add Image")); 0406 m_buttonImage->setCheckable(false); 0407 m_buttonImage->setShortcut(Qt::ALT | Qt::Key_I); 0408 m_buttonImage->setToolTip(i18n("Add Image") + QLatin1Char(' ') + m_buttonImage->shortcut().toString()); 0409 connect(m_buttonImage, &QAction::triggered, this, &TitleWidget::slotImageTool); 0410 0411 m_toolbar->addSeparator(); 0412 0413 m_buttonLoad = m_toolbar->addAction(QIcon::fromTheme(QStringLiteral("document-open")), i18n("Open Document")); 0414 m_buttonLoad->setCheckable(false); 0415 m_buttonLoad->setShortcut(Qt::CTRL | Qt::Key_O); 0416 m_buttonLoad->setToolTip(i18n("Open Document") + QLatin1Char(' ') + m_buttonLoad->shortcut().toString()); 0417 connect(m_buttonLoad, SIGNAL(triggered()), this, SLOT(loadTitle())); 0418 0419 m_buttonSave = m_toolbar->addAction(QIcon::fromTheme(QStringLiteral("document-save-as")), i18n("Save As")); 0420 m_buttonSave->setCheckable(false); 0421 m_buttonSave->setShortcut(Qt::CTRL | Qt::Key_S); 0422 m_buttonSave->setToolTip(i18n("Save As") + QLatin1Char(' ') + m_buttonSave->shortcut().toString()); 0423 connect(m_buttonSave, &QAction::triggered, [this]() { saveTitle(); }); 0424 0425 m_buttonDownload = new KNSWidgets::Action(i18n("Download New Title Templates..."), QStringLiteral(":data/kdenlive_titles.knsrc"), this); 0426 m_buttonDownload->setShortcut(Qt::ALT | Qt::Key_D); 0427 m_buttonDownload->setToolTip(i18n("Download New Title Templates...") + QLatin1Char(' ') + m_buttonDownload->shortcut().toString()); 0428 m_toolbar->addAction(m_buttonDownload); 0429 connect(m_buttonDownload, &KNSWidgets::Action::dialogFinished, this, [&](const QList<KNSCore::Entry> &changedEntries) { 0430 if (changedEntries.count() > 0) { 0431 refreshTitleTemplates(m_projectTitlePath); 0432 refreshTemplateBoxContents(); 0433 } 0434 }); 0435 0436 layout->addWidget(m_toolbar); 0437 0438 // initialize graphic scene 0439 m_scene = new GraphicsSceneRectMove(TITLERVERSION, this); 0440 graphicsView->setScene(m_scene); 0441 graphicsView->setMouseTracking(true); 0442 graphicsView->setViewportUpdateMode(QGraphicsView::FullViewportUpdate); 0443 graphicsView->setDragMode(QGraphicsView::RubberBandDrag); 0444 graphicsView->setRubberBandSelectionMode(Qt::ContainsItemBoundingRect); 0445 m_titledocument.setScene(m_scene, m_frameWidth, m_frameHeight); 0446 connect(m_scene, &QGraphicsScene::changed, this, &TitleWidget::slotChanged); 0447 connect(font_size, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), m_scene, &GraphicsSceneRectMove::slotUpdateFontSize); 0448 connect(use_grid, &QAbstractButton::toggled, m_scene, &GraphicsSceneRectMove::slotUseGrid); 0449 0450 // Video frame rect 0451 QPen framepen; 0452 framepen.setColor(Qt::red); 0453 m_frameBorder = new QGraphicsRectItem(QRectF(0, 0, m_frameWidth, m_frameHeight)); 0454 m_frameBorder->setPen(framepen); 0455 m_frameBorder->setZValue(1000); 0456 m_frameBorder->setBrush(Qt::transparent); 0457 m_frameBorder->setData(-1, -1); 0458 graphicsView->scene()->addItem(m_frameBorder); 0459 0460 // Guides 0461 connect(show_guides, &QCheckBox::stateChanged, this, &TitleWidget::showGuides); 0462 show_guides->setChecked(KdenliveSettings::titlerShowGuides()); 0463 hguides->setValue(KdenliveSettings::titlerHGuides()); 0464 vguides->setValue(KdenliveSettings::titlerVGuides()); 0465 guideColor->setColor(KdenliveSettings::titleGuideColor()); 0466 connect(guideColor, &KColorButton::changed, this, &TitleWidget::guideColorChanged); 0467 connect(hguides, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, &TitleWidget::updateGuides); 0468 connect(vguides, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, &TitleWidget::updateGuides); 0469 updateGuides(0); 0470 0471 // semi transparent safe zones 0472 framepen.setColor(QColor(255, 0, 0, 100)); 0473 QGraphicsRectItem *safe1 = new QGraphicsRectItem(QRectF(m_frameWidth * 0.05, m_frameHeight * 0.05, m_frameWidth * 0.9, m_frameHeight * 0.9), m_frameBorder); 0474 safe1->setBrush(Qt::transparent); 0475 safe1->setPen(framepen); 0476 safe1->setData(-1, -1); 0477 QGraphicsRectItem *safe2 = new QGraphicsRectItem(QRectF(m_frameWidth * 0.1, m_frameHeight * 0.1, m_frameWidth * 0.8, m_frameHeight * 0.8), m_frameBorder); 0478 safe2->setBrush(Qt::transparent); 0479 safe2->setPen(framepen); 0480 safe2->setData(-1, -1); 0481 0482 m_frameBackground = new QGraphicsRectItem(QRectF(0, 0, m_frameWidth, m_frameHeight)); 0483 m_frameBackground->setZValue(-1100); 0484 m_frameBackground->setBrush(Qt::transparent); 0485 graphicsView->scene()->addItem(m_frameBackground); 0486 0487 m_frameImage = new QGraphicsPixmapItem(); 0488 QTransform qtrans; 0489 qtrans.scale(2.0, 2.0); 0490 m_frameImage->setTransform(qtrans); 0491 m_frameImage->setZValue(-1200); 0492 displayBackgroundFrame(); 0493 graphicsView->scene()->addItem(m_frameImage); 0494 0495 bgBox->setCurrentIndex(KdenliveSettings::titlerbg()); 0496 connect(bgBox, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, [&](int ix) { 0497 KdenliveSettings::setTitlerbg(ix); 0498 displayBackgroundFrame(); 0499 }); 0500 0501 connect(m_scene, &QGraphicsScene::selectionChanged, this, &TitleWidget::selectionChanged); 0502 connect(m_scene, &GraphicsSceneRectMove::itemMoved, this, &TitleWidget::selectionChanged); 0503 connect(m_scene, &GraphicsSceneRectMove::sceneZoom, this, &TitleWidget::slotZoom); 0504 connect(m_scene, &GraphicsSceneRectMove::actionFinished, this, &TitleWidget::slotSelectTool); 0505 connect(m_scene, &GraphicsSceneRectMove::newRect, this, &TitleWidget::slotNewRect); 0506 connect(m_scene, &GraphicsSceneRectMove::newEllipse, this, &TitleWidget::slotNewEllipse); 0507 connect(m_scene, &GraphicsSceneRectMove::newText, this, &TitleWidget::slotNewText); 0508 connect(zoom_slider, &QAbstractSlider::valueChanged, this, &TitleWidget::slotUpdateZoom); 0509 connect(zoom_spin, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, &TitleWidget::slotUpdateZoom); 0510 0511 // mbd: load saved settings 0512 loadGradients(); 0513 readChoices(); 0514 0515 graphicsView->show(); 0516 graphicsView->setInteractive(true); 0517 // qCDebug(KDENLIVE_LOG) << "// TITLE WIDGWT: " << graphicsView->viewport()->width() << 'x' << graphicsView->viewport()->height(); 0518 m_startViewport = new QGraphicsRectItem(QRectF(0, 0, m_frameWidth, m_frameHeight)); 0519 // Setting data at -1 so that the item is recognized as undeletable by graphicsscenerectmove 0520 m_startViewport->setData(-1, -1); 0521 m_endViewport = new QGraphicsRectItem(QRectF(0, 0, m_frameWidth, m_frameHeight)); 0522 m_endViewport->setData(-1, -1); 0523 m_startViewport->setData(0, m_frameWidth); 0524 m_startViewport->setData(1, m_frameHeight); 0525 m_endViewport->setData(0, m_frameWidth); 0526 m_endViewport->setData(1, m_frameHeight); 0527 0528 // scale the view so that the title widget is not too big at startup 0529 graphicsView->scale(.5, .5); 0530 if (url.isValid()) { 0531 loadTitle(url); 0532 } else { 0533 prepareTools(nullptr); 0534 slotTextTool(); 0535 QTimer::singleShot(200, this, &TitleWidget::slotAdjustZoom); 0536 } 0537 initAnimation(); 0538 QColor color = backgroundColor->color(); 0539 m_scene->setBackgroundBrush(QBrush(color)); 0540 color.setAlpha(backgroundAlpha->value()); 0541 m_frameBackground->setBrush(color); 0542 connect(anim_start, &QAbstractButton::toggled, this, &TitleWidget::slotAnimStart); 0543 connect(anim_end, &QAbstractButton::toggled, this, &TitleWidget::slotAnimEnd); 0544 connect(templateBox, SIGNAL(currentIndexChanged(int)), this, SLOT(templateIndexChanged(int))); 0545 0546 createButton->setEnabled(KdenliveSettings::producerslist().contains(QStringLiteral("kdenlivetitle"))); 0547 auto *addMenu = new QMenu(this); 0548 addMenu->addAction(i18n("Save and add to project")); 0549 m_createTitleAction = new QAction(i18n("Create Title"), this); 0550 createButton->setMenu(addMenu); 0551 connect(addMenu, &QMenu::triggered, this, [this]() { 0552 const QUrl url = saveTitle(); 0553 if (!url.isEmpty()) { 0554 pCore->bin()->slotAddClipToProject(url); 0555 done(QDialog::Rejected); 0556 } 0557 }); 0558 createButton->setDefaultAction(m_createTitleAction); 0559 connect(m_createTitleAction, &QAction::triggered, this, [this]() { done(QDialog::Accepted); }); 0560 connect(cancelButton, &QPushButton::clicked, this, [this]() { done(QDialog::Rejected); }); 0561 refreshTitleTemplates(m_projectTitlePath); 0562 0563 // patterns 0564 0565 m_patternsModel = new PatternsModel(this); 0566 m_patternsModel->setBackgroundPixmap(m_frameImage); 0567 connect(this, &TitleWidget::updatePatternsBackgroundFrame, m_patternsModel, &PatternsModel::repaintScenes); 0568 0569 connect(scaleSlider, &QSlider::valueChanged, this, &TitleWidget::slotPatternsTileWidth); 0570 connect(patternsList, &QListView::doubleClicked, this, &TitleWidget::slotPatternDblClicked); 0571 0572 connect(btn_add, &QToolButton::clicked, this, &TitleWidget::slotPatternBtnAddClicked); 0573 connect(btn_remove, &QToolButton::clicked, this, &TitleWidget::slotPatternBtnRemoveClicked); 0574 connect(btn_removeAll, &QToolButton::clicked, this, [&]() { 0575 m_patternsModel->removeAll(); 0576 btn_remove->setEnabled(false); 0577 btn_removeAll->setEnabled(false); 0578 }); 0579 0580 scaleSlider->setRange(6, 16); 0581 patternsList->setModel(m_patternsModel); 0582 connect(patternsList->selectionModel(), &QItemSelectionModel::currentChanged, this, 0583 [&](const QModelIndex &cur, const QModelIndex &prev) { btn_remove->setEnabled(cur != prev && cur.isValid()); }); 0584 0585 readPatterns(); 0586 0587 // templateBox->setIconSize(QSize(60,60)); 0588 refreshTemplateBoxContents(); 0589 m_lastDocumentHash = QCryptographicHash::hash(xml().toString().toLatin1(), QCryptographicHash::Md5).toHex(); 0590 } 0591 0592 TitleWidget::~TitleWidget() 0593 { 0594 writePatterns(); 0595 delete m_patternsModel; 0596 0597 m_scene->blockSignals(true); 0598 delete m_buttonRect; 0599 delete m_buttonEllipse; 0600 delete m_buttonText; 0601 delete m_buttonImage; 0602 delete m_buttonCursor; 0603 delete m_buttonSave; 0604 delete m_buttonLoad; 0605 delete m_unicodeAction; 0606 delete m_zUp; 0607 delete m_zDown; 0608 delete m_zTop; 0609 delete m_zBottom; 0610 delete m_selectAll; 0611 delete m_selectText; 0612 delete m_selectRects; 0613 delete m_selectImages; 0614 delete m_unselectAll; 0615 0616 delete m_unicodeDialog; 0617 delete m_frameBorder; 0618 delete m_frameImage; 0619 delete m_startViewport; 0620 delete m_endViewport; 0621 delete m_scene; 0622 } 0623 0624 // static 0625 QStringList TitleWidget::extractImageList(QString &xml, const QString &root) 0626 { 0627 QStringList result; 0628 if (xml.isEmpty()) { 0629 return result; 0630 } 0631 QDomDocument doc; 0632 doc.setContent(xml); 0633 QDomNodeList images = doc.elementsByTagName(QStringLiteral("content")); 0634 for (int i = 0; i < images.count(); ++i) { 0635 QDomElement image = images.at(i).toElement(); 0636 if (image.hasAttribute(QStringLiteral("url"))) { 0637 QString filePath = image.attribute(QStringLiteral("url")); 0638 if (!QFileInfo(filePath).isAbsolute()) { 0639 // Ensure we return absolute paths 0640 filePath.prepend(root); 0641 } 0642 result.append(filePath); 0643 } 0644 } 0645 return result; 0646 } 0647 0648 // static 0649 QPair<QStringList, QStringList> TitleWidget::extractAndFixImageList(QDomElement &e, const QString &root) 0650 { 0651 QString xml = Xml::getXmlProperty(e, QStringLiteral("xmldata")); 0652 if (xml.isEmpty()) { 0653 return {}; 0654 } 0655 QStringList fontsList = extractFontList(xml); 0656 QStringList imageList; 0657 QDomDocument doc; 0658 doc.setContent(xml); 0659 bool updated = false; 0660 QDomNodeList images = doc.elementsByTagName(QStringLiteral("content")); 0661 for (int i = 0; i < images.count(); ++i) { 0662 QDomElement image = images.at(i).toElement(); 0663 if (image.hasAttribute(QStringLiteral("url"))) { 0664 QString filePath = image.attribute(QStringLiteral("url")); 0665 if (!QFileInfo(filePath).isAbsolute()) { 0666 // Ensure Title images have absolute paths, since it does not handle relative paths internally 0667 filePath.prepend(root); 0668 image.setAttribute(QStringLiteral("url"), filePath); 0669 updated = true; 0670 } 0671 imageList.append(filePath); 0672 } 0673 } 0674 if (updated) { 0675 Xml::setXmlProperty(e, QStringLiteral("xmldata"), doc.toString()); 0676 } 0677 return {imageList, fontsList}; 0678 } 0679 0680 // static 0681 QStringList TitleWidget::extractFontList(const QString &xml) 0682 { 0683 QStringList result; 0684 if (xml.isEmpty()) { 0685 return result; 0686 } 0687 QDomDocument doc; 0688 doc.setContent(xml); 0689 QDomNodeList elements = doc.elementsByTagName(QStringLiteral("content")); 0690 for (int i = 0; i < elements.count(); ++i) { 0691 QDomElement element = elements.at(i).toElement(); 0692 if (element.hasAttribute(QStringLiteral("font"))) { 0693 result.append(element.attribute(QStringLiteral("font"))); 0694 } 0695 } 0696 return result; 0697 } 0698 // static 0699 void TitleWidget::refreshTitleTemplates(const QString &projectPath) 0700 { 0701 QStringList filters = QStringList() << QStringLiteral("*.kdenlivetitle"); 0702 titleTemplates.clear(); 0703 0704 // project templates 0705 QDir dir(projectPath); 0706 QStringList templateFiles = dir.entryList(filters, QDir::Files); 0707 for (const QString &fname : qAsConst(templateFiles)) { 0708 TitleTemplate t; 0709 t.name = fname; 0710 t.file = dir.absoluteFilePath(fname); 0711 t.icon = QIcon(KThumb::getImage(QUrl::fromLocalFile(t.file), 0, 60, -1)); 0712 titleTemplates.append(t); 0713 } 0714 0715 // system templates 0716 QStringList currentTitleTemplates = 0717 QStandardPaths::locateAll(QStandardPaths::AppLocalDataLocation, QStringLiteral("titles/"), QStandardPaths::LocateDirectory); 0718 currentTitleTemplates.removeDuplicates(); 0719 for (const QString &folderpath : qAsConst(currentTitleTemplates)) { 0720 QDir folder(folderpath); 0721 QStringList filesnames = folder.entryList(filters, QDir::Files); 0722 for (const QString &fname : qAsConst(filesnames)) { 0723 TitleTemplate t; 0724 t.name = fname; 0725 t.file = folder.absoluteFilePath(fname); 0726 t.icon = QIcon(KThumb::getImage(QUrl::fromLocalFile(t.file), 0, 60, -1)); 0727 titleTemplates.append(t); 0728 } 0729 } 0730 } 0731 0732 void TitleWidget::templateIndexChanged(int index) 0733 { 0734 QString item = templateBox->itemData(index).toString(); 0735 if (!item.isEmpty()) { 0736 if (m_lastDocumentHash != QCryptographicHash::hash(xml().toString().toLatin1(), QCryptographicHash::Md5).toHex()) { 0737 if (KMessageBox::warningContinueCancel(this, i18n("Do you really want to load a new template? Changes in this title will be lost!")) != 0738 KMessageBox::Continue) { 0739 return; 0740 } 0741 } 0742 loadTitle(QUrl::fromLocalFile(item)); 0743 0744 // mbt 1607: Add property to distinguish between unchanged template titles and user titles. 0745 // Text of unchanged template titles should be selected when clicked. 0746 QList<QGraphicsItem *> list = graphicsView->scene()->items(); 0747 for (QGraphicsItem *qgItem : qAsConst(list)) { 0748 if (qgItem->type() == TEXTITEM) { 0749 auto *i = static_cast<MyTextItem *>(qgItem); 0750 i->setProperty("isTemplate", "true"); 0751 i->setProperty("templateText", i->toHtml()); 0752 } 0753 } 0754 m_lastDocumentHash = QCryptographicHash::hash(xml().toString().toLatin1(), QCryptographicHash::Md5).toHex(); 0755 } 0756 } 0757 // virtual 0758 void TitleWidget::resizeEvent(QResizeEvent * /*event*/) 0759 { 0760 // slotAdjustZoom(); 0761 } 0762 // virtual 0763 void TitleWidget::keyPressEvent(QKeyEvent *e) 0764 { 0765 if (e->key() != Qt::Key_Escape && e->key() != Qt::Key_Return && e->key() != Qt::Key_Enter) { 0766 QDialog::keyPressEvent(e); 0767 } 0768 } 0769 0770 void TitleWidget::slotTextTool() 0771 { 0772 m_scene->setTool(TITLE_TEXT); 0773 showToolbars(TITLE_TEXT); 0774 checkButton(TITLE_TEXT); 0775 } 0776 0777 void TitleWidget::slotRectTool() 0778 { 0779 m_scene->setTool(TITLE_RECTANGLE); 0780 showToolbars(TITLE_RECTANGLE); 0781 checkButton(TITLE_RECTANGLE); 0782 0783 // Disable dragging mode, would make dragging a rect impossible otherwise ;) 0784 graphicsView->setDragMode(QGraphicsView::NoDrag); 0785 } 0786 0787 void TitleWidget::slotEllipseTool() 0788 { 0789 m_scene->setTool(TITLE_ELLIPSE); 0790 showToolbars(TITLE_ELLIPSE); 0791 checkButton(TITLE_ELLIPSE); 0792 0793 // Disable dragging mode, would make dragging a ellipse impossible otherwise ;) 0794 graphicsView->setDragMode(QGraphicsView::NoDrag); 0795 } 0796 0797 void TitleWidget::slotSelectTool() 0798 { 0799 m_scene->setTool(TITLE_SELECT); 0800 0801 // Enable rubberband selecting mode. 0802 graphicsView->setDragMode(QGraphicsView::RubberBandDrag); 0803 0804 // Find out which toolbars need to be shown, depending on selected item 0805 TITLETOOL t = TITLE_SELECT; 0806 QList<QGraphicsItem *> l = graphicsView->scene()->selectedItems(); 0807 if (!l.isEmpty()) { 0808 switch (l.at(0)->type()) { 0809 case TEXTITEM: 0810 t = TITLE_TEXT; 0811 break; 0812 case RECTITEM: 0813 t = TITLE_RECTANGLE; 0814 break; 0815 case ELLIPSEITEM: 0816 t = TITLE_ELLIPSE; 0817 break; 0818 case IMAGEITEM: 0819 t = TITLE_IMAGE; 0820 break; 0821 } 0822 } 0823 btn_add->setEnabled(!l.isEmpty()); 0824 0825 enableToolbars(t); 0826 if (t == TITLE_RECTANGLE && (l.at(0) == m_endViewport || l.at(0) == m_startViewport)) { 0827 // graphicsView->centerOn(l.at(0)); 0828 t = TITLE_SELECT; 0829 } 0830 showToolbars(t); 0831 0832 if (!l.isEmpty()) { 0833 updateCoordinates(l.at(0)); 0834 updateDimension(l.at(0)); 0835 updateRotZoom(l.at(0)); 0836 } 0837 0838 checkButton(TITLE_SELECT); 0839 } 0840 0841 void TitleWidget::slotImageTool() 0842 { 0843 QList<QByteArray> supported = QImageReader::supportedImageFormats(); 0844 QStringList mimeTypeFilters; 0845 QString allExtensions = i18n("All Images") + QStringLiteral(" ("); 0846 for (const QByteArray &mimeType : qAsConst(supported)) { 0847 mimeTypeFilters.append(i18n("%1 Image", QString(mimeType)) + QStringLiteral("( *.") + QString(mimeType) + QLatin1Char(')')); 0848 allExtensions.append(QStringLiteral("*.") + mimeType + QLatin1Char(' ')); 0849 } 0850 mimeTypeFilters.sort(); 0851 allExtensions.append(QLatin1Char(')')); 0852 mimeTypeFilters.prepend(allExtensions); 0853 QString clipFolder = KRecentDirs::dir(QStringLiteral(":KdenliveImageFolder")); 0854 if (clipFolder.isEmpty()) { 0855 clipFolder = QDir::homePath(); 0856 } 0857 QFileDialog dialog(this, i18n("Add Image"), clipFolder); 0858 dialog.setAcceptMode(QFileDialog::AcceptOpen); 0859 dialog.setNameFilters(mimeTypeFilters); 0860 if (dialog.exec() != QDialog::Accepted) { 0861 return; 0862 } 0863 QUrl url = QUrl::fromLocalFile(dialog.selectedFiles().at(0)); 0864 if (url.isValid()) { 0865 KRecentDirs::add(QStringLiteral(":KdenliveImageFolder"), url.adjusted(QUrl::RemoveFilename).toLocalFile()); 0866 if (url.toLocalFile().endsWith(QLatin1String(".svg"))) { 0867 MySvgItem *svg = new MySvgItem(url.toLocalFile()); 0868 svg->setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemSendsGeometryChanges); 0869 svg->setZValue(m_count++); 0870 svg->setData(Qt::UserRole, url.toLocalFile()); 0871 m_scene->addNewItem(svg); 0872 prepareTools(svg); 0873 } else { 0874 QPixmap pix(url.toLocalFile()); 0875 auto *image = new MyPixmapItem(pix); 0876 image->setShapeMode(QGraphicsPixmapItem::BoundingRectShape); 0877 image->setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemSendsGeometryChanges); 0878 image->setData(Qt::UserRole, url.toLocalFile()); 0879 image->setZValue(m_count++); 0880 m_scene->addNewItem(image); 0881 prepareTools(image); 0882 } 0883 } 0884 m_scene->setTool(TITLE_SELECT); 0885 showToolbars(TITLE_SELECT); 0886 checkButton(TITLE_SELECT); 0887 } 0888 0889 void TitleWidget::showToolbars(TITLETOOL toolType) 0890 { 0891 toolbar_stack->setEnabled(toolType != TITLE_SELECT); 0892 switch (toolType) { 0893 case TITLE_IMAGE: 0894 toolbar_stack->setCurrentIndex(2); 0895 break; 0896 case TITLE_ELLIPSE: 0897 case TITLE_RECTANGLE: 0898 toolbar_stack->setCurrentIndex(1); 0899 break; 0900 case TITLE_TEXT: 0901 default: 0902 toolbar_stack->setCurrentIndex(0); 0903 break; 0904 } 0905 } 0906 0907 void TitleWidget::enableToolbars(TITLETOOL toolType) 0908 { 0909 // TITLETOOL is defined in effectstack/graphicsscenerectmove.h 0910 bool enable = false; 0911 if (toolType == TITLE_RECTANGLE || toolType == TITLE_ELLIPSE || toolType == TITLE_IMAGE) { 0912 enable = true; 0913 } 0914 value_w->setEnabled(enable); 0915 value_h->setEnabled(enable); 0916 } 0917 0918 void TitleWidget::checkButton(TITLETOOL toolType) 0919 { 0920 bool bSelect = false; 0921 bool bText = false; 0922 bool bRect = false; 0923 bool bEllipse = false; 0924 bool bImage = false; 0925 0926 switch (toolType) { 0927 case TITLE_SELECT: 0928 bSelect = true; 0929 break; 0930 case TITLE_TEXT: 0931 bText = true; 0932 break; 0933 case TITLE_RECTANGLE: 0934 bRect = true; 0935 break; 0936 case TITLE_ELLIPSE: 0937 bEllipse = true; 0938 break; 0939 case TITLE_IMAGE: 0940 bImage = true; 0941 break; 0942 default: 0943 break; 0944 } 0945 0946 m_buttonCursor->setChecked(bSelect); 0947 m_buttonText->setChecked(bText); 0948 m_buttonRect->setChecked(bRect); 0949 m_buttonEllipse->setChecked(bEllipse); 0950 m_buttonImage->setChecked(bImage); 0951 } 0952 0953 void TitleWidget::displayBackgroundFrame() 0954 { 0955 QRectF r = m_frameBorder->sceneBoundingRect(); 0956 if (!displayBg->isChecked()) { 0957 switch (KdenliveSettings::titlerbg()) { 0958 case 0: { 0959 QPixmap pattern(20, 20); 0960 pattern.fill(Qt::gray); 0961 QColor bgcolor(180, 180, 180); 0962 QPainter p(&pattern); 0963 p.fillRect(QRect(0, 0, 10, 10), bgcolor); 0964 p.fillRect(QRect(10, 10, 20, 20), bgcolor); 0965 p.end(); 0966 QBrush br(pattern); 0967 QPixmap bg(int(r.width() / 2), int(r.height() / 2)); 0968 QPainter p2(&bg); 0969 p2.fillRect(bg.rect(), br); 0970 p2.end(); 0971 m_frameImage->setPixmap(bg); 0972 break; 0973 } 0974 default: { 0975 QColor col = KdenliveSettings::titlerbg() == 1 ? Qt::black : Qt::white; 0976 QPixmap bg(int(r.width() / 2), int(r.height() / 2)); 0977 QPainter p2(&bg); 0978 p2.fillRect(bg.rect(), col); 0979 p2.end(); 0980 m_frameImage->setPixmap(bg); 0981 } 0982 } 0983 Q_EMIT updatePatternsBackgroundFrame(); 0984 } else { 0985 Q_EMIT requestBackgroundFrame(true); 0986 } 0987 } 0988 0989 void TitleWidget::slotGotBackground(const QImage &img) 0990 { 0991 QRectF r = m_frameBorder->sceneBoundingRect(); 0992 m_frameImage->setPixmap(QPixmap::fromImage(img.scaled(int(r.width() / 2), int(r.height() / 2)))); 0993 Q_EMIT requestBackgroundFrame(false); 0994 Q_EMIT updatePatternsBackgroundFrame(); 0995 } 0996 0997 void TitleWidget::initAnimation() 0998 { 0999 align_box->setEnabled(false); 1000 QPen startpen(Qt::DotLine); 1001 QPen endpen(Qt::DashDotLine); 1002 startpen.setColor(QColor(100, 200, 100, 140)); 1003 endpen.setColor(QColor(200, 100, 100, 140)); 1004 1005 m_startViewport->setPen(startpen); 1006 m_endViewport->setPen(endpen); 1007 1008 m_startViewport->setZValue(-1000); 1009 m_endViewport->setZValue(-1000); 1010 1011 m_startViewport->setFlag(QGraphicsItem::ItemIsMovable, false); 1012 m_startViewport->setFlag(QGraphicsItem::ItemIsSelectable, false); 1013 m_endViewport->setFlag(QGraphicsItem::ItemIsMovable, false); 1014 m_endViewport->setFlag(QGraphicsItem::ItemIsSelectable, false); 1015 1016 graphicsView->scene()->addItem(m_startViewport); 1017 graphicsView->scene()->addItem(m_endViewport); 1018 1019 connect(keep_aspect, &QAbstractButton::toggled, this, &TitleWidget::slotKeepAspect); 1020 connect(resize50, &QAbstractButton::clicked, this, [&]() { slotResize(50); }); 1021 connect(resize100, &QAbstractButton::clicked, this, [&]() { slotResize(100); }); 1022 connect(resize200, &QAbstractButton::clicked, this, [&]() { slotResize(200); }); 1023 } 1024 1025 void TitleWidget::slotUpdateZoom(int pos) 1026 { 1027 zoom_spin->setValue(pos); 1028 zoom_slider->setValue(pos); 1029 m_scene->setZoom(pos / 100.); 1030 } 1031 1032 void TitleWidget::slotZoom(bool up) 1033 { 1034 int pos = zoom_slider->value(); 1035 if (up) { 1036 pos++; 1037 } else { 1038 pos--; 1039 } 1040 zoom_slider->setValue(pos); 1041 } 1042 1043 void TitleWidget::slotAdjustZoom() 1044 { 1045 /*double scalex = graphicsView->width() / (double)(m_frameWidth * 1.2); 1046 double scaley = graphicsView->height() / (double)(m_frameHeight * 1.2); 1047 if (scalex > scaley) scalex = scaley; 1048 int zoompos = qRound(scalex * 7);*/ 1049 graphicsView->fitInView(m_frameBorder, Qt::KeepAspectRatio); 1050 int zoompos = int(graphicsView->transform().m11() * 100); 1051 zoom_slider->setValue(zoompos); 1052 graphicsView->centerOn(m_frameBorder); 1053 } 1054 1055 void TitleWidget::slotZoomOneToOne() 1056 { 1057 zoom_slider->setValue(100); 1058 graphicsView->centerOn(m_frameBorder); 1059 } 1060 1061 void TitleWidget::slotNewRect(QGraphicsRectItem *rect) 1062 { 1063 updateAxisButtons(rect); // back to default 1064 1065 if (rectLineWidth->value() == 0) { 1066 rect->setPen(Qt::NoPen); 1067 } else { 1068 QPen penf(rectFColor->color()); 1069 penf.setWidth(rectLineWidth->value()); 1070 penf.setJoinStyle(Qt::RoundJoin); 1071 rect->setPen(penf); 1072 } 1073 if (plain_rect->isChecked()) { 1074 rect->setBrush(QBrush(rectBColor->color())); 1075 rect->setData(TitleDocument::Gradient, QVariant()); 1076 } else { 1077 // gradient 1078 QString gradientData = gradients_rect_combo->currentData().toString(); 1079 rect->setData(TitleDocument::Gradient, gradientData); 1080 QLinearGradient gr = GradientWidget::gradientFromString(gradientData, int(rect->boundingRect().width()), int(rect->boundingRect().height())); 1081 rect->setBrush(QBrush(gr)); 1082 } 1083 rect->setZValue(m_count++); 1084 rect->setData(TitleDocument::ZoomFactor, 100); 1085 prepareTools(rect); 1086 // setCurrentItem(rect); 1087 // graphicsView->setFocus(); 1088 } 1089 1090 void TitleWidget::slotNewEllipse(QGraphicsEllipseItem *ellipse) 1091 { 1092 updateAxisButtons(ellipse); // back to default 1093 1094 if (rectLineWidth->value() == 0) { 1095 ellipse->setPen(Qt::NoPen); 1096 } else { 1097 QPen penf(rectFColor->color()); 1098 penf.setWidth(rectLineWidth->value()); 1099 penf.setJoinStyle(Qt::RoundJoin); 1100 ellipse->setPen(penf); 1101 } 1102 if (plain_rect->isChecked()) { 1103 ellipse->setBrush(QBrush(rectBColor->color())); 1104 ellipse->setData(TitleDocument::Gradient, QVariant()); 1105 } else { 1106 // gradient 1107 QString gradientData = gradients_rect_combo->currentData().toString(); 1108 ellipse->setData(TitleDocument::Gradient, gradientData); 1109 QLinearGradient gr = GradientWidget::gradientFromString(gradientData, int(ellipse->boundingRect().width()), int(ellipse->boundingRect().height())); 1110 ellipse->setBrush(QBrush(gr)); 1111 } 1112 ellipse->setZValue(m_count++); 1113 ellipse->setData(TitleDocument::ZoomFactor, 100); 1114 prepareTools(ellipse); 1115 // setCurrentItem(rect); 1116 // graphicsView->setFocus(); 1117 } 1118 1119 void TitleWidget::slotNewText(MyTextItem *tt) 1120 { 1121 updateAxisButtons(tt); // back to default 1122 1123 letter_spacing->blockSignals(true); 1124 line_spacing->blockSignals(true); 1125 letter_spacing->setValue(0); 1126 line_spacing->setValue(0); 1127 letter_spacing->blockSignals(false); 1128 line_spacing->blockSignals(false); 1129 letter_spacing->setEnabled(true); 1130 line_spacing->setEnabled(true); 1131 QFont font = font_family->currentFont(); 1132 font.setPixelSize(font_size->value()); 1133 // mbd: issue 551: 1134 font.setWeight(QFont::Weight(font_weight_box->itemData(font_weight_box->currentIndex()).toInt())); 1135 font.setItalic(buttonItalic->isChecked()); 1136 font.setUnderline(buttonUnder->isChecked()); 1137 1138 tt->setFont(font); 1139 QColor color = fontColorButton->color(); 1140 QColor outlineColor = textOutlineColor->color(); 1141 tt->setTextColor(color); 1142 tt->document()->setDocumentMargin(0); 1143 1144 QTextCursor cur(tt->document()); 1145 cur.select(QTextCursor::Document); 1146 QTextBlockFormat format = cur.blockFormat(); 1147 QTextCharFormat cformat = cur.charFormat(); 1148 double outlineWidth = textOutline->value(); 1149 1150 tt->setData(TitleDocument::OutlineWidth, outlineWidth); 1151 tt->setData(TitleDocument::OutlineColor, outlineColor); 1152 if (outlineWidth > 0.0) { 1153 cformat.setTextOutline(QPen(outlineColor, outlineWidth)); 1154 } 1155 tt->updateShadow(shadowBox->isChecked(), blur_radius->value(), shadowX->value(), shadowY->value(), shadowColor->color()); 1156 if (gradient_color->isChecked()) { 1157 QString gradientData = gradients_combo->currentData().toString(); 1158 tt->setData(TitleDocument::Gradient, gradientData); 1159 QLinearGradient gr = GradientWidget::gradientFromString(gradientData, int(tt->boundingRect().width()), int(tt->boundingRect().height())); 1160 cformat.setForeground(QBrush(gr)); 1161 } else { 1162 cformat.setForeground(QBrush(color)); 1163 } 1164 cur.setCharFormat(cformat); 1165 cur.setBlockFormat(format); 1166 tt->setTextCursor(cur); 1167 tt->setZValue(m_count++); 1168 setCurrentItem(tt); 1169 prepareTools(tt); 1170 } 1171 1172 void TitleWidget::setFontBoxWeight(int weight) 1173 { 1174 int index = font_weight_box->findData(weight); 1175 if (index < 0) { 1176 index = font_weight_box->findData(QFont::Normal); 1177 } 1178 font_weight_box->setCurrentIndex(index); 1179 } 1180 1181 void TitleWidget::setCurrentItem(QGraphicsItem *item) 1182 { 1183 m_scene->setSelectedItem(item); 1184 btn_add->setEnabled(item != nullptr); 1185 } 1186 1187 void TitleWidget::zIndexChanged(int v) 1188 { 1189 QList<QGraphicsItem *> l = graphicsView->scene()->selectedItems(); 1190 for (auto &i : l) { 1191 i->setZValue(v); 1192 } 1193 } 1194 1195 void TitleWidget::selectionChanged() 1196 { 1197 if (m_scene->tool() != TITLE_SELECT) { 1198 return; 1199 } 1200 1201 // qCDebug(KDENLIVE_LOG) << "Number of selected items: " << graphicsView->scene()->selectedItems().length() << '\n'; 1202 1203 QList<QGraphicsItem *> l; 1204 1205 // mbt 1607: One text item might have grabbed the keyboard. 1206 // Ungrab it for all items that are not selected, otherwise 1207 // text input would only work for the text item that grabbed 1208 // the keyboard last. 1209 l = graphicsView->scene()->items(); 1210 for (QGraphicsItem *item : qAsConst(l)) { 1211 if (item->type() == TEXTITEM && !item->isSelected()) { 1212 auto *i = static_cast<MyTextItem *>(item); 1213 i->clearFocus(); 1214 } 1215 } 1216 1217 l = graphicsView->scene()->selectedItems(); 1218 1219 if (!l.isEmpty()) { 1220 buttonUnselectAll->setEnabled(true); 1221 // Enable all z index buttons if items selected. 1222 // We can selectively disable them later. 1223 zUp->setEnabled(true); 1224 zDown->setEnabled(true); 1225 zTop->setEnabled(true); 1226 zBottom->setEnabled(true); 1227 } else { 1228 buttonUnselectAll->setEnabled(false); 1229 } 1230 if (l.size() >= 2) { 1231 buttonSelectText->setEnabled(true); 1232 buttonSelectRects->setEnabled(true); 1233 buttonSelectImages->setEnabled(true); 1234 } else { 1235 buttonSelectText->setEnabled(false); 1236 buttonSelectRects->setEnabled(false); 1237 buttonSelectImages->setEnabled(false); 1238 } 1239 1240 if (l.size() == 0) { 1241 prepareTools(nullptr); 1242 } else if (l.size() == 1) { 1243 prepareTools(l.at(0)); 1244 } else { 1245 /* 1246 For multiple selected objects we need to decide which tools to show. 1247 */ 1248 int firstType = l.at(0)->type(); 1249 bool allEqual = true; 1250 for (auto i : qAsConst(l)) { 1251 if (i->type() != firstType) { 1252 allEqual = false; 1253 break; 1254 } 1255 } 1256 // qCDebug(KDENLIVE_LOG) << "All equal? " << allEqual << ".\n"; 1257 if (allEqual) { 1258 prepareTools(l.at(0)); 1259 } else { 1260 // Get the default toolset, but enable the property frame (x,y,w,h) 1261 prepareTools(nullptr); 1262 frame_properties->setEnabled(true); 1263 1264 // Enable x/y/w/h if it makes sense. 1265 value_x->setEnabled(true); 1266 value_y->setEnabled(true); 1267 bool containsTextitem = false; 1268 for (auto i : qAsConst(l)) { 1269 if (i->type() == TEXTITEM) { 1270 containsTextitem = true; 1271 break; 1272 } 1273 } 1274 if (!containsTextitem) { 1275 value_w->setEnabled(true); 1276 value_h->setEnabled(true); 1277 } 1278 } 1279 1280 // Disable z index buttons if they don't make sense for the current selection 1281 int firstZindex = int(l.at(0)->zValue()); 1282 allEqual = true; 1283 for (auto &i : l) { 1284 if (int(i->zValue()) != firstZindex) { 1285 allEqual = false; 1286 break; 1287 } 1288 } 1289 if (!allEqual) { 1290 zUp->setEnabled(false); 1291 zDown->setEnabled(false); 1292 } 1293 } 1294 } 1295 1296 void TitleWidget::slotValueChanged(int type) 1297 { 1298 /* 1299 type tells us which QSpinBox value has changed. 1300 */ 1301 1302 QList<QGraphicsItem *> l = graphicsView->scene()->selectedItems(); 1303 // qCDebug(KDENLIVE_LOG) << l.size() << " items to be resized\n"; 1304 1305 // Get the updated value here already to do less coding afterwards 1306 int val = 0; 1307 switch (type) { 1308 case ValueWidth: 1309 val = value_w->value(); 1310 break; 1311 case ValueHeight: 1312 val = value_h->value(); 1313 break; 1314 case ValueX: 1315 val = value_x->value(); 1316 break; 1317 case ValueY: 1318 val = value_y->value(); 1319 break; 1320 } 1321 1322 for (int k = 0; k < l.size(); ++k) { 1323 // qCDebug(KDENLIVE_LOG) << "Type of item " << k << ": " << l.at(k)->type() << '\n'; 1324 1325 if (l.at(k)->type() == TEXTITEM) { 1326 // Just update the position. We don't allow setting width/height for text items yet. 1327 switch (type) { 1328 case ValueX: 1329 updatePosition(l.at(k), val, int(l.at(k)->pos().y())); 1330 break; 1331 case ValueY: 1332 updatePosition(l.at(k), int(l.at(k)->pos().x()), val); 1333 break; 1334 } 1335 1336 } else if (l.at(k)->type() == RECTITEM) { 1337 auto *rec = static_cast<QGraphicsRectItem *>(l.at(k)); 1338 switch (type) { 1339 case ValueX: 1340 updatePosition(l.at(k), val, int(l.at(k)->pos().y())); 1341 break; 1342 case ValueY: 1343 updatePosition(l.at(k), int(l.at(k)->pos().x()), val); 1344 break; 1345 case ValueWidth: 1346 rec->setRect(QRect(0, 0, val, int(rec->rect().height()))); 1347 break; 1348 case ValueHeight: 1349 rec->setRect(QRect(0, 0, int(rec->rect().width()), val)); 1350 break; 1351 } 1352 1353 } else if (l.at(k)->type() == ELLIPSEITEM) { 1354 auto *ellipse = static_cast<QGraphicsEllipseItem *>(l.at(k)); 1355 switch (type) { 1356 case ValueX: 1357 updatePosition(l.at(k), val, int(l.at(k)->pos().y())); 1358 break; 1359 case ValueY: 1360 updatePosition(l.at(k), int(l.at(k)->pos().x()), val); 1361 break; 1362 case ValueWidth: 1363 ellipse->setRect(QRect(0, 0, val, int(ellipse->rect().height()))); 1364 break; 1365 case ValueHeight: 1366 ellipse->setRect(QRect(0, 0, int(ellipse->rect().width()), val)); 1367 break; 1368 } 1369 1370 } else if (l.at(k)->type() == IMAGEITEM) { 1371 1372 if (type == ValueX) { 1373 updatePosition(l.at(k), val, int(l.at(k)->pos().y())); 1374 1375 } else if (type == ValueY) { 1376 updatePosition(l.at(k), int(l.at(k)->pos().x()), val); 1377 1378 } else { 1379 // Width/height has changed. This is more complex. 1380 1381 QGraphicsItem *i = l.at(k); 1382 Transform t = m_transformations.value(i); 1383 1384 // Ratio width:height 1385 double phi = i->boundingRect().width() / i->boundingRect().height(); 1386 // TODO: proper calculation for rotation around 3 axes 1387 double alpha = t.rotatez / 180.0 * M_PI; 1388 1389 // New length 1390 double length; 1391 1392 // Scaling factor 1393 double scalex = t.scalex; 1394 double scaley = t.scaley; 1395 1396 // We want to keep the aspect ratio of the image as the user does not yet have the possibility 1397 // to restore the original ratio. You rarely want to change it anyway. 1398 switch (type) { 1399 case ValueWidth: 1400 // Add 0.5 because otherwise incrementing by 1 might have no effect 1401 length = val / (cos(alpha) + 1 / phi * sin(alpha)) + 0.5; 1402 scalex = length / i->boundingRect().width(); 1403 if (preserveAspectRatio->isChecked()) { 1404 scaley = scalex; 1405 } 1406 break; 1407 case ValueHeight: 1408 length = val / (phi * sin(alpha) + cos(alpha)) + 0.5; 1409 scaley = length / i->boundingRect().height(); 1410 if (preserveAspectRatio->isChecked()) { 1411 scalex = scaley; 1412 } 1413 break; 1414 } 1415 t.scalex = scalex; 1416 t.scaley = scaley; 1417 QTransform qtrans; 1418 qtrans.scale(scalex, scaley); 1419 qtrans.rotate(t.rotatex, Qt::XAxis); 1420 qtrans.rotate(t.rotatey, Qt::YAxis); 1421 qtrans.rotate(t.rotatez, Qt::ZAxis); 1422 i->setTransform(qtrans); 1423 // qCDebug(KDENLIVE_LOG) << "scale is: " << scale << '\n'; 1424 // qCDebug(KDENLIVE_LOG) << i->boundingRect().width() << ": new width\n"; 1425 m_transformations[i] = t; 1426 1427 if (l.size() == 1) { 1428 // Only update the w/h values if the selection contains just one item. 1429 // Otherwise, what should we do? ;) 1430 // (Use the values of the first item? Of the second? Of the x-th?) 1431 updateDimension(i); 1432 // Update rotation/zoom values. 1433 // These values are not yet able to handle multiple items! 1434 updateRotZoom(i); 1435 } 1436 } 1437 } 1438 } 1439 } 1440 1441 void TitleWidget::updateDimension(QGraphicsItem *i) 1442 { 1443 bool wBlocked = value_w->signalsBlocked(); 1444 bool hBlocked = value_h->signalsBlocked(); 1445 bool zBlocked = zValue->signalsBlocked(); 1446 value_w->blockSignals(true); 1447 value_h->blockSignals(true); 1448 zValue->blockSignals(true); 1449 1450 zValue->setValue(int(i->zValue())); 1451 if (i->type() == IMAGEITEM) { 1452 // Get multipliers for rotation/scaling 1453 1454 /*Transform t = m_transformations.value(i); 1455 QRectF r = i->boundingRect(); 1456 int width = (int) ( abs(r.width()*t.scalex * cos(t.rotate/180.0*M_PI)) 1457 + abs(r.height()*t.scaley * sin(t.rotate/180.0*M_PI)) ); 1458 int height = (int) ( abs(r.height()*t.scaley * cos(t.rotate/180*M_PI)) 1459 + abs(r.width()*t.scalex * sin(t.rotate/180*M_PI)) );*/ 1460 1461 value_w->setValue(int(i->sceneBoundingRect().width())); 1462 value_h->setValue(int(i->sceneBoundingRect().height())); 1463 } else if (i->type() == RECTITEM || i->type() == ELLIPSEITEM) { 1464 auto *r = static_cast<QGraphicsRectItem *>(i); 1465 // qCDebug(KDENLIVE_LOG) << "Rect width is: " << r->rect().width() << ", was: " << value_w->value() << '\n'; 1466 value_w->setValue(int(r->rect().width())); 1467 value_h->setValue(int(r->rect().height())); 1468 } else if (i->type() == TEXTITEM) { 1469 auto *t = static_cast<MyTextItem *>(i); 1470 value_w->setValue(int(t->boundingRect().width())); 1471 value_h->setValue(int(t->boundingRect().height())); 1472 } 1473 1474 zValue->blockSignals(zBlocked); 1475 value_w->blockSignals(wBlocked); 1476 value_h->blockSignals(hBlocked); 1477 } 1478 1479 void TitleWidget::updateCoordinates(QGraphicsItem *i) 1480 { 1481 // Block signals emitted by this method 1482 value_x->blockSignals(true); 1483 value_y->blockSignals(true); 1484 1485 if (i->type() == TEXTITEM) { 1486 1487 auto *rec = static_cast<MyTextItem *>(i); 1488 1489 // Set the correct x coordinate value 1490 if (origin_x_left->isChecked()) { 1491 // Origin (0 point) is at m_frameWidth, coordinate axis is inverted 1492 value_x->setValue(int(m_frameWidth - rec->pos().x() - rec->boundingRect().width())); 1493 } else { 1494 // Origin is at 0 (default) 1495 value_x->setValue(int(rec->pos().x())); 1496 } 1497 1498 // Same for y 1499 if (origin_y_top->isChecked()) { 1500 value_y->setValue(int(m_frameHeight - rec->pos().y() - rec->boundingRect().height())); 1501 } else { 1502 value_y->setValue(int(rec->pos().y())); 1503 } 1504 1505 } else if (i->type() == RECTITEM) { 1506 1507 auto *rec = static_cast<QGraphicsRectItem *>(i); 1508 1509 if (origin_x_left->isChecked()) { 1510 // Origin (0 point) is at m_frameWidth 1511 value_x->setValue(int(m_frameWidth - rec->pos().x() - rec->rect().width())); 1512 } else { 1513 // Origin is at 0 (default) 1514 value_x->setValue(int(rec->pos().x())); 1515 } 1516 1517 if (origin_y_top->isChecked()) { 1518 value_y->setValue(int(m_frameHeight - rec->pos().y() - rec->rect().height())); 1519 } else { 1520 value_y->setValue(int(rec->pos().y())); 1521 } 1522 1523 } else if (i->type() == ELLIPSEITEM) { 1524 1525 auto *rec = static_cast<QGraphicsEllipseItem *>(i); 1526 1527 if (origin_x_left->isChecked()) { 1528 // Origin (0 point) is at m_frameWidth 1529 value_x->setValue(int(m_frameWidth - rec->pos().x() - rec->rect().width())); 1530 } else { 1531 // Origin is at 0 (default) 1532 value_x->setValue(int(rec->pos().x())); 1533 } 1534 1535 if (origin_y_top->isChecked()) { 1536 value_y->setValue(int(m_frameHeight - rec->pos().y() - rec->rect().height())); 1537 } else { 1538 value_y->setValue(int(rec->pos().y())); 1539 } 1540 1541 } else if (i->type() == IMAGEITEM) { 1542 1543 if (origin_x_left->isChecked()) { 1544 value_x->setValue(int(m_frameWidth - i->pos().x() - i->sceneBoundingRect().width())); 1545 } else { 1546 value_x->setValue(int(i->pos().x())); 1547 } 1548 1549 if (origin_y_top->isChecked()) { 1550 value_y->setValue(int(m_frameHeight - i->pos().y() - i->sceneBoundingRect().height())); 1551 } else { 1552 value_y->setValue(int(i->pos().y())); 1553 } 1554 } 1555 1556 // Stop blocking signals now 1557 value_x->blockSignals(false); 1558 value_y->blockSignals(false); 1559 } 1560 1561 void TitleWidget::updateRotZoom(QGraphicsItem *i) 1562 { 1563 itemzoom->blockSignals(true); 1564 itemrotatex->blockSignals(true); 1565 itemrotatey->blockSignals(true); 1566 itemrotatez->blockSignals(true); 1567 1568 Transform t = m_transformations.value(i); 1569 1570 if (!i->data(TitleDocument::ZoomFactor).isNull()) { 1571 itemzoom->setValue(i->data(TitleDocument::ZoomFactor).toInt()); 1572 } else { 1573 itemzoom->setValue(qRound(t.scalex * 100.0)); 1574 } 1575 1576 itemrotatex->setValue(int(t.rotatex)); 1577 itemrotatey->setValue(int(t.rotatey)); 1578 itemrotatez->setValue(int(t.rotatez)); 1579 1580 itemzoom->blockSignals(false); 1581 itemrotatex->blockSignals(false); 1582 itemrotatey->blockSignals(false); 1583 itemrotatez->blockSignals(false); 1584 } 1585 1586 void TitleWidget::updatePosition(QGraphicsItem *i) 1587 { 1588 updatePosition(i, value_x->value(), value_y->value()); 1589 } 1590 1591 void TitleWidget::updatePosition(QGraphicsItem *i, int x, int y) 1592 { 1593 if (i->type() == TEXTITEM) { 1594 auto *rec = static_cast<MyTextItem *>(i); 1595 1596 int posX; 1597 if (origin_x_left->isChecked()) { 1598 /* 1599 * Origin of the X axis is at m_frameWidth, and distance from right 1600 * border of the item to the right border of the frame is taken. See 1601 * comment to slotOriginXClicked(). 1602 */ 1603 posX = m_frameWidth - x - int(rec->boundingRect().width()); 1604 } else { 1605 posX = x; 1606 } 1607 1608 int posY; 1609 if (origin_y_top->isChecked()) { 1610 /* Same for y axis */ 1611 posY = m_frameHeight - y - int(rec->boundingRect().height()); 1612 } else { 1613 posY = y; 1614 } 1615 1616 rec->setPos(posX, posY); 1617 1618 } else if (i->type() == RECTITEM) { 1619 1620 auto *rec = static_cast<QGraphicsRectItem *>(i); 1621 1622 int posX; 1623 if (origin_x_left->isChecked()) { 1624 posX = m_frameWidth - x - int(rec->rect().width()); 1625 } else { 1626 posX = x; 1627 } 1628 1629 int posY; 1630 if (origin_y_top->isChecked()) { 1631 posY = m_frameHeight - y - int(rec->rect().height()); 1632 } else { 1633 posY = y; 1634 } 1635 1636 rec->setPos(posX, posY); 1637 1638 } else if (i->type() == ELLIPSEITEM) { 1639 1640 auto *rec = static_cast<QGraphicsEllipseItem *>(i); 1641 1642 int posX; 1643 if (origin_x_left->isChecked()) { 1644 posX = m_frameWidth - x - int(rec->rect().width()); 1645 } else { 1646 posX = x; 1647 } 1648 1649 int posY; 1650 if (origin_y_top->isChecked()) { 1651 posY = m_frameHeight - y - int(rec->rect().height()); 1652 } else { 1653 posY = y; 1654 } 1655 1656 rec->setPos(posX, posY); 1657 1658 } else if (i->type() == IMAGEITEM) { 1659 int posX; 1660 if (origin_x_left->isChecked()) { 1661 // Use the sceneBoundingRect because this also regards transformations like zoom 1662 posX = m_frameWidth - x - int(i->sceneBoundingRect().width()); 1663 } else { 1664 posX = x; 1665 } 1666 1667 int posY; 1668 if (origin_y_top->isChecked()) { 1669 posY = m_frameHeight - y - int(i->sceneBoundingRect().height()); 1670 } else { 1671 posY = y; 1672 } 1673 1674 i->setPos(posX, posY); 1675 } 1676 } 1677 1678 void TitleWidget::updateTextOriginX() 1679 { 1680 if (origin_x_left->isChecked()) { 1681 origin_x_left->setText(i18n("\u2212X")); 1682 } else { 1683 origin_x_left->setText(i18n("+X")); 1684 } 1685 } 1686 1687 void TitleWidget::slotOriginXClicked() 1688 { 1689 // Update the text displayed on the button. 1690 updateTextOriginX(); 1691 1692 QList<QGraphicsItem *> l = graphicsView->scene()->selectedItems(); 1693 if (l.size() >= 1) { 1694 updateCoordinates(l.at(0)); 1695 1696 // Remember x axis setting 1697 l.at(0)->setData(TitleDocument::OriginXLeft, origin_x_left->isChecked() ? TitleDocument::AxisInverted : TitleDocument::AxisDefault); 1698 } 1699 graphicsView->setFocus(); 1700 } 1701 1702 void TitleWidget::updateTextOriginY() 1703 { 1704 if (origin_y_top->isChecked()) { 1705 origin_y_top->setText(i18n("\u2212Y")); 1706 } else { 1707 origin_y_top->setText(i18n("+Y")); 1708 } 1709 } 1710 1711 void TitleWidget::slotOriginYClicked() 1712 { 1713 // Update the text displayed on the button. 1714 updateTextOriginY(); 1715 1716 QList<QGraphicsItem *> l = graphicsView->scene()->selectedItems(); 1717 if (l.size() >= 1) { 1718 updateCoordinates(l.at(0)); 1719 1720 l.at(0)->setData(TitleDocument::OriginYTop, origin_y_top->isChecked() ? TitleDocument::AxisInverted : TitleDocument::AxisDefault); 1721 } 1722 graphicsView->setFocus(); 1723 } 1724 1725 void TitleWidget::updateAxisButtons(QGraphicsItem *i) 1726 { 1727 int xAxis = i->data(TitleDocument::OriginXLeft).toInt(); 1728 int yAxis = i->data(TitleDocument::OriginYTop).toInt(); 1729 origin_x_left->blockSignals(true); 1730 origin_y_top->blockSignals(true); 1731 1732 if (xAxis == TitleDocument::AxisInverted) { 1733 origin_x_left->setChecked(true); 1734 } else { 1735 origin_x_left->setChecked(false); 1736 } 1737 updateTextOriginX(); 1738 1739 if (yAxis == TitleDocument::AxisInverted) { 1740 origin_y_top->setChecked(true); 1741 } else { 1742 origin_y_top->setChecked(false); 1743 } 1744 updateTextOriginY(); 1745 1746 origin_x_left->blockSignals(false); 1747 origin_y_top->blockSignals(false); 1748 } 1749 1750 void TitleWidget::slotChangeBackground() 1751 { 1752 QColor color = backgroundColor->color(); 1753 m_scene->setBackgroundBrush(QBrush(color)); 1754 color.setAlpha(backgroundAlpha->value()); 1755 m_frameBackground->setBrush(QBrush(color)); 1756 } 1757 1758 void TitleWidget::slotChanged() 1759 { 1760 QList<QGraphicsItem *> l = graphicsView->scene()->selectedItems(); 1761 if (l.size() >= 1 && l.at(0)->type() == TEXTITEM) { 1762 textChanged(static_cast<MyTextItem *>(l.at(0))); 1763 } 1764 } 1765 1766 void TitleWidget::textChanged(MyTextItem *i) 1767 { 1768 /* 1769 * If the user has set origin_x_left (the same for y), we need to look 1770 * whether a text element has been selected. If yes, we need to ensure that 1771 * the right border of the text field remains fixed also when some text has 1772 * been entered. 1773 * 1774 * This is also known as right-justified, with the difference that it is not 1775 * valid for text but for its boundingRect. Text may still be 1776 * left-justified. 1777 */ 1778 updateDimension(i); 1779 1780 if (origin_x_left->isChecked() || origin_y_top->isChecked()) { 1781 if (!i->document()->isEmpty()) { 1782 updatePosition(i); 1783 } else { 1784 /* 1785 * Don't do anything if the string is empty. If the position were 1786 * updated here, a newly created text field would be set to the 1787 * position of the last selected text field. 1788 */ 1789 } 1790 } 1791 1792 // mbt 1607: Template text has changed; don't auto-select content anymore. 1793 if (i->property("isTemplate").isValid()) { 1794 if (i->property("templateText").isValid()) { 1795 if (i->property("templateText") == i->toHtml()) { 1796 // Unchanged, do nothing. 1797 } else { 1798 i->setProperty("isTemplate", QVariant::Invalid); 1799 i->setProperty("templateText", QVariant::Invalid); 1800 } 1801 } 1802 } 1803 } 1804 1805 void TitleWidget::slotInsertUnicode() 1806 { 1807 m_unicodeDialog->exec(); 1808 } 1809 1810 void TitleWidget::slotInsertUnicodeString(const QString &string) 1811 { 1812 const QList<QGraphicsItem *> l = graphicsView->scene()->selectedItems(); 1813 if (!l.isEmpty()) { 1814 if (l.at(0)->type() == TEXTITEM) { 1815 auto *t = static_cast<MyTextItem *>(l.at(0)); 1816 t->textCursor().insertText(string); 1817 } 1818 } 1819 } 1820 1821 void TitleWidget::slotUpdateText() 1822 { 1823 QFont font = font_family->currentFont(); 1824 QString selected = font.family(); 1825 if (!QFontDatabase().families().contains(selected)) { 1826 QSignalBlocker bk(font_family); 1827 font = QFontDatabase::systemFont(QFontDatabase::GeneralFont); 1828 font_family->setCurrentFont(font); 1829 } 1830 font.setPixelSize(font_size->value()); 1831 font.setItalic(buttonItalic->isChecked()); 1832 font.setUnderline(buttonUnder->isChecked()); 1833 font.setWeight(QFont::Weight(font_weight_box->itemData(font_weight_box->currentIndex()).toInt())); 1834 if (letter_spacing->value() != 0) { 1835 font.setLetterSpacing(QFont::AbsoluteSpacing, letter_spacing->value()); 1836 } 1837 QColor color = fontColorButton->color(); 1838 QColor outlineColor = textOutlineColor->color(); 1839 QString gradientData; 1840 if (gradient_color->isChecked()) { 1841 // user wants a gradient 1842 gradientData = gradients_combo->currentData().toString(); 1843 } 1844 1845 double outlineWidth = textOutline->value(); 1846 1847 int i; 1848 QList<QGraphicsItem *> l = graphicsView->scene()->selectedItems(); 1849 for (i = 0; i < l.length(); ++i) { 1850 MyTextItem *item = nullptr; 1851 if (l.at(i)->type() == TEXTITEM) { 1852 item = static_cast<MyTextItem *>(l.at(i)); 1853 } 1854 if (!item) { 1855 // No text item, try next one. 1856 continue; 1857 } 1858 1859 // Set alignment of all text in the text item 1860 QTextCursor cur(item->document()); 1861 cur.select(QTextCursor::Document); 1862 QTextBlockFormat format = cur.blockFormat(); 1863 item->setData(TitleDocument::LineSpacing, line_spacing->value()); 1864 format.setLineHeight(line_spacing->value(), QTextBlockFormat::LineDistanceHeight); 1865 if (buttonAlignLeft->isChecked() || buttonAlignCenter->isChecked() || buttonAlignRight->isChecked()) { 1866 if (buttonAlignCenter->isChecked()) { 1867 item->setAlignment(Qt::AlignHCenter); 1868 } else if (buttonAlignRight->isChecked()) { 1869 item->setAlignment(Qt::AlignRight); 1870 } else if (buttonAlignLeft->isChecked()) { 1871 item->setAlignment(Qt::AlignLeft); 1872 } 1873 } else { 1874 item->setAlignment(qApp->isLeftToRight() ? Qt::AlignRight : Qt::AlignLeft); 1875 } 1876 1877 // Set font properties 1878 item->setFont(font); 1879 QTextCharFormat cformat = cur.charFormat(); 1880 1881 item->setData(TitleDocument::OutlineWidth, outlineWidth); 1882 item->setData(TitleDocument::OutlineColor, outlineColor); 1883 if (outlineWidth > 0.0) { 1884 cformat.setTextOutline(QPen(outlineColor, outlineWidth, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin)); 1885 } 1886 1887 if (gradientData.isEmpty()) { 1888 cformat.setForeground(QBrush(color)); 1889 } else { 1890 QLinearGradient gr = GradientWidget::gradientFromString(gradientData, int(item->boundingRect().width()), int(item->boundingRect().height())); 1891 cformat.setForeground(QBrush(gr)); 1892 } 1893 // Store gradient in item properties 1894 item->setData(TitleDocument::Gradient, gradientData); 1895 cur.setCharFormat(cformat); 1896 cur.setBlockFormat(format); 1897 // item->setTextCursor(cur); 1898 cur.clearSelection(); 1899 item->setTextCursor(cur); 1900 item->setTextColor(color); 1901 } 1902 } 1903 1904 void TitleWidget::rectChanged() 1905 { 1906 QList<QGraphicsItem *> l = graphicsView->scene()->selectedItems(); 1907 for (auto i : qAsConst(l)) { 1908 if (i->type() == RECTITEM && (settingUp == 0)) { 1909 auto *rec = static_cast<QGraphicsRectItem *>(i); 1910 QColor f = rectFColor->color(); 1911 if (rectLineWidth->value() == 0) { 1912 rec->setPen(Qt::NoPen); 1913 } else { 1914 QPen penf(f); 1915 penf.setWidth(rectLineWidth->value()); 1916 penf.setJoinStyle(Qt::RoundJoin); 1917 rec->setPen(penf); 1918 } 1919 if (plain_rect->isChecked()) { 1920 rec->setBrush(QBrush(rectBColor->color())); 1921 rec->setData(TitleDocument::Gradient, QVariant()); 1922 } else { 1923 // gradient 1924 QString gradientData = gradients_rect_combo->currentData().toString(); 1925 rec->setData(TitleDocument::Gradient, gradientData); 1926 QLinearGradient gr = GradientWidget::gradientFromString(gradientData, int(rec->boundingRect().width()), int(rec->boundingRect().height())); 1927 rec->setBrush(QBrush(gr)); 1928 } 1929 } else if (i->type() == ELLIPSEITEM && (settingUp == 0)) { 1930 auto *ellipse = static_cast<QGraphicsEllipseItem *>(i); 1931 QColor f = rectFColor->color(); 1932 if (rectLineWidth->value() == 0) { 1933 ellipse->setPen(Qt::NoPen); 1934 } else { 1935 QPen penf(f); 1936 penf.setWidth(rectLineWidth->value()); 1937 penf.setJoinStyle(Qt::RoundJoin); 1938 ellipse->setPen(penf); 1939 } 1940 if (plain_rect->isChecked()) { 1941 ellipse->setBrush(QBrush(rectBColor->color())); 1942 ellipse->setData(TitleDocument::Gradient, QVariant()); 1943 } else { 1944 // gradient 1945 QString gradientData = gradients_rect_combo->currentData().toString(); 1946 ellipse->setData(TitleDocument::Gradient, gradientData); 1947 QLinearGradient gr = 1948 GradientWidget::gradientFromString(gradientData, int(ellipse->boundingRect().width()), int(ellipse->boundingRect().height())); 1949 ellipse->setBrush(QBrush(gr)); 1950 } 1951 } 1952 } 1953 } 1954 1955 void TitleWidget::itemScaled(int val) 1956 { 1957 QList<QGraphicsItem *> l = graphicsView->scene()->selectedItems(); 1958 if (l.size() == 1) { 1959 Transform x = m_transformations.value(l.at(0)); 1960 x.scalex = val / 100.0; 1961 x.scaley = val / 100.0; 1962 QTransform qtrans; 1963 qtrans.scale(x.scalex, x.scaley); 1964 qtrans.rotate(x.rotatex, Qt::XAxis); 1965 qtrans.rotate(x.rotatey, Qt::YAxis); 1966 qtrans.rotate(x.rotatez, Qt::ZAxis); 1967 l[0]->setTransform(qtrans); 1968 l[0]->setData(TitleDocument::ZoomFactor, val); 1969 m_transformations[l.at(0)] = x; 1970 updateDimension(l.at(0)); 1971 } 1972 } 1973 1974 void TitleWidget::itemRotateX(int val) 1975 { 1976 itemRotate(val, 0); 1977 } 1978 1979 void TitleWidget::itemRotateY(int val) 1980 { 1981 itemRotate(val, 1); 1982 } 1983 1984 void TitleWidget::itemRotateZ(int val) 1985 { 1986 itemRotate(val, 2); 1987 } 1988 1989 void TitleWidget::itemRotate(int val, int axis) 1990 { 1991 QList<QGraphicsItem *> l = graphicsView->scene()->selectedItems(); 1992 if (l.size() == 1) { 1993 Transform x = m_transformations[l.at(0)]; 1994 switch (axis) { 1995 case 0: 1996 x.rotatex = val; 1997 break; 1998 case 1: 1999 x.rotatey = val; 2000 break; 2001 case 2: 2002 x.rotatez = val; 2003 break; 2004 } 2005 2006 l[0]->setData(TitleDocument::RotateFactor, QList<QVariant>() << QVariant(x.rotatex) << QVariant(x.rotatey) << QVariant(x.rotatez)); 2007 2008 QTransform qtrans; 2009 qtrans.scale(x.scalex, x.scaley); 2010 qtrans.rotate(x.rotatex, Qt::XAxis); 2011 qtrans.rotate(x.rotatey, Qt::YAxis); 2012 qtrans.rotate(x.rotatez, Qt::ZAxis); 2013 l[0]->setTransform(qtrans); 2014 m_transformations[l.at(0)] = x; 2015 if (l[0]->data(TitleDocument::ZoomFactor).isNull()) { 2016 l[0]->setData(TitleDocument::ZoomFactor, 100); 2017 } 2018 updateDimension(l.at(0)); 2019 } 2020 } 2021 2022 void TitleWidget::itemHCenter() 2023 { 2024 QList<QGraphicsItem *> l = graphicsView->scene()->selectedItems(); 2025 if (l.size() == 1) { 2026 QGraphicsItem *item = l.at(0); 2027 QRectF br = item->sceneBoundingRect(); 2028 int width = int(br.width()); 2029 int newPos = (m_frameWidth - width) / 2; 2030 newPos += int(item->pos().x() - br.left()); // Check item transformation 2031 item->setPos(newPos, item->pos().y()); 2032 updateCoordinates(item); 2033 slotAdjustZoom(); 2034 graphicsView->centerOn(m_frameBorder); 2035 slotAdjustZoom(); 2036 graphicsView->centerOn(m_frameBorder); 2037 } 2038 } 2039 2040 void TitleWidget::itemVCenter() 2041 { 2042 QList<QGraphicsItem *> l = graphicsView->scene()->selectedItems(); 2043 if (l.size() == 1) { 2044 QGraphicsItem *item = l.at(0); 2045 QRectF br = item->sceneBoundingRect(); 2046 int height = int(br.height()); 2047 int newPos = (m_frameHeight - height) / 2; 2048 newPos += int(item->pos().y() - br.top()); // Check item transformation 2049 item->setPos(item->pos().x(), newPos); 2050 updateCoordinates(item); 2051 } 2052 } 2053 2054 void TitleWidget::itemTop() 2055 { 2056 QList<QGraphicsItem *> l = graphicsView->scene()->selectedItems(); 2057 if (l.size() == 1) { 2058 QList<double> margins{m_frameHeight * 0.05, m_frameHeight * 0.1}; 2059 QGraphicsItem *item = l.at(0); 2060 QRectF br = item->sceneBoundingRect(); 2061 double diff; 2062 if (br.top() < 0.) { 2063 // align with big margin 2064 diff = margins.at(1) - br.top(); 2065 } else if (qFuzzyIsNull(br.top())) { 2066 // align right with frame border 2067 diff = -br.bottom(); 2068 } else if (br.top() <= margins.at(0)) { 2069 // align with 0 2070 diff = -br.top(); 2071 } else if (br.top() <= margins.at(1)) { 2072 // align with small margin 2073 diff = margins.at(0) - br.top(); 2074 } else { 2075 // align with big margin 2076 diff = margins.at(1) - br.top(); 2077 } 2078 item->moveBy(0, diff); 2079 updateCoordinates(item); 2080 } 2081 } 2082 2083 void TitleWidget::itemBottom() 2084 { 2085 QList<QGraphicsItem *> l = graphicsView->scene()->selectedItems(); 2086 if (l.size() == 1) { 2087 QList<double> margins{m_frameHeight * 0.9, m_frameHeight * 0.95}; 2088 QGraphicsItem *item = l.at(0); 2089 QRectF br = item->sceneBoundingRect(); 2090 double diff; 2091 if (br.bottom() < margins.at(0)) { 2092 // align with small margin 2093 diff = margins.at(0) - br.bottom(); 2094 } else if (br.bottom() < margins.at(1)) { 2095 // align big margin 2096 diff = margins.at(1) - br.bottom(); 2097 } else if (br.bottom() < m_frameHeight) { 2098 // align with frame 2099 diff = m_frameHeight - br.bottom(); 2100 } else if (br.top() < m_frameHeight) { 2101 // align left with frame 2102 diff = m_frameHeight - br.top(); 2103 } else { 2104 // align with big margin 2105 diff = margins.at(0) - br.bottom(); 2106 } 2107 item->moveBy(0, diff); 2108 updateCoordinates(item); 2109 } 2110 } 2111 2112 void TitleWidget::itemLeft() 2113 { 2114 QList<QGraphicsItem *> l = graphicsView->scene()->selectedItems(); 2115 if (l.size() == 1) { 2116 QList<double> margins{m_frameWidth * 0.05, m_frameWidth * 0.1}; 2117 QGraphicsItem *item = l.at(0); 2118 QRectF br = item->sceneBoundingRect(); 2119 double diff; 2120 if (br.left() < 0.) { 2121 // align with big margin 2122 diff = margins.at(1) - br.left(); 2123 } else if (qFuzzyIsNull(br.left())) { 2124 // align right with frame border 2125 diff = -br.right(); 2126 } else if (br.left() <= margins.at(0)) { 2127 // align with 0 2128 diff = -br.left(); 2129 } else if (br.left() <= margins.at(1)) { 2130 // align with small margin 2131 diff = margins.at(0) - br.left(); 2132 } else { 2133 // align with big margin 2134 diff = margins.at(1) - br.left(); 2135 } 2136 item->moveBy(diff, 0); 2137 updateCoordinates(item); 2138 } 2139 } 2140 2141 void TitleWidget::itemRight() 2142 { 2143 QList<QGraphicsItem *> l = graphicsView->scene()->selectedItems(); 2144 if (l.size() == 1) { 2145 QList<double> margins{m_frameWidth * 0.9, m_frameWidth * 0.95}; 2146 QGraphicsItem *item = l.at(0); 2147 QRectF br = item->sceneBoundingRect(); 2148 double diff; 2149 if (br.right() < margins.at(0)) { 2150 // align with small margin 2151 diff = margins.at(0) - br.right(); 2152 } else if (br.right() < margins.at(1)) { 2153 // align big margin 2154 diff = margins.at(1) - br.right(); 2155 } else if (br.right() < m_frameWidth) { 2156 // align with frame 2157 diff = m_frameWidth - br.right(); 2158 } else if (br.left() < m_frameWidth) { 2159 // align left with frame 2160 diff = m_frameWidth - br.left(); 2161 } else { 2162 // align with big margin 2163 diff = margins.at(0) - br.right(); 2164 } 2165 item->moveBy(diff, 0); 2166 updateCoordinates(item); 2167 } 2168 } 2169 2170 void TitleWidget::loadTitle(QUrl url) 2171 { 2172 if (!url.isValid()) { 2173 QString startFolder = KRecentDirs::dir(QStringLiteral(":KdenliveProjectsTitles")); 2174 url = QFileDialog::getOpenFileUrl(this, i18n("Load Title"), QUrl::fromLocalFile(startFolder.isEmpty() ? m_projectTitlePath : startFolder), 2175 i18n("Kdenlive title") + QStringLiteral(" (*.kdenlivetitle)")); 2176 } 2177 if (url.isValid()) { 2178 if (anim_start->isChecked()) { 2179 anim_start->setChecked(false); 2180 } 2181 if (anim_end->isChecked()) { 2182 anim_end->setChecked(false); 2183 } 2184 // make sure we don't delete the guides 2185 qDeleteAll(m_guides); 2186 m_guides.clear(); 2187 QList<QGraphicsItem *> items = m_scene->items(); 2188 items.removeAll(m_frameBorder); 2189 items.removeAll(m_frameBackground); 2190 items.removeAll(m_frameImage); 2191 for (auto item : qAsConst(items)) { 2192 if (item->zValue() > -1000) { 2193 delete item; 2194 } 2195 } 2196 m_scene->clearTextSelection(); 2197 QDomDocument doc; 2198 if (!Xml::docContentFromFile(doc, url.toLocalFile(), false)) { 2199 return; 2200 } 2201 setXml(doc); 2202 updateGuides(0); 2203 m_projectTitlePath = QFileInfo(url.toLocalFile()).dir().absolutePath(); 2204 KRecentDirs::add(QStringLiteral(":KdenliveProjectsTitles"), m_projectTitlePath); 2205 } 2206 } 2207 2208 QUrl TitleWidget::saveTitle(QUrl url) 2209 { 2210 if (anim_start->isChecked()) { 2211 slotAnimStart(false); 2212 } 2213 if (anim_end->isChecked()) { 2214 slotAnimEnd(false); 2215 } 2216 // If we have images in the title, ask for embed 2217 QList<QGraphicsItem *> list = graphicsView->scene()->items(); 2218 auto is_embedable = [&](QGraphicsItem *item) { return item->type() == QGraphicsPixmapItem::Type && item != m_frameImage; }; 2219 bool embed_image = std::any_of(list.begin(), list.end(), is_embedable); 2220 if (embed_image && 2221 KMessageBox::questionTwoActions(this, i18n("Do you want to embed Images into this TitleDocument?\nThis is most needed for sharing Titles."), {}, 2222 KGuiItem(i18nc("@action:button", "Embed Images")), 2223 KGuiItem(i18nc("@action:button", "Continue without"))) != KMessageBox::PrimaryAction) { 2224 embed_image = false; 2225 } 2226 if (!url.isValid()) { 2227 QPointer<QFileDialog> fs = new QFileDialog(this, i18n("Save Title"), m_projectTitlePath); 2228 fs->setMimeTypeFilters(QStringList() << QStringLiteral("application/x-kdenlivetitle")); 2229 fs->setFileMode(QFileDialog::AnyFile); 2230 fs->setAcceptMode(QFileDialog::AcceptSave); 2231 fs->setDefaultSuffix(QStringLiteral("kdenlivetitle")); 2232 2233 if ((fs->exec() != 0) && !fs->selectedUrls().isEmpty()) { 2234 url = fs->selectedUrls().constFirst(); 2235 } 2236 delete fs; 2237 } 2238 if (url.isValid()) { 2239 if (!m_titledocument.saveDocument(url, m_startViewport, m_endViewport, m_duration->getValue(), embed_image)) { 2240 KMessageBox::error(this, i18n("Cannot write to file %1", url.toLocalFile())); 2241 } else { 2242 return url; 2243 } 2244 } 2245 return QUrl(); 2246 } 2247 2248 QDomDocument TitleWidget::xml() 2249 { 2250 QDomDocument doc = m_titledocument.xml(m_startViewport, m_endViewport); 2251 int duration = m_duration->getValue(); 2252 doc.documentElement().setAttribute(QStringLiteral("duration"), duration); 2253 doc.documentElement().setAttribute(QStringLiteral("out"), duration - 1); 2254 return doc; 2255 } 2256 2257 int TitleWidget::duration() const 2258 { 2259 return m_duration->getValue(); 2260 } 2261 2262 void TitleWidget::setXml(const QDomDocument &doc, const QString &id) 2263 { 2264 m_clipId = id; 2265 int duration; 2266 if (m_missingMessage) { 2267 delete m_missingMessage; 2268 m_missingMessage = nullptr; 2269 } 2270 m_count = m_titledocument.loadFromXml(doc, m_scene, m_startViewport, m_endViewport, &duration, m_projectTitlePath); 2271 adjustFrameSize(); 2272 if (m_titledocument.invalidCount() > 0) { 2273 m_missingMessage = new KMessageWidget(this); 2274 m_missingMessage->setCloseButtonVisible(true); 2275 m_missingMessage->setWordWrap(true); 2276 m_missingMessage->setMessageType(KMessageWidget::Warning); 2277 m_missingMessage->setText(i18np("This title has 1 missing element", "This title has %1 missing elements", m_titledocument.invalidCount())); 2278 QAction *action = new QAction(i18n("Details")); 2279 m_missingMessage->addAction(action); 2280 connect(action, &QAction::triggered, this, &TitleWidget::showMissingItems); 2281 action = new QAction(i18n("Delete missing elements")); 2282 m_missingMessage->addAction(action); 2283 connect(action, &QAction::triggered, this, &TitleWidget::deleteMissingItems); 2284 messageLayout->addWidget(m_missingMessage); 2285 m_missingMessage->animatedShow(); 2286 } 2287 m_duration->setValue(GenTime(duration, m_fps)); 2288 2289 QDomElement e = doc.documentElement(); 2290 m_transformations.clear(); 2291 QList<QGraphicsItem *> items = graphicsView->scene()->items(); 2292 const double PI = 4.0 * atan(1.0); 2293 for (int i = 0; i < items.count(); ++i) { 2294 QTransform t = items.at(i)->transform(); 2295 Transform x; 2296 x.scalex = t.m11(); 2297 x.scaley = t.m22(); 2298 if (!items.at(i)->data(TitleDocument::RotateFactor).isNull()) { 2299 QList<QVariant> rotlist = items.at(i)->data(TitleDocument::RotateFactor).toList(); 2300 if (rotlist.count() >= 3) { 2301 x.rotatex = rotlist[0].toInt(); 2302 x.rotatey = rotlist[1].toInt(); 2303 x.rotatez = rotlist[2].toInt(); 2304 2305 // Try to adjust zoom 2306 t.rotate(x.rotatex * (-1), Qt::XAxis); 2307 t.rotate(x.rotatey * (-1), Qt::YAxis); 2308 t.rotate(x.rotatez * (-1), Qt::ZAxis); 2309 x.scalex = t.m11(); 2310 x.scaley = t.m22(); 2311 } else { 2312 x.rotatex = 0; 2313 x.rotatey = 0; 2314 x.rotatez = 0; 2315 } 2316 } else { 2317 x.rotatex = 0; 2318 x.rotatey = 0; 2319 x.rotatez = int(180. / PI * atan2(-t.m21(), t.m11())); 2320 } 2321 m_transformations[items.at(i)] = x; 2322 } 2323 // mbd: Update the GUI color selectors to match the stuff from the loaded document 2324 QColor background_color = m_titledocument.getBackgroundColor(); 2325 backgroundAlpha->blockSignals(true); 2326 backgroundColor->blockSignals(true); 2327 backgroundAlpha->setValue(background_color.alpha()); 2328 bgAlphaSlider->setValue(background_color.alpha()); 2329 background_color.setAlpha(255); 2330 backgroundColor->setColor(background_color); 2331 backgroundAlpha->blockSignals(false); 2332 backgroundColor->blockSignals(false); 2333 2334 /*startViewportX->setValue(m_startViewport->data(0).toInt()); 2335 startViewportY->setValue(m_startViewport->data(1).toInt()); 2336 startViewportSize->setValue(m_startViewport->data(2).toInt()); 2337 endViewportX->setValue(m_endViewport->data(0).toInt()); 2338 endViewportY->setValue(m_endViewport->data(1).toInt()); 2339 endViewportSize->setValue(m_endViewport->data(2).toInt());*/ 2340 2341 m_createTitleAction->setText(i18n("Update Title")); 2342 2343 auto *addMenu = new QMenu(this); 2344 addMenu->addAction(i18n("Add as new Title")); 2345 createButton->setMenu(addMenu); 2346 connect(addMenu, &QMenu::triggered, this, [this]() { done(QDialog::Accepted + 1); }); 2347 2348 QTimer::singleShot(200, this, &TitleWidget::slotAdjustZoom); 2349 slotSelectTool(); 2350 selectionChanged(); 2351 } 2352 2353 void TitleWidget::slotAccepted() 2354 { 2355 if (anim_start->isChecked()) { 2356 slotAnimStart(false); 2357 } 2358 if (anim_end->isChecked()) { 2359 slotAnimEnd(false); 2360 } 2361 writeChoices(); 2362 } 2363 2364 void TitleWidget::deleteMissingItems() 2365 { 2366 m_missingMessage->animatedHide(); 2367 QList<QGraphicsItem *> items = graphicsView->scene()->items(); 2368 QList<QGraphicsItem *> toDelete; 2369 for (int i = 0; i < items.count(); ++i) { 2370 if (items.at(i)->data(Qt::UserRole + 2).toInt() == 1) { 2371 // We found a missing item 2372 toDelete << items.at(i); 2373 } 2374 } 2375 if (toDelete.size() != m_titledocument.invalidCount()) { 2376 qDebug() << "/// WARNING, INCOHERENT MISSING ELEMENTS in title: " << toDelete.size() << " != " << m_titledocument.invalidCount(); 2377 } 2378 while (!toDelete.isEmpty()) { 2379 QGraphicsItem *item = toDelete.takeFirst(); 2380 if (m_scene) { 2381 m_scene->removeItem(item); 2382 } 2383 } 2384 m_missingMessage->deleteLater(); 2385 } 2386 2387 void TitleWidget::showMissingItems() 2388 { 2389 QList<QGraphicsItem *> items = graphicsView->scene()->items(); 2390 QStringList missingUrls; 2391 for (int i = 0; i < items.count(); ++i) { 2392 if (items.at(i)->data(Qt::UserRole + 2).toInt() == 1) { 2393 // We found a missing item 2394 missingUrls << items.at(i)->data(Qt::UserRole).toString(); 2395 } 2396 } 2397 missingUrls.removeDuplicates(); 2398 KMessageBox::informationList(QApplication::activeWindow(), i18n("The following files are missing:"), missingUrls); 2399 } 2400 2401 void TitleWidget::writeChoices() 2402 { 2403 // Get a pointer to a shared configuration instance, then get the TitleWidget group. 2404 KSharedConfigPtr config = KSharedConfig::openConfig(); 2405 KConfigGroup titleConfig(config, "TitleWidget"); 2406 // Write the entries 2407 titleConfig.writeEntry("dialog_geometry", saveGeometry().toBase64()); 2408 titleConfig.writeEntry("font_family", font_family->currentFont()); 2409 // titleConfig.writeEntry("font_size", font_size->value()); 2410 titleConfig.writeEntry("font_pixel_size", font_size->value()); 2411 titleConfig.writeEntry("font_color", fontColorButton->color()); 2412 titleConfig.writeEntry("font_outline_color", textOutlineColor->color()); 2413 titleConfig.writeEntry("font_outline", textOutline->value() * 10); 2414 titleConfig.writeEntry("font_weight", font_weight_box->itemData(font_weight_box->currentIndex()).toInt()); 2415 titleConfig.writeEntry("font_italic", buttonItalic->isChecked()); 2416 titleConfig.writeEntry("font_underlined", buttonUnder->isChecked()); 2417 2418 titleConfig.writeEntry("rect_background_color", rectBColor->color()); 2419 titleConfig.writeEntry("rect_foreground_color", rectFColor->color()); 2420 2421 titleConfig.writeEntry("rect_background_alpha", rectBColor->color().alpha()); 2422 titleConfig.writeEntry("rect_foreground_alpha", rectFColor->color().alpha()); 2423 2424 titleConfig.writeEntry("rect_line_width", rectLineWidth->value()); 2425 2426 titleConfig.writeEntry("background_color", backgroundColor->color()); 2427 titleConfig.writeEntry("background_alpha", backgroundAlpha->value()); 2428 2429 titleConfig.writeEntry("use_grid", use_grid->isChecked()); 2430 2431 //! \todo Not sure if I should sync - it is probably safe to do it 2432 config->sync(); 2433 } 2434 2435 void TitleWidget::readChoices() 2436 { 2437 // Get a pointer to a shared configuration instance, then get the TitleWidget group. 2438 KSharedConfigPtr config = KSharedConfig::openConfig(); 2439 KConfigGroup titleConfig(config, "TitleWidget"); 2440 // read the entries 2441 const QByteArray geometry = titleConfig.readEntry("dialog_geometry", QByteArray()); 2442 restoreGeometry(QByteArray::fromBase64(geometry)); 2443 QFont font = titleConfig.readEntry("font_family", font_family->currentFont()); 2444 if (!QFontDatabase().families().contains(font.family())) { 2445 font = QFontDatabase::systemFont(QFontDatabase::GeneralFont); 2446 } 2447 font_family->setCurrentFont(font); 2448 font_size->setValue(titleConfig.readEntry("font_pixel_size", m_frameHeight > 0 ? m_frameHeight / 20 : font_size->value())); 2449 m_scene->slotUpdateFontSize(font_size->value()); 2450 QColor fontColor = QColor(titleConfig.readEntry("font_color", fontColorButton->color())); 2451 QColor outlineColor = QColor(titleConfig.readEntry("font_outline_color", textOutlineColor->color())); 2452 fontColor.setAlpha(titleConfig.readEntry("font_alpha", fontColor.alpha())); 2453 outlineColor.setAlpha(titleConfig.readEntry("font_outline_alpha", outlineColor.alpha())); 2454 fontColorButton->setColor(fontColor); 2455 textOutlineColor->setColor(outlineColor); 2456 textOutline->setValue(titleConfig.readEntry("font_outline", textOutline->value()) / 10.0); 2457 2458 int weight; 2459 if (titleConfig.readEntry("font_bold", false)) { 2460 weight = QFont::Bold; 2461 } else { 2462 weight = titleConfig.readEntry("font_weight", font_weight_box->itemData(font_weight_box->currentIndex()).toInt()); 2463 } 2464 setFontBoxWeight(weight); 2465 buttonItalic->setChecked(titleConfig.readEntry("font_italic", buttonItalic->isChecked())); 2466 buttonUnder->setChecked(titleConfig.readEntry("font_underlined", buttonUnder->isChecked())); 2467 2468 QColor fgColor = QColor(titleConfig.readEntry("rect_foreground_color", rectFColor->color())); 2469 QColor bgColor = QColor(titleConfig.readEntry("rect_background_color", rectBColor->color())); 2470 2471 fgColor.setAlpha(titleConfig.readEntry("rect_foreground_alpha", fgColor.alpha())); 2472 bgColor.setAlpha(titleConfig.readEntry("rect_background_alpha", bgColor.alpha())); 2473 rectFColor->setColor(fgColor); 2474 rectBColor->setColor(bgColor); 2475 2476 rectLineWidth->setValue(titleConfig.readEntry("rect_line_width", rectLineWidth->value())); 2477 2478 backgroundColor->setColor(titleConfig.readEntry("background_color", backgroundColor->color())); 2479 backgroundAlpha->setValue(titleConfig.readEntry("background_alpha", backgroundAlpha->value())); 2480 use_grid->setChecked(titleConfig.readEntry("use_grid", false)); 2481 m_scene->slotUseGrid(use_grid->isChecked()); 2482 } 2483 2484 void TitleWidget::adjustFrameSize() 2485 { 2486 m_frameWidth = m_titledocument.frameWidth(); 2487 m_frameHeight = m_titledocument.frameHeight(); 2488 m_frameBorder->setRect(0, 0, m_frameWidth, m_frameHeight); 2489 displayBackgroundFrame(); 2490 } 2491 2492 void TitleWidget::slotAnimStart(bool anim) 2493 { 2494 if (anim && anim_end->isChecked()) { 2495 anim_end->setChecked(false); 2496 m_endViewport->setZValue(-1000); 2497 m_endViewport->setBrush(QBrush()); 2498 } 2499 slotSelectTool(); 2500 QList<QGraphicsItem *> list = m_scene->items(); 2501 for (int i = 0; i < list.count(); ++i) { 2502 if (list.at(i)->zValue() > -1000) { 2503 if (!list.at(i)->data(-1).isNull()) { 2504 continue; 2505 } 2506 list.at(i)->setFlag(QGraphicsItem::ItemIsMovable, !anim); 2507 list.at(i)->setFlag(QGraphicsItem::ItemIsSelectable, !anim); 2508 } 2509 } 2510 align_box->setEnabled(anim); 2511 itemzoom->setEnabled(!anim); 2512 itemrotatex->setEnabled(!anim); 2513 itemrotatey->setEnabled(!anim); 2514 itemrotatez->setEnabled(!anim); 2515 frame_toolbar->setEnabled(!anim); 2516 toolbar_stack->setEnabled(!anim); 2517 if (anim) { 2518 keep_aspect->setChecked(!m_startViewport->data(0).isNull()); 2519 m_startViewport->setZValue(1100); 2520 QColor col = m_startViewport->pen().color(); 2521 col.setAlpha(100); 2522 m_startViewport->setBrush(col); 2523 m_startViewport->setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable); 2524 m_startViewport->setSelected(true); 2525 selectionChanged(); 2526 slotSelectTool(); 2527 if (m_startViewport->childItems().isEmpty()) { 2528 addAnimInfoText(); 2529 } 2530 } else { 2531 m_startViewport->setZValue(-1000); 2532 m_startViewport->setBrush(QBrush()); 2533 if (!anim_end->isChecked()) { 2534 deleteAnimInfoText(); 2535 } 2536 } 2537 } 2538 2539 void TitleWidget::slotAnimEnd(bool anim) 2540 { 2541 if (anim && anim_start->isChecked()) { 2542 anim_start->setChecked(false); 2543 m_startViewport->setZValue(-1000); 2544 m_startViewport->setBrush(QBrush()); 2545 } 2546 slotSelectTool(); 2547 QList<QGraphicsItem *> list = m_scene->items(); 2548 for (int i = 0; i < list.count(); ++i) { 2549 if (list.at(i)->zValue() > -1000) { 2550 if (!list.at(i)->data(-1).isNull()) { 2551 continue; 2552 } 2553 list.at(i)->setFlag(QGraphicsItem::ItemIsMovable, !anim); 2554 list.at(i)->setFlag(QGraphicsItem::ItemIsSelectable, !anim); 2555 } 2556 } 2557 align_box->setEnabled(anim); 2558 itemzoom->setEnabled(!anim); 2559 itemrotatex->setEnabled(!anim); 2560 itemrotatey->setEnabled(!anim); 2561 itemrotatez->setEnabled(!anim); 2562 frame_toolbar->setEnabled(!anim); 2563 toolbar_stack->setEnabled(!anim); 2564 if (anim) { 2565 keep_aspect->setChecked(!m_endViewport->data(0).isNull()); 2566 m_endViewport->setZValue(1100); 2567 QColor col = m_endViewport->pen().color(); 2568 col.setAlpha(100); 2569 m_endViewport->setBrush(col); 2570 m_endViewport->setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable); 2571 m_endViewport->setSelected(true); 2572 m_startViewport->setSelected(false); 2573 selectionChanged(); 2574 slotSelectTool(); 2575 if (m_endViewport->childItems().isEmpty()) { 2576 addAnimInfoText(); 2577 } 2578 } else { 2579 m_endViewport->setZValue(-1000); 2580 m_endViewport->setBrush(QBrush()); 2581 m_endViewport->setFlag(QGraphicsItem::ItemIsMovable, false); 2582 m_endViewport->setFlag(QGraphicsItem::ItemIsSelectable, false); 2583 if (!anim_start->isChecked()) { 2584 deleteAnimInfoText(); 2585 } 2586 } 2587 } 2588 2589 void TitleWidget::addAnimInfoText() 2590 { 2591 // add text to anim viewport 2592 QGraphicsTextItem *t = new QGraphicsTextItem(i18nc("Indicates the start of an animation", "Start Viewport"), m_startViewport); 2593 QGraphicsTextItem *t2 = new QGraphicsTextItem(i18nc("Indicates the end of an animation", "End Viewport"), m_endViewport); 2594 QFont font = t->font(); 2595 font.setPixelSize(int(m_startViewport->rect().width() / 10)); 2596 QColor col = m_startViewport->pen().color(); 2597 col.setAlpha(255); 2598 t->setDefaultTextColor(col); 2599 t->setFont(font); 2600 font.setPixelSize(int(m_endViewport->rect().width() / 10)); 2601 col = m_endViewport->pen().color(); 2602 col.setAlpha(255); 2603 t2->setDefaultTextColor(col); 2604 t2->setFont(font); 2605 } 2606 2607 void TitleWidget::updateInfoText() 2608 { 2609 // update info text font 2610 if (!m_startViewport->childItems().isEmpty()) { 2611 MyTextItem *item = static_cast<MyTextItem *>(m_startViewport->childItems().at(0)); 2612 if (item) { 2613 QFont font = item->font(); 2614 font.setPixelSize(int(m_startViewport->rect().width() / 10)); 2615 item->setFont(font); 2616 } 2617 } 2618 if (!m_endViewport->childItems().isEmpty()) { 2619 MyTextItem *item = static_cast<MyTextItem *>(m_endViewport->childItems().at(0)); 2620 if (item) { 2621 QFont font = item->font(); 2622 font.setPixelSize(int(m_endViewport->rect().width() / 10)); 2623 item->setFont(font); 2624 } 2625 } 2626 } 2627 2628 void TitleWidget::deleteAnimInfoText() 2629 { 2630 // end animation editing, remove info text 2631 while (!m_startViewport->childItems().isEmpty()) { 2632 QGraphicsItem *item = m_startViewport->childItems().at(0); 2633 if (m_scene) { 2634 m_scene->removeItem(item); 2635 } 2636 } 2637 while (!m_endViewport->childItems().isEmpty()) { 2638 QGraphicsItem *item = m_endViewport->childItems().at(0); 2639 if (m_scene) { 2640 m_scene->removeItem(item); 2641 } 2642 } 2643 } 2644 2645 void TitleWidget::slotKeepAspect(bool keep) 2646 { 2647 if (int(m_endViewport->zValue()) == 1100) { 2648 m_endViewport->setData(0, keep ? m_frameWidth : QVariant()); 2649 m_endViewport->setData(1, keep ? m_frameHeight : QVariant()); 2650 } else { 2651 m_startViewport->setData(0, keep ? m_frameWidth : QVariant()); 2652 m_startViewport->setData(1, keep ? m_frameHeight : QVariant()); 2653 } 2654 } 2655 2656 void TitleWidget::slotResize(int percentSize) 2657 { 2658 int w, h; 2659 if (percentSize < 100) { 2660 w = m_frameWidth / (100 / percentSize); 2661 h = m_frameHeight / (100 / percentSize); 2662 } else { 2663 w = m_frameWidth * (percentSize / 100); 2664 h = m_frameHeight * (percentSize / 100); 2665 } 2666 if (int(m_endViewport->zValue()) == 1100) { 2667 m_endViewport->setRect(0, 0, w, h); 2668 } else { 2669 m_startViewport->setRect(0, 0, w, h); 2670 } 2671 updateInfoText(); 2672 } 2673 2674 void TitleWidget::slotAddEffect(int /*ix*/) 2675 { 2676 /* 2677 QList<QGraphicsItem *> list = graphicsView->scene()->selectedItems(); 2678 int effect = effect_list->itemData(ix).toInt(); 2679 if (list.size() == 1) { 2680 if (effect == NOEFFECT) 2681 effect_stack->setHidden(true); 2682 else { 2683 effect_stack->setCurrentIndex(effect - 1); 2684 effect_stack->setHidden(false); 2685 } 2686 } else // Hide the effects stack when more than one element is selected. 2687 effect_stack->setHidden(true); 2688 for (QGraphicsItem * item : list) { 2689 switch (effect) { 2690 case NOEFFECT: 2691 item->setData(100, QVariant()); 2692 item->setGraphicsEffect(0); 2693 break; 2694 case TYPEWRITEREFFECT: 2695 if (item->type() == TEXTITEM) { 2696 QStringList effdata = QStringList() << QStringLiteral("typewriter") << QString::number(typewriter_delay->value()) + QLatin1Char(';') + 2697 QString::number(typewriter_start->value()); 2698 item->setData(100, effdata); 2699 } 2700 break; 2701 // Do not remove the non-QGraphicsEffects. 2702 case BLUREFFECT: 2703 item->setGraphicsEffect(new QGraphicsBlurEffect()); 2704 break; 2705 case SHADOWEFFECT: 2706 item->setGraphicsEffect(new QGraphicsDropShadowEffect()); 2707 break; 2708 } 2709 }*/ 2710 } 2711 2712 qreal TitleWidget::zIndexBounds(bool maxBound, bool intersectingOnly) 2713 { 2714 qreal bound = maxBound ? -99 : 99; 2715 const QList<QGraphicsItem *> l = graphicsView->scene()->selectedItems(); 2716 if (!l.isEmpty()) { 2717 QList<QGraphicsItem *> lItems; 2718 // Get items (all or intersecting only) 2719 if (intersectingOnly) { 2720 lItems = graphicsView->scene()->items(l[0]->sceneBoundingRect(), Qt::IntersectsItemShape); 2721 } else { 2722 lItems = graphicsView->scene()->items(); 2723 } 2724 if (!lItems.isEmpty()) { 2725 int n = lItems.size(); 2726 qreal z; 2727 if (maxBound) { 2728 for (int i = 0; i < n; ++i) { 2729 z = lItems[i]->zValue(); 2730 if (z > bound && !lItems[i]->isSelected()) { 2731 bound = z; 2732 } else if (z - 1 > bound) { 2733 // To get the maximum index even if it is of an item of the current selection. 2734 // Used when updating multiple items, to get all to the same level. 2735 // Otherwise, the maximum would stay at -99 if the highest item is in the selection. 2736 bound = z - 1; 2737 } 2738 } 2739 } else { 2740 // Get minimum z index. 2741 for (int i = 0; i < n; ++i) { 2742 z = lItems[i]->zValue(); 2743 if (z < bound && !lItems[i]->isSelected() && z > -999) { 2744 // There are items at the very bottom (background e.g.) with z-index < -1000. 2745 bound = z; 2746 } else if (z + 1 < bound && z > -999) { 2747 bound = z + 1; 2748 } 2749 } 2750 } 2751 } 2752 } 2753 return bound; 2754 } 2755 2756 void TitleWidget::slotZIndexUp() 2757 { 2758 QList<QGraphicsItem *> l = graphicsView->scene()->selectedItems(); 2759 if (l.size() >= 1) { 2760 qreal currentZ = l[0]->zValue(); 2761 qreal max = zIndexBounds(true, true); 2762 if (currentZ <= max) { 2763 l[0]->setZValue(currentZ + 1); 2764 updateDimension(l[0]); 2765 } 2766 } 2767 } 2768 2769 void TitleWidget::slotZIndexTop() 2770 { 2771 QList<QGraphicsItem *> l = graphicsView->scene()->selectedItems(); 2772 qreal max = zIndexBounds(true, false); 2773 // qCDebug(KDENLIVE_LOG) << "Max z-index is " << max << ".\n"; 2774 for (auto &i : l) { 2775 qreal currentZ = i->zValue(); 2776 if (currentZ <= max) { 2777 // qCDebug(KDENLIVE_LOG) << "Updating item " << i << ", is " << currentZ << ".\n"; 2778 i->setZValue(max + 1); 2779 } else { 2780 // qCDebug(KDENLIVE_LOG) << "Not updating " << i << ", is " << currentZ << ".\n"; 2781 } 2782 } 2783 // Update the z index value in the GUI 2784 if (!l.isEmpty()) { 2785 updateDimension(l[0]); 2786 } 2787 } 2788 2789 void TitleWidget::slotZIndexDown() 2790 { 2791 QList<QGraphicsItem *> l = graphicsView->scene()->selectedItems(); 2792 if (l.size() >= 1) { 2793 qreal currentZ = l[0]->zValue(); 2794 qreal min = zIndexBounds(false, true); 2795 if (currentZ >= min) { 2796 l[0]->setZValue(currentZ - 1); 2797 updateDimension(l[0]); 2798 } 2799 } 2800 } 2801 2802 void TitleWidget::slotZIndexBottom() 2803 { 2804 QList<QGraphicsItem *> l = graphicsView->scene()->selectedItems(); 2805 qreal min = zIndexBounds(false, false); 2806 for (auto &i : l) { 2807 qreal currentZ = i->zValue(); 2808 if (currentZ >= min) { 2809 i->setZValue(min - 1); 2810 } 2811 } 2812 // Update the z index value in the GUI 2813 if (!l.isEmpty()) { 2814 updateDimension(l[0]); 2815 } 2816 } 2817 2818 void TitleWidget::slotSelectAll() 2819 { 2820 QList<QGraphicsItem *> l = graphicsView->scene()->items(); 2821 for (auto i : qAsConst(l)) { 2822 i->setSelected(true); 2823 } 2824 } 2825 2826 void TitleWidget::selectItems(int itemType) 2827 { 2828 QList<QGraphicsItem *> l; 2829 if (!graphicsView->scene()->selectedItems().isEmpty()) { 2830 l = graphicsView->scene()->selectedItems(); 2831 for (auto i : qAsConst(l)) { 2832 if (i->type() != itemType) { 2833 i->setSelected(false); 2834 } 2835 } 2836 } else { 2837 l = graphicsView->scene()->items(); 2838 for (auto i : qAsConst(l)) { 2839 if (i->type() == itemType) { 2840 i->setSelected(true); 2841 } 2842 } 2843 } 2844 } 2845 2846 void TitleWidget::slotSelectText() 2847 { 2848 selectItems(TEXTITEM); 2849 } 2850 2851 void TitleWidget::slotSelectRects() 2852 { 2853 selectItems(RECTITEM); 2854 } 2855 2856 void TitleWidget::slotSelectEllipses() 2857 { 2858 selectItems(ELLIPSEITEM); 2859 } 2860 2861 void TitleWidget::slotSelectImages() 2862 { 2863 selectItems(IMAGEITEM); 2864 } 2865 2866 void TitleWidget::slotSelectNone() 2867 { 2868 graphicsView->blockSignals(true); 2869 QList<QGraphicsItem *> l = graphicsView->scene()->items(); 2870 for (auto i : qAsConst(l)) { 2871 i->setSelected(false); 2872 } 2873 graphicsView->blockSignals(false); 2874 selectionChanged(); 2875 } 2876 2877 QString TitleWidget::getTooltipWithShortcut(const QString &tipText, QAction *button) 2878 { 2879 return tipText + QStringLiteral(" <b>") + button->shortcut().toString() + QStringLiteral("</b>"); 2880 } 2881 2882 void TitleWidget::prepareTools(QGraphicsItem *referenceItem) 2883 { 2884 // Let some GUI elements block signals. We may want to change their values without any sideeffects. 2885 // Additionally, store the previous blocking state to avoid side effects when this function is called from within another one. 2886 // Note: Disabling an element also blocks signals. So disabled elements don't need to be set to blocking too. 2887 bool blockOX = origin_x_left->signalsBlocked(); 2888 bool blockOY = origin_y_top->signalsBlocked(); 2889 bool blockRX = itemrotatex->signalsBlocked(); 2890 bool blockRY = itemrotatey->signalsBlocked(); 2891 bool blockRZ = itemrotatez->signalsBlocked(); 2892 bool blockZoom = itemzoom->signalsBlocked(); 2893 bool blockX = value_x->signalsBlocked(); 2894 bool blockY = value_y->signalsBlocked(); 2895 bool blockW = value_w->signalsBlocked(); 2896 bool blockH = value_h->signalsBlocked(); 2897 origin_x_left->blockSignals(true); 2898 origin_y_top->blockSignals(true); 2899 itemrotatex->blockSignals(true); 2900 itemrotatey->blockSignals(true); 2901 itemrotatez->blockSignals(true); 2902 itemzoom->blockSignals(true); 2903 value_x->blockSignals(true); 2904 value_y->blockSignals(true); 2905 value_w->blockSignals(true); 2906 value_h->blockSignals(true); 2907 2908 if (referenceItem == nullptr) { 2909 // qCDebug(KDENLIVE_LOG) << "nullptr item.\n"; 2910 origin_x_left->setChecked(false); 2911 origin_y_top->setChecked(false); 2912 updateTextOriginX(); 2913 updateTextOriginY(); 2914 enableToolbars(TITLE_SELECT); 2915 showToolbars(TITLE_SELECT); 2916 2917 itemzoom->setEnabled(false); 2918 itemrotatex->setEnabled(false); 2919 itemrotatey->setEnabled(false); 2920 itemrotatez->setEnabled(false); 2921 frame_properties->setEnabled(false); 2922 toolbar_stack->setEnabled(false); 2923 /*letter_spacing->setEnabled(false); 2924 line_spacing->setEnabled(false); 2925 letter_spacing->setValue(0); 2926 line_spacing->setValue(0);*/ 2927 } else { 2928 toolbar_stack->setEnabled(true); 2929 frame_properties->setEnabled(true); 2930 if (referenceItem != m_startViewport && referenceItem != m_endViewport) { 2931 itemzoom->setEnabled(true); 2932 itemrotatex->setEnabled(true); 2933 itemrotatey->setEnabled(true); 2934 itemrotatez->setEnabled(true); 2935 } else { 2936 itemzoom->setEnabled(false); 2937 itemrotatex->setEnabled(false); 2938 itemrotatey->setEnabled(false); 2939 itemrotatez->setEnabled(false); 2940 updateInfoText(); 2941 } 2942 2943 letter_spacing->setEnabled(referenceItem->type() == TEXTITEM); 2944 line_spacing->setEnabled(referenceItem->type() == TEXTITEM); 2945 2946 if (referenceItem->type() == TEXTITEM) { 2947 showToolbars(TITLE_TEXT); 2948 auto *i = static_cast<MyTextItem *>(referenceItem); 2949 if (!i->document()->isEmpty()) { 2950 // We have an existing text item selected 2951 if (!i->data(100).isNull()) { 2952 // Item has an effect 2953 /*QStringList effdata = i->data(100).toStringList(); 2954 QString effectName = effdata.takeFirst(); 2955 if (effectName == QLatin1String("typewriter")) { 2956 QStringList params = effdata.at(0).split(QLatin1Char(';')); 2957 typewriter_delay->setValue(params.at(0).toInt()); 2958 typewriter_start->setValue(params.at(1).toInt()); 2959 effect_list->setCurrentIndex(effect_list->findData((int)TYPEWRITEREFFECT)); 2960 }*/ 2961 } else { 2962 /*if (i->graphicsEffect()) { 2963 QGraphicsBlurEffect *blur = static_cast <QGraphicsBlurEffect *>(i->graphicsEffect()); 2964 if (blur) { 2965 effect_list->setCurrentIndex(effect_list->findData((int) BLUREFFECT)); 2966 int rad = (int) blur->blurRadius(); 2967 blur_radius->setValue(rad); 2968 effect_stack->setHidden(false); 2969 } else { 2970 QGraphicsDropShadowEffect *shad = static_cast <QGraphicsDropShadowEffect *>(i->graphicsEffect()); 2971 if (shad) { 2972 effect_list->setCurrentIndex(effect_list->findData((int) SHADOWEFFECT)); 2973 shadow_radius->setValue(shad->blurRadius()); 2974 shadow_x->setValue(shad->xOffset()); 2975 shadow_y->setValue(shad->yOffset()); 2976 effect_stack->setHidden(false); 2977 } 2978 } 2979 } else { 2980 effect_list->setCurrentIndex(effect_list->findData((int) NOEFFECT)); 2981 effect_stack->setHidden(true); 2982 }*/ 2983 } 2984 font_size->blockSignals(true); 2985 font_family->blockSignals(true); 2986 font_weight_box->blockSignals(true); 2987 buttonItalic->blockSignals(true); 2988 buttonUnder->blockSignals(true); 2989 fontColorButton->blockSignals(true); 2990 buttonAlignLeft->blockSignals(true); 2991 buttonAlignRight->blockSignals(true); 2992 buttonAlignCenter->blockSignals(true); 2993 2994 QFont font = i->font(); 2995 font_family->setCurrentFont(font); 2996 font_size->setValue(font.pixelSize()); 2997 m_scene->slotUpdateFontSize(font.pixelSize()); 2998 buttonItalic->setChecked(font.italic()); 2999 buttonUnder->setChecked(font.underline()); 3000 setFontBoxWeight(font.weight()); 3001 3002 QTextCursor cursor(i->document()); 3003 cursor.select(QTextCursor::Document); 3004 QColor color = cursor.charFormat().foreground().color(); 3005 fontColorButton->setColor(color); 3006 3007 if (!i->data(TitleDocument::OutlineWidth).isNull()) { 3008 textOutline->blockSignals(true); 3009 textOutline->setValue(int(i->data(TitleDocument::OutlineWidth).toDouble())); 3010 textOutline->blockSignals(false); 3011 } else { 3012 textOutline->blockSignals(true); 3013 textOutline->setValue(0); 3014 textOutline->blockSignals(false); 3015 } 3016 if (!i->data(TitleDocument::OutlineColor).isNull()) { 3017 textOutlineColor->blockSignals(true); 3018 QVariant variant = i->data(TitleDocument::OutlineColor); 3019 color = variant.value<QColor>(); 3020 textOutlineColor->setColor(color); 3021 textOutlineColor->blockSignals(false); 3022 } 3023 if (!i->data(TitleDocument::Gradient).isNull()) { 3024 gradients_combo->blockSignals(true); 3025 gradient_color->setChecked(true); 3026 QString gradientData = i->data(TitleDocument::Gradient).toString(); 3027 int ix = gradients_combo->findData(gradientData); 3028 if (ix == -1) { 3029 // This gradient does not exist in our settings, store it 3030 storeGradient(gradientData); 3031 ix = gradients_combo->findData(gradientData); 3032 } 3033 gradients_combo->setCurrentIndex(ix); 3034 gradients_combo->blockSignals(false); 3035 } else { 3036 plain_color->setChecked(true); 3037 } 3038 if (i->alignment() == Qt::AlignHCenter) { 3039 buttonAlignCenter->setChecked(true); 3040 } else if (i->alignment() == Qt::AlignRight) { 3041 buttonAlignRight->setChecked(true); 3042 } else if (i->alignment() == Qt::AlignLeft) { 3043 buttonAlignLeft->setChecked(true); 3044 } else { 3045 QAbstractButton *selectedButton = m_textAlignGroup->button(KdenliveSettings::titlerAlign()); 3046 if (selectedButton) { 3047 selectedButton->setChecked(true); 3048 } 3049 } 3050 3051 QStringList sInfo = i->shadowInfo(); 3052 if (sInfo.count() >= 5) { 3053 QSignalBlocker bk(shadowBox); 3054 shadowBox->setChecked(static_cast<bool>(sInfo.at(0).toInt())); 3055 shadowBox->blockSignals(true); 3056 shadowColor->setColor(QColor(sInfo.at(1))); 3057 blur_radius->setValue(sInfo.at(2).toInt()); 3058 shadowX->setValue(sInfo.at(3).toInt()); 3059 shadowY->setValue(sInfo.at(4).toInt()); 3060 } 3061 3062 sInfo = i->twInfo(); 3063 if (sInfo.count() >= 5) { 3064 QSignalBlocker bk(typewriterBox); 3065 typewriterBox->setChecked(static_cast<bool>(sInfo.at(0).toInt())); 3066 tw_sb_step->setValue(sInfo.at(1).toInt()); 3067 switch (sInfo.at(2).toInt()) { 3068 case 1: 3069 tw_rd_char->setChecked(true); 3070 break; 3071 case 2: 3072 tw_rd_word->setChecked(true); 3073 break; 3074 case 3: 3075 tw_rd_line->setChecked(true); 3076 break; 3077 default: 3078 tw_rd_custom->setChecked(true); 3079 break; 3080 } 3081 tw_sb_sigma->setValue(sInfo.at(3).toInt()); 3082 tw_sb_seed->setValue(sInfo.at(4).toInt()); 3083 } 3084 3085 letter_spacing->blockSignals(true); 3086 line_spacing->blockSignals(true); 3087 QTextCursor cur = i->textCursor(); 3088 QTextBlockFormat format = cur.blockFormat(); 3089 letter_spacing->setValue(int(font.letterSpacing())); 3090 line_spacing->setValue(int(format.lineHeight())); 3091 letter_spacing->blockSignals(false); 3092 line_spacing->blockSignals(false); 3093 3094 font_size->blockSignals(false); 3095 font_family->blockSignals(false); 3096 font_weight_box->blockSignals(false); 3097 buttonItalic->blockSignals(false); 3098 buttonUnder->blockSignals(false); 3099 fontColorButton->blockSignals(false); 3100 buttonAlignLeft->blockSignals(false); 3101 buttonAlignRight->blockSignals(false); 3102 buttonAlignCenter->blockSignals(false); 3103 3104 // mbt 1607: Select text if the text item is an unchanged template item. 3105 if (i->property("isTemplate").isValid()) { 3106 cur.setPosition(0, QTextCursor::MoveAnchor); 3107 cur.select(QTextCursor::Document); 3108 i->setTextCursor(cur); 3109 // Make text editable now. 3110 i->grabKeyboard(); 3111 i->setTextInteractionFlags(Qt::TextEditorInteraction); 3112 } 3113 } 3114 3115 updateAxisButtons(i); 3116 updateCoordinates(i); 3117 updateDimension(i); 3118 enableToolbars(TITLE_TEXT); 3119 3120 } else if ((referenceItem)->type() == RECTITEM) { 3121 showToolbars(TITLE_RECTANGLE); 3122 settingUp = 1; 3123 auto *rec = static_cast<QGraphicsRectItem *>(referenceItem); 3124 if (rec == m_startViewport || rec == m_endViewport) { 3125 enableToolbars(TITLE_SELECT); 3126 } else { 3127 QColor fcol = rec->pen().color(); 3128 QColor bcol = rec->brush().color(); 3129 rectFColor->setColor(fcol); 3130 QString gradientData = rec->data(TitleDocument::Gradient).toString(); 3131 if (gradientData.isEmpty()) { 3132 plain_rect->setChecked(true); 3133 rectBColor->setColor(bcol); 3134 } else { 3135 gradient_rect->setChecked(true); 3136 gradients_rect_combo->blockSignals(true); 3137 int ix = gradients_rect_combo->findData(gradientData); 3138 if (ix == -1) { 3139 storeGradient(gradientData); 3140 ix = gradients_rect_combo->findData(gradientData); 3141 } 3142 gradients_rect_combo->setCurrentIndex(ix); 3143 gradients_rect_combo->blockSignals(false); 3144 } 3145 settingUp = 0; 3146 if (rec->pen() == Qt::NoPen) { 3147 rectLineWidth->setValue(0); 3148 } else { 3149 rectLineWidth->setValue(rec->pen().width()); 3150 } 3151 enableToolbars(TITLE_RECTANGLE); 3152 } 3153 3154 updateAxisButtons(referenceItem); 3155 updateCoordinates(rec); 3156 updateDimension(rec); 3157 3158 } else if ((referenceItem)->type() == ELLIPSEITEM) { 3159 showToolbars(TITLE_RECTANGLE); 3160 settingUp = 1; 3161 auto *ellipse = static_cast<QGraphicsEllipseItem *>(referenceItem); 3162 QColor fcol = ellipse->pen().color(); 3163 QColor bcol = ellipse->brush().color(); 3164 rectFColor->setColor(fcol); 3165 QString gradientData = ellipse->data(TitleDocument::Gradient).toString(); 3166 if (gradientData.isEmpty()) { 3167 plain_rect->setChecked(true); 3168 rectBColor->setColor(bcol); 3169 } else { 3170 gradient_rect->setChecked(true); 3171 gradients_rect_combo->blockSignals(true); 3172 int ix = gradients_rect_combo->findData(gradientData); 3173 if (ix == -1) { 3174 storeGradient(gradientData); 3175 ix = gradients_rect_combo->findData(gradientData); 3176 } 3177 gradients_rect_combo->setCurrentIndex(ix); 3178 gradients_rect_combo->blockSignals(false); 3179 } 3180 settingUp = 0; 3181 if (ellipse->pen() == Qt::NoPen) { 3182 rectLineWidth->setValue(0); 3183 } else { 3184 rectLineWidth->setValue(ellipse->pen().width()); 3185 } 3186 enableToolbars(TITLE_ELLIPSE); 3187 3188 updateAxisButtons(referenceItem); 3189 updateCoordinates(ellipse); 3190 updateDimension(ellipse); 3191 3192 } else if (referenceItem->type() == IMAGEITEM) { 3193 showToolbars(TITLE_IMAGE); 3194 3195 updateCoordinates(referenceItem); 3196 updateDimension(referenceItem); 3197 enableToolbars(TITLE_IMAGE); 3198 QSignalBlocker bk(preserveAspectRatio); 3199 Transform t = m_transformations.value(referenceItem); 3200 preserveAspectRatio->setChecked(qFuzzyCompare(t.scalex, t.scaley)); 3201 3202 } else { 3203 showToolbars(TITLE_SELECT); 3204 enableToolbars(TITLE_SELECT); 3205 frame_properties->setEnabled(false); 3206 } 3207 zValue->setValue(int(referenceItem->zValue())); 3208 if (!referenceItem->data(TitleDocument::ZoomFactor).isNull()) { 3209 itemzoom->setValue(referenceItem->data(TitleDocument::ZoomFactor).toInt()); 3210 } else { 3211 itemzoom->setValue(qRound(m_transformations.value(referenceItem).scalex * 100.0)); 3212 } 3213 itemrotatex->setValue(int(m_transformations.value(referenceItem).rotatex)); 3214 itemrotatey->setValue(int(m_transformations.value(referenceItem).rotatey)); 3215 itemrotatez->setValue(int(m_transformations.value(referenceItem).rotatez)); 3216 } 3217 3218 itemrotatex->blockSignals(blockRX); 3219 itemrotatey->blockSignals(blockRY); 3220 itemrotatez->blockSignals(blockRZ); 3221 itemzoom->blockSignals(blockZoom); 3222 origin_x_left->blockSignals(blockOX); 3223 origin_y_top->blockSignals(blockOY); 3224 value_x->blockSignals(blockX); 3225 value_y->blockSignals(blockY); 3226 value_w->blockSignals(blockW); 3227 value_h->blockSignals(blockH); 3228 } 3229 3230 void TitleWidget::slotEditGradient() 3231 { 3232 auto *caller = qobject_cast<QToolButton *>(QObject::sender()); 3233 if (!caller) { 3234 return; 3235 } 3236 QComboBox *combo = nullptr; 3237 if (caller == edit_gradient) { 3238 combo = gradients_combo; 3239 } else { 3240 combo = gradients_rect_combo; 3241 } 3242 QMap<QString, QString> gradients; 3243 for (int i = 0; i < combo->count(); i++) { 3244 gradients.insert(combo->itemText(i), combo->itemData(i).toString()); 3245 } 3246 GradientWidget d(gradients, combo->currentIndex()); 3247 if (d.exec() == QDialog::Accepted) { 3248 // Save current gradients 3249 QMap<QString, QString> gradMap = d.gradients(); 3250 QList<QIcon> icons = d.icons(); 3251 QMap<QString, QString>::const_iterator i = gradMap.constBegin(); 3252 KSharedConfigPtr config = KSharedConfig::openConfig(); 3253 KConfigGroup group(config, "TitleGradients"); 3254 group.deleteGroup(); 3255 combo->clear(); 3256 gradients_rect_combo->clear(); 3257 int ix = 0; 3258 while (i != gradMap.constEnd()) { 3259 group.writeEntry(i.key(), i.value()); 3260 gradients_combo->addItem(icons.at(ix), i.key(), i.value()); 3261 gradients_rect_combo->addItem(icons.at(ix), i.key(), i.value()); 3262 ++i; 3263 ix++; 3264 } 3265 group.sync(); 3266 combo->setCurrentIndex(d.selectedGradient()); 3267 } 3268 } 3269 3270 void TitleWidget::storeGradient(const QString &gradientData) 3271 { 3272 KSharedConfigPtr config = KSharedConfig::openConfig(); 3273 KConfigGroup group(config, "TitleGradients"); 3274 QMap<QString, QString> values = group.entryMap(); 3275 int ix = qMax(1, values.count()); 3276 QString gradName = i18n("Gradient %1", ix); 3277 while (values.contains(gradName)) { 3278 ix++; 3279 gradName = i18n("Gradient %1", ix); 3280 } 3281 group.writeEntry(gradName, gradientData); 3282 group.sync(); 3283 QPixmap pix(30, 30); 3284 pix.fill(Qt::transparent); 3285 QLinearGradient gr = GradientWidget::gradientFromString(gradientData, pix.width(), pix.height()); 3286 gr.setStart(0, pix.height() / 2); 3287 gr.setFinalStop(pix.width(), pix.height() / 2); 3288 QPainter painter(&pix); 3289 painter.fillRect(0, 0, pix.width(), pix.height(), QBrush(gr)); 3290 painter.end(); 3291 QIcon icon(pix); 3292 gradients_combo->addItem(icon, gradName, gradientData); 3293 gradients_rect_combo->addItem(icon, gradName, gradientData); 3294 } 3295 3296 void TitleWidget::loadGradients() 3297 { 3298 gradients_combo->blockSignals(true); 3299 gradients_rect_combo->blockSignals(true); 3300 QString grad_data = gradients_combo->currentData().toString(); 3301 QString rect_data = gradients_rect_combo->currentData().toString(); 3302 gradients_combo->clear(); 3303 gradients_rect_combo->clear(); 3304 KSharedConfigPtr config = KSharedConfig::openConfig(); 3305 KConfigGroup group(config, "TitleGradients"); 3306 QMap<QString, QString> values = group.entryMap(); 3307 if (values.isEmpty()) { 3308 // Ensure we at least always have one sample black to white gradient 3309 values.insert(i18n("Gradient"), QStringLiteral("#ffffffff;#ff000000;0;100;90")); 3310 } 3311 QMapIterator<QString, QString> k(values); 3312 while (k.hasNext()) { 3313 k.next(); 3314 QPixmap pix(30, 30); 3315 pix.fill(Qt::transparent); 3316 QLinearGradient gr = GradientWidget::gradientFromString(k.value(), pix.width(), pix.height()); 3317 gr.setStart(0, pix.height() / 2); 3318 gr.setFinalStop(pix.width(), pix.height() / 2); 3319 QPainter painter(&pix); 3320 painter.fillRect(0, 0, pix.width(), pix.height(), QBrush(gr)); 3321 painter.end(); 3322 QIcon icon(pix); 3323 gradients_combo->addItem(icon, k.key(), k.value()); 3324 gradients_rect_combo->addItem(icon, k.key(), k.value()); 3325 } 3326 int ix = gradients_combo->findData(grad_data); 3327 if (ix >= 0) { 3328 gradients_combo->setCurrentIndex(ix); 3329 } 3330 ix = gradients_rect_combo->findData(rect_data); 3331 if (ix >= 0) { 3332 gradients_rect_combo->setCurrentIndex(ix); 3333 } 3334 gradients_combo->blockSignals(false); 3335 gradients_rect_combo->blockSignals(false); 3336 } 3337 3338 void TitleWidget::slotUpdateShadow() 3339 { 3340 QList<QGraphicsItem *> l = graphicsView->scene()->selectedItems(); 3341 for (int i = 0; i < graphicsView->scene()->selectedItems().length(); ++i) { 3342 MyTextItem *item = nullptr; 3343 if (l.at(i)->type() == TEXTITEM) { 3344 item = static_cast<MyTextItem *>(l.at(i)); 3345 } 3346 if (!item) { 3347 // No text item, try next one. 3348 continue; 3349 } 3350 item->updateShadow(shadowBox->isChecked(), blur_radius->value(), shadowX->value(), shadowY->value(), shadowColor->color()); 3351 } 3352 } 3353 3354 void TitleWidget::slotUpdateTW() 3355 { 3356 QList<QGraphicsItem *> l = graphicsView->scene()->selectedItems(); 3357 for (int i = 0; i < graphicsView->scene()->selectedItems().length(); ++i) { 3358 MyTextItem *item = nullptr; 3359 if (l.at(i)->type() == TEXTITEM) { 3360 item = static_cast<MyTextItem *>(l.at(i)); 3361 } 3362 if (!item) { 3363 // No text item, try next one. 3364 continue; 3365 } 3366 int mode = 0; 3367 if (tw_rd_char->isChecked()) 3368 mode = 1; 3369 else if (tw_rd_word->isChecked()) 3370 mode = 2; 3371 else if (tw_rd_line->isChecked()) 3372 mode = 3; 3373 3374 item->updateTW(typewriterBox->isChecked(), tw_sb_step->value(), mode, tw_sb_sigma->value(), tw_sb_seed->value()); 3375 } 3376 } 3377 3378 const QString TitleWidget::titleSuggest() 3379 { 3380 // Find top item to extract title proposal 3381 QList<QGraphicsItem *> list = graphicsView->scene()->items(); 3382 int y = m_frameHeight; 3383 QString title; 3384 for (QGraphicsItem *qgItem : qAsConst(list)) { 3385 if (qgItem->pos().y() < y && qgItem->type() == TEXTITEM) { 3386 auto *i = static_cast<MyTextItem *>(qgItem); 3387 QString currentTitle = i->toPlainText().simplified(); 3388 if (currentTitle.length() > 2) { 3389 title = currentTitle.length() > 12 ? currentTitle.left(12) + QStringLiteral("...") : currentTitle; 3390 y = int(qgItem->pos().y()); 3391 } 3392 } 3393 } 3394 return title; 3395 } 3396 3397 void TitleWidget::showGuides(int state) 3398 { 3399 for (QGraphicsLineItem *it : qAsConst(m_guides)) { 3400 it->setVisible(state == Qt::Checked); 3401 } 3402 KdenliveSettings::setTitlerShowGuides(state == Qt::Checked); 3403 } 3404 3405 void TitleWidget::updateGuides(int) 3406 { 3407 KdenliveSettings::setTitlerHGuides(hguides->value()); 3408 KdenliveSettings::setTitlerVGuides(vguides->value()); 3409 if (!m_guides.isEmpty()) { 3410 qDeleteAll(m_guides); 3411 m_guides.clear(); 3412 } 3413 QPen framepen; 3414 QColor gColor(KdenliveSettings::titleGuideColor()); 3415 framepen.setColor(gColor); 3416 3417 // Guides 3418 // Horizontal guides 3419 int max = hguides->value(); 3420 bool guideVisible = show_guides->checkState() == Qt::Checked; 3421 for (int i = 0; i < max; i++) { 3422 auto *line1 = new QGraphicsLineItem(0, (i + 1) * m_frameHeight / (max + 1), m_frameWidth, (i + 1) * m_frameHeight / (max + 1), m_frameBorder); 3423 line1->setPen(framepen); 3424 line1->setFlags({}); 3425 line1->setData(-1, -1); 3426 line1->setVisible(guideVisible); 3427 m_guides << line1; 3428 } 3429 max = vguides->value(); 3430 for (int i = 0; i < max; i++) { 3431 auto *line1 = new QGraphicsLineItem((i + 1) * m_frameWidth / (max + 1), 0, (i + 1) * m_frameWidth / (max + 1), m_frameHeight, m_frameBorder); 3432 line1->setPen(framepen); 3433 line1->setFlags({}); 3434 line1->setData(-1, -1); 3435 line1->setVisible(guideVisible); 3436 m_guides << line1; 3437 } 3438 3439 gColor.setAlpha(160); 3440 framepen.setColor(gColor); 3441 3442 auto *line6 = new QGraphicsLineItem(0, 0, m_frameWidth, m_frameHeight, m_frameBorder); 3443 line6->setPen(framepen); 3444 line6->setFlags({}); 3445 line6->setData(-1, -1); 3446 line6->setVisible(guideVisible); 3447 m_guides << line6; 3448 3449 auto *line7 = new QGraphicsLineItem(m_frameWidth, 0, 0, m_frameHeight, m_frameBorder); 3450 line7->setPen(framepen); 3451 line7->setFlags({}); 3452 line7->setData(-1, -1); 3453 line7->setVisible(guideVisible); 3454 m_guides << line7; 3455 } 3456 3457 void TitleWidget::guideColorChanged(const QColor &col) 3458 { 3459 KdenliveSettings::setTitleGuideColor(col); 3460 QColor guideCol(col); 3461 for (QGraphicsLineItem *it : qAsConst(m_guides)) { 3462 int alpha = it->pen().color().alpha(); 3463 guideCol.setAlpha(alpha); 3464 QPen framePen(guideCol); 3465 it->setPen(framePen); 3466 } 3467 } 3468 3469 void TitleWidget::slotPatternsTileWidth(int width) 3470 { 3471 int w = width * pCore->getCurrentProfile()->display_aspect_num(); 3472 int h = width * pCore->getCurrentProfile()->display_aspect_den(); 3473 m_patternsModel->setTileSize(QSize(w, h)); 3474 patternsList->setGridSize(QSize(w + 4, h + 4)); 3475 m_patternsModel->repaintScenes(); 3476 } 3477 3478 void TitleWidget::slotPatternDblClicked(const QModelIndex &idx) 3479 { 3480 if (!idx.isValid()) return; 3481 3482 QString data = m_patternsModel->data(idx, Qt::UserRole).toString(); 3483 3484 QDomDocument doc; 3485 doc.setContent(data); 3486 3487 QList<QGraphicsItem *> items; 3488 int width, height, duration, missing; 3489 TitleDocument::loadFromXml(doc, items, width, height, nullptr, nullptr, nullptr, &duration, missing); 3490 3491 for (QGraphicsItem *item : qAsConst(items)) { 3492 item->setZValue(m_count++); 3493 updateAxisButtons(item); 3494 prepareTools(item); 3495 m_scene->addNewItem(item); 3496 } 3497 m_scene->clearSelection(); 3498 for (QGraphicsItem *item : qAsConst(items)) { 3499 item->setSelected(true); 3500 } 3501 } 3502 3503 void TitleWidget::slotPatternBtnAddClicked() 3504 { 3505 QDomDocument doc = TitleDocument::xml(graphicsView->scene()->selectedItems(), m_frameWidth, m_frameHeight, nullptr, nullptr, false); 3506 3507 m_patternsModel->addScene(doc.toString()); 3508 btn_removeAll->setEnabled(m_patternsModel->rowCount(QModelIndex()) != 0); 3509 } 3510 3511 void TitleWidget::slotPatternBtnRemoveClicked() 3512 { 3513 QModelIndexList items = patternsList->selectionModel()->selectedIndexes(); 3514 std::sort(items.begin(), items.end()); 3515 std::reverse(items.begin(), items.end()); 3516 for (auto idx : qAsConst(items)) { 3517 m_patternsModel->removeScene(idx); 3518 } 3519 btn_removeAll->setEnabled(m_patternsModel->rowCount(QModelIndex()) != 0); 3520 } 3521 3522 void TitleWidget::readPatterns() 3523 { 3524 KSharedConfigPtr config = KSharedConfig::openConfig(); 3525 KConfigGroup titleConfig(config, "TitlePatterns"); 3526 3527 // Read the entries 3528 scaleSlider->setValue(titleConfig.readEntry("scale_factor", scaleSlider->minimum())); 3529 m_patternsModel->deserialize(titleConfig.readEntry("patterns", QByteArray())); 3530 3531 btn_remove->setEnabled(false); 3532 btn_removeAll->setEnabled(m_patternsModel->rowCount(QModelIndex()) != 0); 3533 } 3534 3535 void TitleWidget::writePatterns() 3536 { 3537 3538 KSharedConfigPtr config = KSharedConfig::openConfig(); 3539 KConfigGroup titleConfig(config, "TitlePatterns"); 3540 3541 int sf = titleConfig.readEntry("scale_factor", scaleSlider->minimum()); 3542 3543 // check whether the model was updated or scale slider differs 3544 if ((!m_patternsModel->getModifiedCounter()) && (sf == scaleSlider->value())) return; 3545 3546 // Write the entries 3547 titleConfig.writeEntry("scale_factor", scaleSlider->value()); 3548 QByteArray ba = m_patternsModel->serialize(); 3549 titleConfig.writeEntry("patterns", ba); 3550 3551 config->sync(); 3552 }