]> git.sesse.net Git - kdenlive/blob - src/dragvalue.cpp
Bezier spline widget: use DragValue instead of SpinBoxes, range 0-1 with 3 decimals...
[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 <KIcon>
41 #include <KLocalizedString>
42 #include <KGlobalSettings>
43
44
45 DragValue::DragValue(const QString &label, double defaultValue, int decimals, int id, const QString suffix, bool showSlider, QWidget* parent) :
46         QWidget(parent),
47         m_maximum(100),
48         m_minimum(0),
49         m_decimals(decimals),
50         m_default(defaultValue),
51         m_id(id),
52         m_intEdit(NULL),
53         m_doubleEdit(NULL)
54 {
55     if (showSlider) setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
56     else setSizePolicy(QSizePolicy::Maximum, QSizePolicy::MinimumExpanding);
57     setFocusPolicy(Qt::StrongFocus);
58     setContextMenuPolicy(Qt::CustomContextMenu);
59     
60     QHBoxLayout *l = new QHBoxLayout;
61     l->setSpacing(0);
62     l->setContentsMargins(0, 0, 0, 0);
63     m_label = new CustomLabel(label, showSlider, decimals, this);
64     m_label->setCursor(Qt::PointingHandCursor);
65     m_label->setRange(m_minimum, m_maximum);
66     l->addWidget(m_label);
67     if (decimals == 0) {
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         l->addWidget(m_doubleEdit);
91         connect(m_doubleEdit, SIGNAL(valueChanged(double)), this, SLOT(slotSetValue(double)));
92         connect(m_doubleEdit, SIGNAL(editingFinished()), this, SLOT(slotEditingFinished()));
93     }
94     
95     connect(m_label, SIGNAL(valueChanged(double,bool)), this, SLOT(setValue(double,bool)));
96     connect(m_label, SIGNAL(resetValue()), this, SLOT(slotReset()));
97     setLayout(l);
98     if (m_intEdit) m_label->setMaximumHeight(m_intEdit->sizeHint().height());
99     else m_label->setMaximumHeight(m_doubleEdit->sizeHint().height());
100
101     m_menu = new QMenu(this);
102   
103     m_scale = new KSelectAction(i18n("Scaling"), this);
104     m_scale->addAction(i18n("Normal scale"));
105     m_scale->addAction(i18n("Pixel scale"));
106     m_scale->addAction(i18n("Nonlinear scale"));
107     m_scale->setCurrentItem(KdenliveSettings::dragvalue_mode());
108     m_menu->addAction(m_scale);
109     
110     m_directUpdate = new QAction(i18n("Direct update"), this);
111     m_directUpdate->setCheckable(true);
112     m_directUpdate->setChecked(KdenliveSettings::dragvalue_directupdate());
113     m_menu->addAction(m_directUpdate);
114     
115     QAction *reset = new QAction(KIcon("edit-undo"), i18n("Reset value"), this);
116     connect(reset, SIGNAL(triggered()), this, SLOT(slotReset()));
117     m_menu->addAction(reset);
118     
119     if (m_id > -1) {
120         QAction *timeline = new QAction(KIcon("go-jump"), i18n("Show %1 in timeline", label), this);
121         connect(timeline, SIGNAL(triggered()), this, SLOT(slotSetInTimeline()));
122         connect(m_label, SIGNAL(setInTimeline()), this, SLOT(slotSetInTimeline()));
123         m_menu->addAction(timeline);
124     }
125
126     connect(this, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(slotShowContextMenu(const QPoint&)));
127     connect(m_scale, SIGNAL(triggered(int)), this, SLOT(slotSetScaleMode(int)));
128     connect(m_directUpdate, SIGNAL(triggered(bool)), this, SLOT(slotSetDirectUpdate(bool)));
129 }
130
131
132 DragValue::~DragValue()
133 {
134     if (m_intEdit) delete m_intEdit;
135     if (m_doubleEdit) delete m_doubleEdit;
136     delete m_menu;
137     delete m_scale;
138     delete m_directUpdate;
139 }
140
141 int DragValue::spinSize()
142 {
143     if (m_intEdit) return m_intEdit->sizeHint().width();
144     else return m_doubleEdit->sizeHint().width();
145 }
146
147 void DragValue::setSpinSize(int width)
148 {
149     if (m_intEdit) m_intEdit->setMinimumWidth(width);
150     else m_doubleEdit->setMinimumWidth(width);
151 }
152
153 void DragValue::slotSetInTimeline()
154 {
155     emit inTimeline(m_id);
156 }
157
158 int DragValue::precision() const
159 {
160     return m_decimals;
161 }
162
163 qreal DragValue::maximum() const
164 {
165     return m_maximum;
166 }
167
168 qreal DragValue::minimum() const
169 {
170     return m_minimum;
171 }
172
173 qreal DragValue::value() const
174 {
175     if (m_intEdit) return m_intEdit->value();
176     else return m_doubleEdit->value();
177 }
178
179 void DragValue::setMaximum(qreal max)
180 {
181     m_maximum = max;
182     m_label->setRange(m_minimum, m_maximum);
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     m_label->setRange(m_minimum, m_maximum);
191     if (m_intEdit) m_intEdit->setRange(m_minimum, m_maximum);
192     else m_doubleEdit->setRange(m_minimum, m_maximum);
193 }
194
195 void DragValue::setRange(qreal min, qreal max)
196 {
197     m_maximum = max;
198     m_minimum = min;
199     m_label->setRange(m_minimum, m_maximum);
200     if (m_intEdit) m_intEdit->setRange(m_minimum, m_maximum);
201     else m_doubleEdit->setRange(m_minimum, m_maximum);
202 }
203
204 void DragValue::setPrecision(int /*precision*/)
205 {
206     //TODO: Not implemented, in case we need double value, we should replace the KIntSpinBox with KDoubleNumInput...
207     /*m_precision = precision;
208     if (precision == 0)
209         m_edit->setValidator(new QIntValidator(m_minimum, m_maximum, this));
210     else
211         m_edit->setValidator(new QDoubleValidator(m_minimum, m_maximum, precision, this));*/
212 }
213
214 void DragValue::setStep(qreal step)
215 {
216     m_label->setStep(step);
217     if (m_intEdit)
218         m_intEdit->setSingleStep(step);
219     else
220         m_doubleEdit->setSingleStep(step);
221 }
222
223 void DragValue::slotReset()
224 {
225     if (m_intEdit) {
226         m_intEdit->blockSignals(true);
227         m_intEdit->setValue(m_default);
228         m_intEdit->blockSignals(false);
229         emit valueChanged((int) m_default, true);
230     }
231     else {
232         m_doubleEdit->blockSignals(true);
233         m_doubleEdit->setValue(m_default);
234         m_doubleEdit->blockSignals(false);
235         emit valueChanged(m_default, true);
236     }
237     m_label->setProgressValue(m_default);
238 }
239
240 void DragValue::slotSetValue(int value)
241 {
242     setValue(value, KdenliveSettings::dragvalue_directupdate());
243 }
244
245 void DragValue::slotSetValue(double value)
246 {
247     setValue(value, KdenliveSettings::dragvalue_directupdate());
248 }
249
250 void DragValue::setValue(double value, bool final)
251 {
252     value = qBound(m_minimum, value, m_maximum);
253     if (m_intEdit) {
254         m_intEdit->blockSignals(true);
255         m_intEdit->setValue((int) value);
256         m_intEdit->blockSignals(false);
257         emit valueChanged((int) value, final);
258     }
259     else {
260         m_doubleEdit->blockSignals(true);
261         m_doubleEdit->setValue(value);
262         m_doubleEdit->blockSignals(false);
263         emit valueChanged(value, final);
264     }
265     m_label->setProgressValue(value);
266 }
267
268 void DragValue::focusInEvent(QFocusEvent* e)
269 {
270     if (e->reason() == Qt::TabFocusReason || e->reason() == Qt::BacktabFocusReason) {
271         if (m_intEdit) m_intEdit->setFocus(e->reason());
272         else m_doubleEdit->setFocus(e->reason());
273     } else {
274         QWidget::focusInEvent(e);
275     }
276 }
277
278 void DragValue::slotEditingFinished()
279 {
280     if (m_intEdit) {
281         int value = m_intEdit->value();
282         m_intEdit->blockSignals(true);
283         m_intEdit->clearFocus();
284         m_intEdit->blockSignals(false);
285         if (!KdenliveSettings::dragvalue_directupdate()) emit valueChanged(value, true);
286     }
287     else {
288         double value = m_doubleEdit->value();
289         m_doubleEdit->blockSignals(true);
290         m_doubleEdit->clearFocus();
291         m_doubleEdit->blockSignals(false);
292         if (!KdenliveSettings::dragvalue_directupdate()) emit valueChanged(value, true);
293     }
294 }
295
296 void DragValue::slotShowContextMenu(const QPoint& pos)
297 {
298     // values might have been changed by another object of this class
299     m_scale->setCurrentItem(KdenliveSettings::dragvalue_mode());
300     m_directUpdate->setChecked(KdenliveSettings::dragvalue_directupdate());
301     m_menu->exec(mapToGlobal(pos));
302 }
303
304 void DragValue::slotSetScaleMode(int mode)
305 {
306     KdenliveSettings::setDragvalue_mode(mode);
307 }
308
309 void DragValue::slotSetDirectUpdate(bool directUpdate)
310 {
311     KdenliveSettings::setDragvalue_directupdate(directUpdate);
312 }
313
314 void DragValue::setInTimelineProperty(bool intimeline)
315 {
316     if (m_label->property("inTimeline").toBool() == intimeline) return;
317     m_label->setProperty("inTimeline", intimeline);
318     style()->unpolish(m_label);
319     style()->polish(m_label);
320     m_label->update();
321     if (m_intEdit) {
322         m_intEdit->setProperty("inTimeline", intimeline);
323         style()->unpolish(m_intEdit);
324         style()->polish(m_intEdit);
325         m_intEdit->update();
326     }
327     else {
328         m_doubleEdit->setProperty("inTimeline", intimeline);
329         style()->unpolish(m_doubleEdit);
330         style()->polish(m_doubleEdit);
331         m_doubleEdit->update();
332     }
333     
334 }
335
336 CustomLabel::CustomLabel(const QString &label, bool showSlider, int precision, QWidget* parent) :
337     QProgressBar(parent),
338     m_dragMode(false),
339     m_step(1.0),
340     m_showSlider(showSlider),
341     m_precision(pow(10, precision)),
342     m_value(0)
343 {
344     setFont(KGlobalSettings::toolBarFont());
345     setFormat(" " + label);
346     setFocusPolicy(Qt::ClickFocus);
347     setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Maximum);
348     if (!showSlider) {
349         QSize sh;
350         const QFontMetrics &fm = fontMetrics();
351         sh.setWidth(fm.width(" " + label + " "));
352         setMaximumWidth(sh.width());
353         setObjectName("dragOnly");
354     }
355     setValue(0);
356 }
357
358 void CustomLabel::mousePressEvent(QMouseEvent* e)
359 {
360     if (e->button() == Qt::LeftButton) {
361         m_dragStartPosition = m_dragLastPosition = e->pos();
362         e->accept();
363     }
364     else if (e->button() == Qt::MidButton) {
365         emit resetValue();
366         m_dragStartPosition = QPoint(-1, -1);
367     }
368     else QWidget::mousePressEvent(e);
369 }
370
371 void CustomLabel::mouseMoveEvent(QMouseEvent* e)
372 {
373     if (m_dragStartPosition != QPoint(-1, -1)) {
374         if (!m_dragMode && (e->pos() - m_dragStartPosition).manhattanLength() >= QApplication::startDragDistance()) {
375             m_dragMode = true;
376             m_dragLastPosition = e->pos();
377             e->accept();
378             return;
379         }
380         if (m_dragMode) {
381             if (KdenliveSettings::dragvalue_mode() > 0 || !m_showSlider) {
382                 int diff = e->x() - m_dragLastPosition.x();
383
384                 if (e->modifiers() == Qt::ControlModifier)
385                     diff *= 2;
386                 else if (e->modifiers() == Qt::ShiftModifier)
387                     diff /= 2;
388                 if (KdenliveSettings::dragvalue_mode() == 2)
389                     diff = (diff > 0 ? 1 : -1) * pow(diff, 2);
390
391                 double nv = m_value + diff * m_step;
392                 if (nv != m_value) setNewValue(nv, KdenliveSettings::dragvalue_directupdate());
393             }
394             else {
395                 double nv = minimum() + ((double) maximum() - minimum()) / width() * e->pos().x();
396                 if (nv != m_value) setNewValue(nv, KdenliveSettings::dragvalue_directupdate());
397             }
398             m_dragLastPosition = e->pos();
399             e->accept();
400         }
401     }
402     else QWidget::mouseMoveEvent(e);
403 }
404
405 void CustomLabel::mouseReleaseEvent(QMouseEvent* e)
406 {
407     if (e->button() == Qt::MidButton) {
408         e->accept();
409         return;
410     }
411     if (e->modifiers() == Qt::ControlModifier) {
412         emit setInTimeline();
413         e->accept();
414         return;
415     }
416     if (m_dragMode) {
417         setNewValue(m_value, true);
418         m_dragLastPosition = m_dragStartPosition;
419         e->accept();
420     }
421     else if (m_showSlider) {
422         setNewValue((minimum() + ((double)maximum() - minimum()) / width() * e->pos().x()), true);
423         m_dragLastPosition = m_dragStartPosition;
424         e->accept();
425     }
426     m_dragMode = false;
427 }
428
429 void CustomLabel::wheelEvent(QWheelEvent* e)
430 {
431     if (e->delta() > 0) {
432         if (e->modifiers() == Qt::ControlModifier) slotValueInc(m_step * 10);
433         else if (e->modifiers() == Qt::AltModifier) slotValueInc(m_step / m_precision);
434         else slotValueInc();
435     }
436     else {
437         if (e->modifiers() == Qt::ControlModifier) slotValueDec(m_step * 10);
438         else if (e->modifiers() == Qt::AltModifier) slotValueDec(m_step / m_precision);
439         else slotValueDec();
440     }
441     e->accept();
442 }
443
444 void CustomLabel::slotValueInc(double factor)
445 {
446     setNewValue(m_value + m_step * factor, true);
447 }
448
449 void CustomLabel::slotValueDec(double factor)
450 {
451     setNewValue(m_value - m_step * factor, true);
452 }
453
454 void CustomLabel::setProgressValue(double value)
455 {
456     m_value = value;
457     setValue((int) value);
458 }
459
460 void CustomLabel::setNewValue(double value, bool update)
461 {
462     m_value = value;
463     setValue(value);
464     emit valueChanged(value, update);
465 }
466
467 void CustomLabel::setStep(double step)
468 {
469     m_step = step;
470 }
471
472 #include "dragvalue.moc"
473