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). *
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. *
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. *
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 ***************************************************************************/
20 #include "dragvalue.h"
21 #include "kdenlivesettings.h"
25 #include <QHBoxLayout>
26 #include <QToolButton>
28 #include <QDoubleValidator>
29 #include <QIntValidator>
30 #include <QMouseEvent>
31 #include <QFocusEvent>
32 #include <QWheelEvent>
33 #include <QApplication>
42 #include <KLocalizedString>
43 #include <KGlobalSettings>
46 DragValue::DragValue(const QString &label, double defaultValue, int decimals, double min, double max, int id, const QString suffix, bool showSlider, QWidget* parent) :
51 m_default(defaultValue),
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);
62 QHBoxLayout *l = new QHBoxLayout;
64 l->setContentsMargins(0, 0, 0, 0);
65 m_label = new CustomLabel(label, showSlider, m_maximum - m_minimum, this);
66 l->addWidget(m_label);
68 m_label->setMaximum(max - min);
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()));
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);
95 if (m_maximum - m_minimum > 10000) factor = 1000;
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()));
103 connect(m_label, SIGNAL(valueChanged(double,bool)), this, SLOT(setValueFromProgress(double,bool)));
104 connect(m_label, SIGNAL(resetValue()), this, SLOT(slotReset()));
106 if (m_intEdit) m_label->setMaximumHeight(m_intEdit->sizeHint().height());
107 else m_label->setMaximumHeight(m_doubleEdit->sizeHint().height());
109 m_menu = new QMenu(this);
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);
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);
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);
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);
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)));
140 DragValue::~DragValue()
142 if (m_intEdit) delete m_intEdit;
143 if (m_doubleEdit) delete m_doubleEdit;
146 //delete m_directUpdate;
149 int DragValue::spinSize()
151 if (m_intEdit) return m_intEdit->sizeHint().width();
152 else return m_doubleEdit->sizeHint().width();
155 void DragValue::setSpinSize(int width)
157 if (m_intEdit) m_intEdit->setMinimumWidth(width);
158 else m_doubleEdit->setMinimumWidth(width);
161 void DragValue::slotSetInTimeline()
163 emit inTimeline(m_id);
166 int DragValue::precision() const
171 qreal DragValue::maximum() const
176 qreal DragValue::minimum() const
181 qreal DragValue::value() const
183 if (m_intEdit) return m_intEdit->value();
184 else return m_doubleEdit->value();
187 void DragValue::setMaximum(qreal max)
190 if (m_intEdit) m_intEdit->setRange(m_minimum, m_maximum);
191 else m_doubleEdit->setRange(m_minimum, m_maximum);
194 void DragValue::setMinimum(qreal min)
197 if (m_intEdit) m_intEdit->setRange(m_minimum, m_maximum);
198 else m_doubleEdit->setRange(m_minimum, m_maximum);
201 void DragValue::setRange(qreal min, qreal max)
205 if (m_intEdit) m_intEdit->setRange(m_minimum, m_maximum);
206 else m_doubleEdit->setRange(m_minimum, m_maximum);
209 void DragValue::setPrecision(int /*precision*/)
211 //TODO: Not implemented, in case we need double value, we should replace the KIntSpinBox with KDoubleNumInput...
212 /*m_precision = precision;
214 m_edit->setValidator(new QIntValidator(m_minimum, m_maximum, this));
216 m_edit->setValidator(new QDoubleValidator(m_minimum, m_maximum, precision, this));*/
219 void DragValue::setStep(qreal step)
222 m_intEdit->setSingleStep(step);
224 m_doubleEdit->setSingleStep(step);
227 void DragValue::slotReset()
230 m_intEdit->blockSignals(true);
231 m_intEdit->setValue(m_default);
232 m_intEdit->blockSignals(false);
233 emit valueChanged((int) m_default, true);
236 m_doubleEdit->blockSignals(true);
237 m_doubleEdit->setValue(m_default);
238 m_doubleEdit->blockSignals(false);
239 emit valueChanged(m_default, true);
241 m_label->setProgressValue((m_default - m_minimum) / (m_maximum - m_minimum) * m_label->maximum());
244 void DragValue::slotSetValue(int value)
246 setValue(value, true);
249 void DragValue::slotSetValue(double value)
251 setValue(value, true);
254 void DragValue::setValueFromProgress(double value, bool final)
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);
261 void DragValue::setValue(double value, bool final)
263 value = qBound(m_minimum, value, m_maximum);
265 m_intEdit->blockSignals(true);
266 m_intEdit->setValue((int) value);
267 m_intEdit->blockSignals(false);
268 emit valueChanged((int) value, final);
271 m_doubleEdit->blockSignals(true);
272 m_doubleEdit->setValue(value);
273 m_doubleEdit->blockSignals(false);
274 emit valueChanged(value, final);
277 m_label->setProgressValue((value - m_minimum) / (m_maximum - m_minimum) * m_label->maximum());
280 void DragValue::focusOutEvent(QFocusEvent*)
282 if (m_intEdit) m_intEdit->setFocusPolicy(Qt::StrongFocus);
283 else m_doubleEdit->setFocusPolicy(Qt::StrongFocus);
286 void DragValue::focusInEvent(QFocusEvent* e)
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());
294 QWidget::focusInEvent(e);
298 void DragValue::slotEditingFinished()
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);
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);
316 void DragValue::slotShowContextMenu(const QPoint& pos)
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));
324 void DragValue::slotSetScaleMode(int mode)
326 KdenliveSettings::setDragvalue_mode(mode);
329 void DragValue::slotSetDirectUpdate(bool directUpdate)
331 KdenliveSettings::setDragvalue_directupdate(directUpdate);
334 void DragValue::setInTimelineProperty(bool intimeline)
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);
342 m_intEdit->setProperty("inTimeline", intimeline);
343 style()->unpolish(m_intEdit);
344 style()->polish(m_intEdit);
348 m_doubleEdit->setProperty("inTimeline", intimeline);
349 style()->unpolish(m_doubleEdit);
350 style()->polish(m_doubleEdit);
351 m_doubleEdit->update();
356 CustomLabel::CustomLabel(const QString &label, bool showSlider, int range, QWidget* parent) :
357 QProgressBar(parent),
359 m_showSlider(showSlider),
361 //m_precision(pow(10, precision)),
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);
372 const QFontMetrics &fm = fontMetrics();
373 sh.setWidth(fm.width(' ' + label + ' '));
374 setMaximumWidth(sh.width());
375 setObjectName("dragOnly");
380 void CustomLabel::mousePressEvent(QMouseEvent* e)
382 if (e->button() == Qt::LeftButton) {
383 m_dragStartPosition = m_dragLastPosition = e->pos();
386 else if (e->button() == Qt::MidButton) {
388 m_dragStartPosition = QPoint(-1, -1);
390 else QWidget::mousePressEvent(e);
393 void CustomLabel::mouseMoveEvent(QMouseEvent* e)
395 if (m_dragStartPosition != QPoint(-1, -1)) {
396 if (!m_dragMode && (e->pos() - m_dragStartPosition).manhattanLength() >= QApplication::startDragDistance()) {
398 m_dragLastPosition = e->pos();
403 if (KdenliveSettings::dragvalue_mode() > 0 || !m_showSlider) {
404 int diff = e->x() - m_dragLastPosition.x();
406 if (e->modifiers() == Qt::ControlModifier)
408 else if (e->modifiers() == Qt::ShiftModifier)
410 if (KdenliveSettings::dragvalue_mode() == 2)
411 diff = (diff > 0 ? 1 : -1) * pow(diff, 2);
413 double nv = value() + diff * m_step;
414 if (nv != value()) setNewValue(nv, KdenliveSettings::dragvalue_directupdate());
417 double nv = minimum() + ((double) maximum() - minimum()) / width() * e->pos().x();
418 if (nv != value()) setNewValue(nv, KdenliveSettings::dragvalue_directupdate());
420 m_dragLastPosition = e->pos();
424 else QWidget::mouseMoveEvent(e);
427 void CustomLabel::mouseReleaseEvent(QMouseEvent* e)
429 if (e->button() == Qt::MidButton) {
433 if (e->modifiers() == Qt::ControlModifier) {
434 emit setInTimeline();
439 setNewValue(value(), true);
440 m_dragLastPosition = m_dragStartPosition;
443 else if (m_showSlider) {
444 setNewValue((double) maximum() * e->pos().x() / width(), true);
445 m_dragLastPosition = m_dragStartPosition;
451 void CustomLabel::wheelEvent(QWheelEvent* e)
453 if (e->delta() > 0) {
454 if (e->modifiers() == Qt::ControlModifier) slotValueInc(10);
455 else if (e->modifiers() == Qt::AltModifier) slotValueInc(0.1);
459 if (e->modifiers() == Qt::ControlModifier) slotValueDec(10);
460 else if (e->modifiers() == Qt::AltModifier) slotValueDec(0.1);
466 void CustomLabel::slotValueInc(double factor)
468 setNewValue(value() + m_step * factor, true);
471 void CustomLabel::slotValueDec(double factor)
473 setNewValue(value() - m_step * factor, true);
476 void CustomLabel::setProgressValue(double value)
478 setValue(qRound(value));
481 void CustomLabel::setNewValue(double value, bool update)
483 setValue(qRound(value));
484 emit valueChanged(qRound(value), update);
487 void CustomLabel::setStep(double step)
492 void CustomLabel::focusInEvent(QFocusEvent*)
494 setFocusPolicy(Qt::WheelFocus);
497 void CustomLabel::focusOutEvent(QFocusEvent*)
499 setFocusPolicy(Qt::StrongFocus);
502 #include "dragvalue.moc"