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