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