]> git.sesse.net Git - kdenlive/blob - src/dragvalue.cpp
Add proper support for 'float' parameter type in effect's drag widget (required for...
[kdenlive] / src / dragvalue.cpp
1 /***************************************************************************
2  *   Copyright (C) 2011 by Till Theato (root@ttill.de)                     *
3  *   Copyright (C) 2011 by Jean-Baptiste Mardelle (jb@kdenlive.org)        *
4  *   This file is part of Kdenlive (www.kdenlive.org).                     *
5  *                                                                         *
6  *   Kdenlive is free software: you can redistribute it and/or modify      *
7  *   it under the terms of the GNU General Public License as published by  *
8  *   the Free Software Foundation, either version 2 of the License, or     *
9  *   (at your option) any later version.                                   *
10  *                                                                         *
11  *   Kdenlive is distributed in the hope that it will be useful,           *
12  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
13  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
14  *   GNU General Public License for more details.                          *
15  *                                                                         *
16  *   You should have received a copy of the GNU General Public License     *
17  *   along with Kdenlive.  If not, see <http://www.gnu.org/licenses/>.     *
18  ***************************************************************************/
19
20 #include "dragvalue.h"
21 #include "kdenlivesettings.h"
22
23 #include <math.h>
24
25 #include <QHBoxLayout>
26 #include <QToolButton>
27 #include <QLineEdit>
28 #include <QDoubleValidator>
29 #include <QIntValidator>
30 #include <QMouseEvent>
31 #include <QFocusEvent>
32 #include <QWheelEvent>
33 #include <QApplication>
34 #include <QMenu>
35 #include <QAction>
36 #include <QLabel>
37 #include <QPainter>
38 #include <QStyle>
39
40 #include <KDebug>
41 #include <KIcon>
42 #include <KLocalizedString>
43 #include <KGlobalSettings>
44
45
46 DragValue::DragValue(const QString &label, double defaultValue, int decimals, double min, double max, int id, const QString suffix, bool showSlider, QWidget* parent) :
47         QWidget(parent),
48         m_maximum(max),
49         m_minimum(min),
50         m_decimals(decimals),
51         m_default(defaultValue),
52         m_id(id),
53         m_intEdit(NULL),
54         m_doubleEdit(NULL)
55 {
56     if (showSlider) setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
57     else setSizePolicy(QSizePolicy::Maximum, QSizePolicy::MinimumExpanding);
58     setFocusPolicy(Qt::StrongFocus);
59     setContextMenuPolicy(Qt::CustomContextMenu);
60     
61     QHBoxLayout *l = new QHBoxLayout;
62     l->setSpacing(0);
63     l->setContentsMargins(0, 0, 0, 0);
64     m_label = new CustomLabel(label, showSlider, this);
65     l->addWidget(m_label);
66     if (decimals == 0) {
67         m_label->setStep(m_label->maximum() / (max - min));
68         m_intEdit = new QSpinBox(this);
69         m_intEdit->setObjectName("dragBox");
70         if (!suffix.isEmpty()) m_intEdit->setSuffix(suffix);
71         m_intEdit->setKeyboardTracking(false);
72         m_intEdit->setButtonSymbols(QAbstractSpinBox::NoButtons);
73         m_intEdit->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
74         m_intEdit->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum);
75         m_intEdit->setRange((int) m_minimum, (int)m_maximum);
76         l->addWidget(m_intEdit);
77         connect(m_intEdit, SIGNAL(valueChanged(int)), this, SLOT(slotSetValue(int)));
78         connect(m_intEdit, SIGNAL(editingFinished()), this, SLOT(slotEditingFinished()));
79     }
80     else {
81         m_doubleEdit = new QDoubleSpinBox(this);
82         m_doubleEdit->setDecimals(decimals);
83         m_doubleEdit->setObjectName("dragBox");
84         if (!suffix.isEmpty()) m_doubleEdit->setSuffix(suffix);
85         m_doubleEdit->setKeyboardTracking(false);
86         m_doubleEdit->setButtonSymbols(QAbstractSpinBox::NoButtons);
87         m_doubleEdit->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
88         m_doubleEdit->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum);
89         m_doubleEdit->setRange(m_minimum, m_maximum);
90         m_doubleEdit->setSingleStep((m_maximum - m_minimum) / 100.0);
91         l->addWidget(m_doubleEdit);
92         connect(m_doubleEdit, SIGNAL(valueChanged(double)), this, SLOT(slotSetValue(double)));
93         connect(m_doubleEdit, SIGNAL(editingFinished()), this, SLOT(slotEditingFinished()));
94     }
95     
96     connect(m_label, SIGNAL(valueChanged(double,bool)), this, SLOT(setValueFromProgress(double,bool)));
97     connect(m_label, SIGNAL(resetValue()), this, SLOT(slotReset()));
98     setLayout(l);
99     if (m_intEdit) m_label->setMaximumHeight(m_intEdit->sizeHint().height());
100     else m_label->setMaximumHeight(m_doubleEdit->sizeHint().height());
101
102     m_menu = new QMenu(this);
103   
104     m_scale = new KSelectAction(i18n("Scaling"), this);
105     m_scale->addAction(i18n("Normal scale"));
106     m_scale->addAction(i18n("Pixel scale"));
107     m_scale->addAction(i18n("Nonlinear scale"));
108     m_scale->setCurrentItem(KdenliveSettings::dragvalue_mode());
109     m_menu->addAction(m_scale);
110     
111     m_directUpdate = new QAction(i18n("Direct update"), this);
112     m_directUpdate->setCheckable(true);
113     m_directUpdate->setChecked(KdenliveSettings::dragvalue_directupdate());
114     m_menu->addAction(m_directUpdate);
115     
116     QAction *reset = new QAction(KIcon("edit-undo"), i18n("Reset value"), this);
117     connect(reset, SIGNAL(triggered()), this, SLOT(slotReset()));
118     m_menu->addAction(reset);
119     
120     if (m_id > -1) {
121         QAction *timeline = new QAction(KIcon("go-jump"), i18n("Show %1 in timeline", label), this);
122         connect(timeline, SIGNAL(triggered()), this, SLOT(slotSetInTimeline()));
123         connect(m_label, SIGNAL(setInTimeline()), this, SLOT(slotSetInTimeline()));
124         m_menu->addAction(timeline);
125     }
126
127     connect(this, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(slotShowContextMenu(const QPoint&)));
128     connect(m_scale, SIGNAL(triggered(int)), this, SLOT(slotSetScaleMode(int)));
129     connect(m_directUpdate, SIGNAL(triggered(bool)), this, SLOT(slotSetDirectUpdate(bool)));
130 }
131
132
133 DragValue::~DragValue()
134 {
135     if (m_intEdit) delete m_intEdit;
136     if (m_doubleEdit) delete m_doubleEdit;
137     delete m_menu;
138     delete m_scale;
139     delete m_directUpdate;
140 }
141
142 int DragValue::spinSize()
143 {
144     if (m_intEdit) return m_intEdit->sizeHint().width();
145     else return m_doubleEdit->sizeHint().width();
146 }
147
148 void DragValue::setSpinSize(int width)
149 {
150     if (m_intEdit) m_intEdit->setMinimumWidth(width);
151     else m_doubleEdit->setMinimumWidth(width);
152 }
153
154 void DragValue::slotSetInTimeline()
155 {
156     emit inTimeline(m_id);
157 }
158
159 int DragValue::precision() const
160 {
161     return m_decimals;
162 }
163
164 qreal DragValue::maximum() const
165 {
166     return m_maximum;
167 }
168
169 qreal DragValue::minimum() const
170 {
171     return m_minimum;
172 }
173
174 qreal DragValue::value() const
175 {
176     if (m_intEdit) return m_intEdit->value();
177     else return m_doubleEdit->value();
178 }
179
180 void DragValue::setMaximum(qreal max)
181 {
182     m_maximum = max;
183     if (m_intEdit) m_intEdit->setRange(m_minimum, m_maximum);
184     else m_doubleEdit->setRange(m_minimum, m_maximum);
185 }
186
187 void DragValue::setMinimum(qreal min)
188 {
189     m_minimum = min;
190     if (m_intEdit) m_intEdit->setRange(m_minimum, m_maximum);
191     else m_doubleEdit->setRange(m_minimum, m_maximum);
192 }
193
194 void DragValue::setRange(qreal min, qreal max)
195 {
196     m_maximum = max;
197     m_minimum = min;
198     if (m_intEdit) m_intEdit->setRange(m_minimum, m_maximum);
199     else m_doubleEdit->setRange(m_minimum, m_maximum);
200 }
201
202 void DragValue::setPrecision(int /*precision*/)
203 {
204     //TODO: Not implemented, in case we need double value, we should replace the KIntSpinBox with KDoubleNumInput...
205     /*m_precision = precision;
206     if (precision == 0)
207         m_edit->setValidator(new QIntValidator(m_minimum, m_maximum, this));
208     else
209         m_edit->setValidator(new QDoubleValidator(m_minimum, m_maximum, precision, this));*/
210 }
211
212 void DragValue::setStep(qreal step)
213 {
214     if (m_intEdit)
215         m_intEdit->setSingleStep(step);
216     else
217         m_doubleEdit->setSingleStep(step);
218 }
219
220 void DragValue::slotReset()
221 {
222     if (m_intEdit) {
223         m_intEdit->blockSignals(true);
224         m_intEdit->setValue(m_default);
225         m_intEdit->blockSignals(false);
226         emit valueChanged((int) m_default, true);
227     }
228     else {
229         m_doubleEdit->blockSignals(true);
230         m_doubleEdit->setValue(m_default);
231         m_doubleEdit->blockSignals(false);
232         emit valueChanged(m_default, true);
233     }
234     m_label->setProgressValue((m_default - m_minimum) / (m_maximum - m_minimum) * m_label->maximum());
235 }
236
237 void DragValue::slotSetValue(int value)
238 {
239    setValue(value, KdenliveSettings::dragvalue_directupdate());
240 }
241
242 void DragValue::slotSetValue(double value)
243 {
244     setValue(value, KdenliveSettings::dragvalue_directupdate());
245 }
246
247 void DragValue::setValueFromProgress(double value, bool final)
248 {
249     value = m_minimum + value * (m_maximum - m_minimum) / m_label->maximum();
250     if (m_decimals == 0) setValue((int) (value + 0.5), final);
251     else setValue(value, final);
252 }
253
254 void DragValue::setValue(double value, bool final)
255 {
256     value = qBound(m_minimum, value, m_maximum);
257     if (m_intEdit) {
258         m_intEdit->blockSignals(true);
259         m_intEdit->setValue((int) value);
260         m_intEdit->blockSignals(false);
261         emit valueChanged((int) value, final);
262     }
263     else {
264         m_doubleEdit->blockSignals(true);
265         m_doubleEdit->setValue(value);
266         m_doubleEdit->blockSignals(false);
267         emit valueChanged(value, final);
268     }
269
270     m_label->setProgressValue((value - m_minimum) / (m_maximum - m_minimum) * m_label->maximum());
271 }
272
273 void DragValue::focusInEvent(QFocusEvent* e)
274 {
275     if (e->reason() == Qt::TabFocusReason || e->reason() == Qt::BacktabFocusReason) {
276         if (m_intEdit) m_intEdit->setFocus(e->reason());
277         else m_doubleEdit->setFocus(e->reason());
278     } else {
279         QWidget::focusInEvent(e);
280     }
281 }
282
283 void DragValue::slotEditingFinished()
284 {
285     if (m_intEdit) {
286         int value = m_intEdit->value();
287         m_intEdit->blockSignals(true);
288         m_intEdit->clearFocus();
289         m_intEdit->blockSignals(false);
290         if (!KdenliveSettings::dragvalue_directupdate()) emit valueChanged(value, true);
291     }
292     else {
293         double value = m_doubleEdit->value();
294         m_doubleEdit->blockSignals(true);
295         m_doubleEdit->clearFocus();
296         m_doubleEdit->blockSignals(false);
297         if (!KdenliveSettings::dragvalue_directupdate()) emit valueChanged(value, true);
298     }
299 }
300
301 void DragValue::slotShowContextMenu(const QPoint& pos)
302 {
303     // values might have been changed by another object of this class
304     m_scale->setCurrentItem(KdenliveSettings::dragvalue_mode());
305     m_directUpdate->setChecked(KdenliveSettings::dragvalue_directupdate());
306     m_menu->exec(mapToGlobal(pos));
307 }
308
309 void DragValue::slotSetScaleMode(int mode)
310 {
311     KdenliveSettings::setDragvalue_mode(mode);
312 }
313
314 void DragValue::slotSetDirectUpdate(bool directUpdate)
315 {
316     KdenliveSettings::setDragvalue_directupdate(directUpdate);
317 }
318
319 void DragValue::setInTimelineProperty(bool intimeline)
320 {
321     if (m_label->property("inTimeline").toBool() == intimeline) return;
322     m_label->setProperty("inTimeline", intimeline);
323     style()->unpolish(m_label);
324     style()->polish(m_label);
325     m_label->update();
326     if (m_intEdit) {
327         m_intEdit->setProperty("inTimeline", intimeline);
328         style()->unpolish(m_intEdit);
329         style()->polish(m_intEdit);
330         m_intEdit->update();
331     }
332     else {
333         m_doubleEdit->setProperty("inTimeline", intimeline);
334         style()->unpolish(m_doubleEdit);
335         style()->polish(m_doubleEdit);
336         m_doubleEdit->update();
337     }
338     
339 }
340
341 CustomLabel::CustomLabel(const QString &label, bool showSlider, QWidget* parent) :
342     QProgressBar(parent),
343     m_dragMode(false),
344     m_showSlider(showSlider),
345     m_step(10.0)
346     //m_precision(pow(10, precision)),
347 {
348     setFont(KGlobalSettings::toolBarFont());
349     setFormat(" " + label);
350     setFocusPolicy(Qt::ClickFocus);
351     setCursor(Qt::PointingHandCursor);
352     setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Maximum);
353     setRange(0, 1000);
354     
355     if (!showSlider) {
356         QSize sh;
357         const QFontMetrics &fm = fontMetrics();
358         sh.setWidth(fm.width(" " + label + " "));
359         setMaximumWidth(sh.width());
360         setObjectName("dragOnly");
361     }
362     setValue(0);
363 }
364
365 void CustomLabel::mousePressEvent(QMouseEvent* e)
366 {
367     if (e->button() == Qt::LeftButton) {
368         m_dragStartPosition = m_dragLastPosition = e->pos();
369         e->accept();
370     }
371     else if (e->button() == Qt::MidButton) {
372         emit resetValue();
373         m_dragStartPosition = QPoint(-1, -1);
374     }
375     else QWidget::mousePressEvent(e);
376 }
377
378 void CustomLabel::mouseMoveEvent(QMouseEvent* e)
379 {
380     if (m_dragStartPosition != QPoint(-1, -1)) {
381         if (!m_dragMode && (e->pos() - m_dragStartPosition).manhattanLength() >= QApplication::startDragDistance()) {
382             m_dragMode = true;
383             m_dragLastPosition = e->pos();
384             e->accept();
385             return;
386         }
387         if (m_dragMode) {
388             if (KdenliveSettings::dragvalue_mode() > 0 || !m_showSlider) {
389                 int diff = e->x() - m_dragLastPosition.x();
390
391                 if (e->modifiers() == Qt::ControlModifier)
392                     diff *= 2;
393                 else if (e->modifiers() == Qt::ShiftModifier)
394                     diff /= 2;
395                 if (KdenliveSettings::dragvalue_mode() == 2)
396                     diff = (diff > 0 ? 1 : -1) * pow(diff, 2);
397
398                 double nv = value() + diff * m_step;
399                 if (nv != value()) setNewValue(nv, KdenliveSettings::dragvalue_directupdate());
400             }
401             else {
402                 double nv = minimum() + ((double) maximum() - minimum()) / width() * e->pos().x();
403                 if (nv != value()) setNewValue(nv, KdenliveSettings::dragvalue_directupdate());
404             }
405             m_dragLastPosition = e->pos();
406             e->accept();
407         }
408     }
409     else QWidget::mouseMoveEvent(e);
410 }
411
412 void CustomLabel::mouseReleaseEvent(QMouseEvent* e)
413 {
414     if (e->button() == Qt::MidButton) {
415         e->accept();
416         return;
417     }
418     if (e->modifiers() == Qt::ControlModifier) {
419         emit setInTimeline();
420         e->accept();
421         return;
422     }
423     if (m_dragMode) {
424         setNewValue(value(), true);
425         m_dragLastPosition = m_dragStartPosition;
426         e->accept();
427     }
428     else if (m_showSlider) {
429         setNewValue((double) maximum() * e->pos().x() / width(), true);
430         m_dragLastPosition = m_dragStartPosition;
431         e->accept();
432     }
433     m_dragMode = false;
434 }
435
436 void CustomLabel::wheelEvent(QWheelEvent* e)
437 {
438     if (e->delta() > 0) {
439         if (e->modifiers() == Qt::ControlModifier) slotValueInc(10);
440         else if (e->modifiers() == Qt::AltModifier) slotValueInc(0.1);
441         else slotValueInc();
442     }
443     else {
444         if (e->modifiers() == Qt::ControlModifier) slotValueDec(10);
445         else if (e->modifiers() == Qt::AltModifier) slotValueDec(0.1);
446         else slotValueDec();
447     }
448     e->accept();
449 }
450
451 void CustomLabel::slotValueInc(double factor)
452 {
453     setNewValue(value() + m_step * factor, true);
454 }
455
456 void CustomLabel::slotValueDec(double factor)
457 {
458     setNewValue(value() - m_step * factor, true);
459 }
460
461 void CustomLabel::setProgressValue(double value)
462 {
463     setValue((int) (value + 0.5));
464 }
465
466 void CustomLabel::setNewValue(double value, bool update)
467 {
468     setValue((int) (value + 0.5));
469     emit valueChanged((int) (value + 0.5), update);
470 }
471
472 void CustomLabel::setStep(double step)
473 {
474     m_step = step;
475 }
476
477 #include "dragvalue.moc"
478