1 /*
2     SPDX-FileCopyrightText: 2017 Jean-Baptiste Mardelle
3     SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
4 */
5 
6 #include "geometrywidget.h"
7 #include "core.h"
8 #include "doublewidget.h"
9 #include "dragvalue.h"
10 #include "monitor/monitor.h"
11 
12 #include <KLocalizedString>
13 #include <QGridLayout>
14 
GeometryWidget(Monitor * monitor,QPair<int,int> range,const QRect & rect,double opacity,const QSize frameSize,bool useRatioLock,bool useOpacity,QWidget * parent)15 GeometryWidget::GeometryWidget(Monitor *monitor, QPair<int, int> range, const QRect &rect, double opacity, const QSize frameSize, bool useRatioLock,
16                                bool useOpacity, QWidget *parent)
17     : QWidget(parent)
18     , m_min(range.first)
19     , m_max(range.second)
20     , m_active(false)
21     , m_monitor(monitor)
22     , m_opacity(nullptr)
23     , m_opacityFactor(100.)
24 {
25     Q_UNUSED(useRatioLock)
26     setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred);
27     auto *layout = new QVBoxLayout(this);
28     layout->setContentsMargins(0, 0, 0, 0);
29     layout->setSpacing(0);
30     m_defaultSize = pCore->getCurrentFrameSize();
31     m_sourceSize = (frameSize.isValid() && !frameSize.isNull()) ? frameSize : m_defaultSize;
32     /*QString paramName = i18n(paramTag.toUtf8().data());
33     QString comment = m_model->data(ix, AssetParameterModel::CommentRole).toString();
34     if (!comment.isEmpty()) {
35         comment = i18n(comment.toUtf8().data());
36     }*/
37 
38     auto *horLayout = new QHBoxLayout;
39     horLayout->setSpacing(2);
40     m_spinX = new DragValue(i18nc("x axis position", "X"), 0, 0, -99000, 99000, -1, QString(), false, false, this);
41     connect(m_spinX, &DragValue::valueChanged, this, &GeometryWidget::slotAdjustRectKeyframeValue);
42     horLayout->addWidget(m_spinX);
43     m_spinX->setObjectName("spinX");
44 
45     m_spinY = new DragValue(i18nc("y axis position", "Y"), 0, 0, -99000, 99000, -1, QString(), false, false, this);
46     connect(m_spinY, &DragValue::valueChanged, this, &GeometryWidget::slotAdjustRectKeyframeValue);
47     horLayout->addWidget(m_spinY);
48     m_spinY->setObjectName("spinY");
49 
50     m_spinWidth = new DragValue(i18nc("Frame width", "W"), m_defaultSize.width(), 0, 1, 99000, -1, QString(), false, false, this);
51     connect(m_spinWidth, &DragValue::valueChanged, this, &GeometryWidget::slotAdjustRectWidth);
52     horLayout->addWidget(m_spinWidth);
53     m_spinWidth->setObjectName("spinW");
54 
55     // Lock ratio stuff
56     m_lockRatio = new QAction(QIcon::fromTheme(QStringLiteral("link")), i18n("Lock aspect ratio"), this);
57     m_lockRatio->setCheckable(true);
58     connect(m_lockRatio, &QAction::triggered, this, &GeometryWidget::slotLockRatio);
59     auto *ratioButton = new QToolButton;
60     ratioButton->setDefaultAction(m_lockRatio);
61     horLayout->addWidget(ratioButton);
62 
63     m_spinHeight = new DragValue(i18nc("Frame height", "H"), m_defaultSize.height(), 0, 1, 99000, -1, QString(), false, false, this);
64     connect(m_spinHeight, &DragValue::valueChanged, this, &GeometryWidget::slotAdjustRectHeight);
65     m_spinHeight->setObjectName("spinH");
66     horLayout->addWidget(m_spinHeight);
67     horLayout->addStretch(10);
68 
69     auto *horLayout2 = new QHBoxLayout;
70     horLayout2->setSpacing(2);
71     m_spinSize = new DragValue(i18n("Size"), 100, 2, 1, 99000, -1, i18n("%"), false, false, this);
72     m_spinSize->setStep(5);
73     m_spinSize->setObjectName("spinS");
74     connect(m_spinSize, &DragValue::valueChanged, this, &GeometryWidget::slotResize);
75     horLayout2->addWidget(m_spinSize);
76 
77     if (useOpacity) {
78         m_opacity = new DragValue(i18n("Opacity"), 100, 0, 0, 100, -1, i18n("%"), true, false, this);
79         m_opacity->setValue((int)(opacity * m_opacityFactor));
80         connect(m_opacity, &DragValue::valueChanged, this, [&]() { emit valueChanged(getValue()); });
81         m_opacity->setObjectName("spinO");
82         horLayout2->addWidget(m_opacity);
83     }
84     horLayout2->addStretch(10);
85 
86     // Build buttons
87     m_originalSize = new QAction(QIcon::fromTheme(QStringLiteral("zoom-original")), i18n("Adjust to original size"), this);
88     connect(m_originalSize, &QAction::triggered, this, &GeometryWidget::slotAdjustToSource);
89     m_originalSize->setCheckable(true);
90     QAction *adjustSize = new QAction(QIcon::fromTheme(QStringLiteral("zoom-fit-best")), i18n("Adjust and center in frame"), this);
91     connect(adjustSize, &QAction::triggered, this, &GeometryWidget::slotAdjustToFrameSize);
92     QAction *fitToWidth = new QAction(QIcon::fromTheme(QStringLiteral("zoom-fit-width")), i18n("Fit to width"), this);
93     connect(fitToWidth, &QAction::triggered, this, &GeometryWidget::slotFitToWidth);
94     QAction *fitToHeight = new QAction(QIcon::fromTheme(QStringLiteral("zoom-fit-height")), i18n("Fit to height"), this);
95     connect(fitToHeight, &QAction::triggered, this, &GeometryWidget::slotFitToHeight);
96 
97     QAction *alignleft = new QAction(QIcon::fromTheme(QStringLiteral("kdenlive-align-left")), i18n("Align left"), this);
98     connect(alignleft, &QAction::triggered, this, &GeometryWidget::slotMoveLeft);
99     QAction *alignhcenter = new QAction(QIcon::fromTheme(QStringLiteral("kdenlive-align-hor")), i18n("Center horizontally"), this);
100     connect(alignhcenter, &QAction::triggered, this, &GeometryWidget::slotCenterH);
101     QAction *alignright = new QAction(QIcon::fromTheme(QStringLiteral("kdenlive-align-right")), i18n("Align right"), this);
102     connect(alignright, &QAction::triggered, this, &GeometryWidget::slotMoveRight);
103     QAction *aligntop = new QAction(QIcon::fromTheme(QStringLiteral("kdenlive-align-top")), i18n("Align top"), this);
104     connect(aligntop, &QAction::triggered, this, &GeometryWidget::slotMoveTop);
105     QAction *alignvcenter = new QAction(QIcon::fromTheme(QStringLiteral("kdenlive-align-vert")), i18n("Center vertically"), this);
106     connect(alignvcenter, &QAction::triggered, this, &GeometryWidget::slotCenterV);
107     QAction *alignbottom = new QAction(QIcon::fromTheme(QStringLiteral("kdenlive-align-bottom")), i18n("Align bottom"), this);
108     connect(alignbottom, &QAction::triggered, this, &GeometryWidget::slotMoveBottom);
109 
110     auto *alignLayout = new QHBoxLayout;
111     alignLayout->setSpacing(0);
112     auto *alignButton = new QToolButton;
113     alignButton->setDefaultAction(alignleft);
114     alignButton->setAutoRaise(true);
115     alignLayout->addWidget(alignButton);
116 
117     alignButton = new QToolButton;
118     alignButton->setDefaultAction(alignhcenter);
119     alignButton->setAutoRaise(true);
120     alignLayout->addWidget(alignButton);
121 
122     alignButton = new QToolButton;
123     alignButton->setDefaultAction(alignright);
124     alignButton->setAutoRaise(true);
125     alignLayout->addWidget(alignButton);
126 
127     alignButton = new QToolButton;
128     alignButton->setDefaultAction(aligntop);
129     alignButton->setAutoRaise(true);
130     alignLayout->addWidget(alignButton);
131 
132     alignButton = new QToolButton;
133     alignButton->setDefaultAction(alignvcenter);
134     alignButton->setAutoRaise(true);
135     alignLayout->addWidget(alignButton);
136 
137     alignButton = new QToolButton;
138     alignButton->setDefaultAction(alignbottom);
139     alignButton->setAutoRaise(true);
140     alignLayout->addWidget(alignButton);
141 
142     alignButton = new QToolButton;
143     alignButton->setDefaultAction(m_originalSize);
144     alignButton->setAutoRaise(true);
145     alignLayout->addWidget(alignButton);
146 
147     alignButton = new QToolButton;
148     alignButton->setDefaultAction(adjustSize);
149     alignButton->setAutoRaise(true);
150     alignLayout->addWidget(alignButton);
151 
152     alignButton = new QToolButton;
153     alignButton->setDefaultAction(fitToWidth);
154     alignButton->setAutoRaise(true);
155     alignLayout->addWidget(alignButton);
156 
157     alignButton = new QToolButton;
158     alignButton->setDefaultAction(fitToHeight);
159     alignButton->setAutoRaise(true);
160     alignLayout->addWidget(alignButton);
161     alignLayout->addStretch(10);
162 
163     layout->addLayout(horLayout);
164     layout->addLayout(alignLayout);
165     layout->addLayout(horLayout2);
166     slotUpdateGeometryRect(rect);
167     slotAdjustRectKeyframeValue();
168     setMinimumHeight(horLayout->sizeHint().height() + horLayout2->sizeHint().height() + alignLayout->sizeHint().height());
169 }
170 
slotAdjustToSource()171 void GeometryWidget::slotAdjustToSource()
172 {
173     m_spinWidth->blockSignals(true);
174     m_spinHeight->blockSignals(true);
175     m_spinWidth->setValue((int)(m_sourceSize.width() / pCore->getCurrentSar() + 0.5), false);
176     m_spinHeight->setValue(m_sourceSize.height(), false);
177     m_spinWidth->blockSignals(false);
178     m_spinHeight->blockSignals(false);
179     slotAdjustRectKeyframeValue();
180     if (m_lockRatio->isChecked()) {
181         m_monitor->setEffectSceneProperty(QStringLiteral("lockratio"), m_originalSize->isChecked() ? (double)m_sourceSize.width() / m_sourceSize.height()
182                                                                                                    : (double)m_defaultSize.width() / m_defaultSize.height());
183     }
184 }
slotAdjustToFrameSize()185 void GeometryWidget::slotAdjustToFrameSize()
186 {
187     double monitorDar = pCore->getCurrentDar();
188     double sourceDar = m_sourceSize.width() / m_sourceSize.height();
189     QSignalBlocker bk1(m_spinWidth);
190     QSignalBlocker bk2(m_spinHeight);
191     if (sourceDar > monitorDar) {
192         // Fit to width
193         double factor = (double)m_defaultSize.width() / m_sourceSize.width() * pCore->getCurrentSar();
194         m_spinHeight->setValue((int)(m_sourceSize.height() * factor + 0.5));
195         m_spinWidth->setValue(m_defaultSize.width());
196     } else {
197         // Fit to height
198         double factor = (double)m_defaultSize.height() / m_sourceSize.height();
199         m_spinHeight->setValue(m_defaultSize.height());
200         m_spinWidth->setValue((int)(m_sourceSize.width() / pCore->getCurrentSar() * factor + 0.5));
201     }
202     // Center
203     QSignalBlocker bk3(m_spinX);
204     QSignalBlocker bk4(m_spinY);
205     m_spinX->setValue((m_defaultSize.width() - m_spinWidth->value()) / 2);
206     m_spinY->setValue((m_defaultSize.height() - m_spinHeight->value()) / 2);
207     slotAdjustRectKeyframeValue();
208 }
209 
slotFitToWidth()210 void GeometryWidget::slotFitToWidth()
211 {
212     double factor = (double)m_defaultSize.width() / m_sourceSize.width() * pCore->getCurrentSar();
213     m_spinWidth->blockSignals(true);
214     m_spinHeight->blockSignals(true);
215     m_spinHeight->setValue((int)(m_sourceSize.height() * factor + 0.5));
216     m_spinWidth->setValue(m_defaultSize.width());
217     m_spinWidth->blockSignals(false);
218     m_spinHeight->blockSignals(false);
219     slotAdjustRectKeyframeValue();
220 }
slotFitToHeight()221 void GeometryWidget::slotFitToHeight()
222 {
223     double factor = (double)m_defaultSize.height() / m_sourceSize.height();
224     m_spinWidth->blockSignals(true);
225     m_spinHeight->blockSignals(true);
226     m_spinHeight->setValue(m_defaultSize.height());
227     m_spinWidth->setValue((int)(m_sourceSize.width() / pCore->getCurrentSar() * factor + 0.5));
228     m_spinWidth->blockSignals(false);
229     m_spinHeight->blockSignals(false);
230     slotAdjustRectKeyframeValue();
231 }
slotResize(double value)232 void GeometryWidget::slotResize(double value)
233 {
234     QSignalBlocker bkh(m_spinHeight);
235     QSignalBlocker bkw(m_spinWidth);
236     QSignalBlocker bkx(m_spinX);
237     QSignalBlocker bky(m_spinY);
238     int w = (m_originalSize->isChecked() ? m_sourceSize.width() : m_defaultSize.width()) * value / 100.0;
239     int h = (m_originalSize->isChecked() ? m_sourceSize.height() : m_defaultSize.height()) * value / 100.0;
240     int delta_x = (m_spinWidth->value() - w) / 2;
241     int delta_y = (m_spinHeight->value() - h) / 2;
242     m_spinWidth->setValue(w);
243     m_spinHeight->setValue(h);
244     m_spinX->setValue(m_spinX->value() + delta_x);
245     m_spinY->setValue(m_spinY->value() + delta_y);
246     slotAdjustRectKeyframeValue();
247 }
248 
249 /** @brief Moves the rect to the left frame border (x position = 0). */
slotMoveLeft()250 void GeometryWidget::slotMoveLeft()
251 {
252     m_spinX->setValue(0);
253 }
254 /** @brief Centers the rect horizontally. */
slotCenterH()255 void GeometryWidget::slotCenterH()
256 {
257     m_spinX->setValue((m_defaultSize.width() - m_spinWidth->value()) / 2);
258 }
259 /** @brief Moves the rect to the right frame border (x position = frame width - rect width). */
slotMoveRight()260 void GeometryWidget::slotMoveRight()
261 {
262     m_spinX->setValue(m_defaultSize.width() - m_spinWidth->value());
263 }
264 
265 /** @brief Moves the rect to the top frame border (y position = 0). */
slotMoveTop()266 void GeometryWidget::slotMoveTop()
267 {
268     m_spinY->setValue(0);
269 }
270 
271 /** @brief Centers the rect vertically. */
slotCenterV()272 void GeometryWidget::slotCenterV()
273 {
274     m_spinY->setValue((m_defaultSize.height() - m_spinHeight->value()) / 2);
275 }
276 
277 /** @brief Moves the rect to the bottom frame border (y position = frame height - rect height). */
slotMoveBottom()278 void GeometryWidget::slotMoveBottom()
279 {
280     m_spinY->setValue(m_defaultSize.height() - m_spinHeight->value());
281 }
282 
283 /** @brief Un/Lock aspect ratio for size in effect parameter. */
slotLockRatio()284 void GeometryWidget::slotLockRatio()
285 {
286     auto *lockRatio = qobject_cast<QAction *>(QObject::sender());
287     if (lockRatio->isChecked()) {
288         m_monitor->setEffectSceneProperty(QStringLiteral("lockratio"), m_originalSize->isChecked() ? (double)m_sourceSize.width() / m_sourceSize.height()
289                                                                                                    : (double)m_defaultSize.width() / m_defaultSize.height());
290     } else {
291         m_monitor->setEffectSceneProperty(QStringLiteral("lockratio"), -1);
292     }
293 }
slotAdjustRectHeight()294 void GeometryWidget::slotAdjustRectHeight()
295 {
296     if (m_lockRatio->isChecked()) {
297         m_spinWidth->blockSignals(true);
298         if (m_originalSize->isChecked()) {
299             m_spinWidth->setValue((int)(m_spinHeight->value() * m_sourceSize.width() / m_sourceSize.height() + 0.5));
300         } else {
301             m_spinWidth->setValue((int)(m_spinHeight->value() * m_defaultSize.width() / m_defaultSize.height() + 0.5));
302         }
303         m_spinWidth->blockSignals(false);
304     }
305     adjustSizeValue();
306     slotAdjustRectKeyframeValue();
307 }
308 
slotAdjustRectWidth()309 void GeometryWidget::slotAdjustRectWidth()
310 {
311     if (m_lockRatio->isChecked()) {
312         m_spinHeight->blockSignals(true);
313         if (m_originalSize->isChecked()) {
314             m_spinHeight->setValue((int)(m_spinWidth->value() * m_sourceSize.height() / m_sourceSize.width() + 0.5));
315         } else {
316             m_spinHeight->setValue((int)(m_spinWidth->value() * m_defaultSize.height() / m_defaultSize.width() + 0.5));
317         }
318         m_spinHeight->blockSignals(false);
319     }
320     adjustSizeValue();
321     slotAdjustRectKeyframeValue();
322 }
323 
adjustSizeValue()324 void GeometryWidget::adjustSizeValue()
325 {
326     double size;
327     if ((double)m_spinWidth->value() / m_spinHeight->value() < pCore->getCurrentDar()) {
328         if (m_originalSize->isChecked()) {
329             size = m_spinWidth->value() * 100.0 / m_sourceSize.width();
330         } else {
331             size = m_spinWidth->value() * 100.0 / m_defaultSize.width();
332         }
333     } else {
334         if (m_originalSize->isChecked()) {
335             size = m_spinHeight->value() * 100.0 / m_sourceSize.height();
336         } else {
337             size = m_spinHeight->value() * 100.0 / m_defaultSize.height();
338         }
339     }
340     m_spinSize->blockSignals(true);
341     m_spinSize->setValue(size);
342     m_spinSize->blockSignals(false);
343 }
344 
slotAdjustRectKeyframeValue()345 void GeometryWidget::slotAdjustRectKeyframeValue()
346 {
347     QRect rect(m_spinX->value(), m_spinY->value(), m_spinWidth->value(), m_spinHeight->value());
348     emit updateMonitorGeometry(rect);
349     emit valueChanged(getValue());
350 }
351 
slotUpdateGeometryRect(const QRect r)352 void GeometryWidget::slotUpdateGeometryRect(const QRect r)
353 {
354     if (!r.isValid()) {
355         return;
356     }
357     m_spinX->blockSignals(true);
358     m_spinY->blockSignals(true);
359     m_spinWidth->blockSignals(true);
360     m_spinHeight->blockSignals(true);
361     m_spinX->setValue(r.x());
362     m_spinY->setValue(r.y());
363     m_spinWidth->setValue(r.width());
364     m_spinHeight->setValue(r.height());
365     m_spinX->blockSignals(false);
366     m_spinY->blockSignals(false);
367     m_spinWidth->blockSignals(false);
368     m_spinHeight->blockSignals(false);
369     //emit updateMonitorGeometry(r);
370     adjustSizeValue();
371     emit valueChanged(getValue());
372 }
373 
setValue(const QRect r,double opacity)374 void GeometryWidget::setValue(const QRect r, double opacity)
375 {
376     if (!r.isValid()) {
377         return;
378     }
379     m_spinX->blockSignals(true);
380     m_spinY->blockSignals(true);
381     m_spinWidth->blockSignals(true);
382     m_spinHeight->blockSignals(true);
383     m_spinX->setValue(r.x());
384     m_spinY->setValue(r.y());
385     m_spinWidth->setValue(r.width());
386     m_spinHeight->setValue(r.height());
387     if (m_opacity) {
388         m_opacity->blockSignals(true);
389         if (opacity < 0) {
390             opacity = 100 / m_opacityFactor;
391         }
392         m_opacity->setValue((int)(opacity * m_opacityFactor + 0.5));
393         m_opacity->blockSignals(false);
394     }
395     m_spinX->blockSignals(false);
396     m_spinY->blockSignals(false);
397     m_spinWidth->blockSignals(false);
398     m_spinHeight->blockSignals(false);
399     adjustSizeValue();
400     emit updateMonitorGeometry(r);
401 }
402 
getValue() const403 const QString GeometryWidget::getValue() const
404 {
405     if (m_opacity) {
406         return QStringLiteral("%1 %2 %3 %4 %5")
407             .arg(m_spinX->value())
408             .arg(m_spinY->value())
409             .arg(m_spinWidth->value())
410             .arg(m_spinHeight->value())
411             .arg(QString::number(m_opacity->value() / m_opacityFactor, 'f'));
412     }
413     return QStringLiteral("%1 %2 %3 %4").arg(m_spinX->value()).arg(m_spinY->value()).arg(m_spinWidth->value()).arg(m_spinHeight->value());
414 }
415 
connectMonitor(bool activate)416 void GeometryWidget::connectMonitor(bool activate)
417 {
418     if (m_active == activate) {
419         return;
420     }
421     m_active = activate;
422     if (activate) {
423         connect(m_monitor, &Monitor::effectChanged, this, &GeometryWidget::slotUpdateGeometryRect, Qt::UniqueConnection);
424         QRect rect(m_spinX->value(), m_spinY->value(), m_spinWidth->value(), m_spinHeight->value());
425         emit updateMonitorGeometry(rect);
426     } else {
427         m_monitor->setEffectKeyframe(false);
428         disconnect(m_monitor, &Monitor::effectChanged, this, &GeometryWidget::slotUpdateGeometryRect);
429     }
430 }
431 
slotSetRange(QPair<int,int> range)432 void GeometryWidget::slotSetRange(QPair<int, int> range)
433 {
434     m_min = range.first;
435     m_max = range.second;
436 }
437