svgui  1.9
Thumbwheel.cpp
Go to the documentation of this file.
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
2 
3 /*
4  Sonic Visualiser
5  An audio file viewer and annotation editor.
6  Centre for Digital Music, Queen Mary, University of London.
7  This file copyright 2006 QMUL.
8 
9  This program is free software; you can redistribute it and/or
10  modify it under the terms of the GNU General Public License as
11  published by the Free Software Foundation; either version 2 of the
12  License, or (at your option) any later version. See the file
13  COPYING included with this distribution for more information.
14 */
15 
16 #include "Thumbwheel.h"
17 
18 #include "base/RangeMapper.h"
19 #include "base/Profiler.h"
20 
21 #include <QMouseEvent>
22 #include <QPaintEvent>
23 #include <QWheelEvent>
24 #include <QInputDialog>
25 #include <QPainter>
26 #include <QPainterPath>
27 
28 #include <cmath>
29 #include <iostream>
30 
31 Thumbwheel::Thumbwheel(Qt::Orientation orientation,
32  QWidget *parent) :
33  QWidget(parent),
34  m_min(0),
35  m_max(100),
36  m_default(50),
37  m_value(50),
38  m_mappedValue(50),
39  m_noMappedUpdate(false),
40  m_rotation(0.5),
41  m_orientation(orientation),
42  m_speed(1.0),
43  m_tracking(true),
44  m_showScale(true),
45  m_clicked(false),
46  m_atDefault(true),
47  m_clickRotation(m_rotation),
48  m_showTooltip(true),
49  m_rangeMapper(0)
50 {
51 }
52 
54 {
55  delete m_rangeMapper;
56 }
57 
58 void
59 Thumbwheel::setRangeMapper(RangeMapper *mapper)
60 {
61  if (m_rangeMapper == mapper) return;
62 
63  if (!m_rangeMapper && mapper) {
64  connect(this, SIGNAL(valueChanged(int)),
65  this, SLOT(updateMappedValue(int)));
66  }
67 
68  delete m_rangeMapper;
69  m_rangeMapper = mapper;
70 
72 }
73 
74 void
76 {
77  m_showTooltip = show;
78  m_noMappedUpdate = true;
80  m_noMappedUpdate = false;
81 }
82 
83 void
85 {
86  if (m_min == min) return;
87 
88  m_min = min;
89  if (m_max <= m_min) m_max = m_min + 1;
90  if (m_value < m_min) m_value = m_min;
91  if (m_value > m_max) m_value = m_max;
92 
93  m_rotation = float(m_value - m_min) / float(m_max - m_min);
94  update();
95 }
96 
97 int
99 {
100  return m_min;
101 }
102 
103 void
105 {
106  if (m_max == max) return;
107 
108  m_max = max;
109  if (m_min >= m_max) m_min = m_max - 1;
110  if (m_value < m_min) m_value = m_min;
111  if (m_value > m_max) m_value = m_max;
112 
113  m_rotation = float(m_value - m_min) / float(m_max - m_min);
114  update();
115 }
116 
117 int
119 {
120  return m_max;
121 }
122 
123 void
125 {
126  if (m_default == deft) return;
127 
128  m_default = deft;
129  if (m_atDefault) {
131  m_atDefault = true; // setValue unsets this
132  m_cache = QImage();
133  emit valueChanged(getValue());
134  }
135 }
136 
137 void
138 Thumbwheel::setMappedValue(float mappedValue)
139 {
140  if (m_rangeMapper) {
141  int newValue = m_rangeMapper->getPositionForValue(mappedValue);
142  bool changed = (m_mappedValue != mappedValue);
143  m_mappedValue = mappedValue;
144  m_noMappedUpdate = true;
145 // SVDEBUG << "Thumbwheel::setMappedValue(" << mappedValue << "): new value is " << newValue << " (visible " << isVisible() << ")" << endl;
146  if (newValue != getValue()) {
147  setValue(newValue);
148  changed = true;
149  m_cache = QImage();
150  }
151  if (changed) emit valueChanged(newValue);
152  m_noMappedUpdate = false;
153  } else {
154  int v = int(mappedValue);
155  if (v != getValue()) {
156  setValue(v);
157  m_cache = QImage();
158  emit valueChanged(v);
159  }
160  }
161 }
162 
163 int
165 {
166  return m_default;
167 }
168 
169 void
171 {
172 // SVDEBUG << "Thumbwheel::setValue(" << value << ") (from " << m_value
173 // << ", rotation " << m_rotation << ")" << " (visible " << isVisible() << ")" << endl;
174 
175  if (m_value != value) {
176 
177  m_atDefault = false;
178 
179  if (value < m_min) value = m_min;
180  if (value > m_max) value = m_max;
181  m_value = value;
182  }
183 
184  m_rotation = float(m_value - m_min) / float(m_max - m_min);
185  m_cache = QImage();
186  if (isVisible()) update();
187 }
188 
189 void
191 {
192  if (m_default == m_value) return;
194  m_atDefault = true;
195  m_cache = QImage();
196  emit valueChanged(getValue());
197 }
198 
199 int
201 {
202  return m_value;
203 }
204 
205 float
207 {
208  if (m_rangeMapper) {
209 // SVDEBUG << "Thumbwheel::getMappedValue(): value = " << getValue() << ", mappedValue = " << m_mappedValue << endl;
210  return m_mappedValue;
211  }
212  return getValue();
213 }
214 
215 void
217 {
218  if (!m_noMappedUpdate) {
219  if (m_rangeMapper) {
220  m_mappedValue = m_rangeMapper->getValueForPosition(value);
221  } else {
222  m_mappedValue = value;
223  }
224  }
225 
226  if (m_showTooltip) {
227  QString name = objectName();
228  QString unit = "";
229  QString text;
230  if (m_rangeMapper) unit = m_rangeMapper->getUnit();
231  if (name != "") {
232  text = tr("%1: %2%3").arg(name).arg(m_mappedValue).arg(unit);
233  } else {
234  text = tr("%2%3").arg(m_mappedValue).arg(unit);
235  }
236  setToolTip(text);
237  }
238 }
239 
240 void
242 {
243  int step = lrintf(m_speed);
244  if (step == 0) step = 1;
245 
246  if (up) {
247  setValue(m_value + step);
248  } else {
249  setValue(m_value - step);
250  }
251 
252  emit valueChanged(getValue());
253 }
254 
255 void
257 {
258  m_speed = speed;
259 }
260 
261 float
263 {
264  return m_speed;
265 }
266 
267 void
269 {
270  m_tracking = tracking;
271 }
272 
273 bool
275 {
276  return m_tracking;
277 }
278 
279 void
281 {
282  m_showScale = showScale;
283 }
284 
285 bool
287 {
288  return m_showScale;
289 }
290 
291 void
293 {
294  emit mouseEntered();
295 }
296 
297 void
299 {
300  emit mouseLeft();
301 }
302 
303 void
305 {
306  if (e->button() == Qt::MidButton ||
307  ((e->button() == Qt::LeftButton) &&
308  (e->modifiers() & Qt::ControlModifier))) {
309  resetToDefault();
310  } else if (e->button() == Qt::LeftButton) {
311  m_clicked = true;
312  m_clickPos = e->pos();
314  }
315 }
316 
317 void
318 Thumbwheel::mouseDoubleClickEvent(QMouseEvent *mouseEvent)
319 {
321 
322  if (mouseEvent->button() != Qt::LeftButton) {
323  return;
324  }
325 
326  bool ok = false;
327 
328  if (m_rangeMapper) {
329 
330  float min = m_rangeMapper->getValueForPosition(m_min);
331  float max = m_rangeMapper->getValueForPosition(m_max);
332 
333  if (min > max) {
334  float tmp = min;
335  min = max;
336  max = tmp;
337  }
338 
339  QString unit = m_rangeMapper->getUnit();
340 
341  QString text;
342  if (objectName() != "") {
343  if (unit != "") {
344  text = tr("New value for %1, from %2 to %3 %4:")
345  .arg(objectName()).arg(min).arg(max).arg(unit);
346  } else {
347  text = tr("New value for %1, from %2 to %3:")
348  .arg(objectName()).arg(min).arg(max);
349  }
350  } else {
351  if (unit != "") {
352  text = tr("Enter a new value from %1 to %2 %3:")
353  .arg(min).arg(max).arg(unit);
354  } else {
355  text = tr("Enter a new value from %1 to %2:")
356  .arg(min).arg(max);
357  }
358  }
359 
360  float newValue = QInputDialog::getDouble
361  (this,
362  tr("Enter new value"),
363  text,
365  min,
366  max,
367  4,
368  &ok);
369 
370  if (ok) {
371  setMappedValue(newValue);
372  }
373 
374  } else {
375 
376  int newValue = QInputDialog::getInt
377  (this,
378  tr("Enter new value"),
379  tr("Enter a new value from %1 to %2:")
380  .arg(m_min).arg(m_max),
381  getValue(), m_min, m_max, 1, &ok);
382 
383  if (ok) {
384  setValue(newValue);
385  }
386  }
387 }
388 
389 
390 void
392 {
393  if (!m_clicked) return;
394  int dist = 0;
395  if (m_orientation == Qt::Horizontal) {
396  dist = e->x() - m_clickPos.x();
397  } else {
398  dist = e->y() - m_clickPos.y();
399  }
400 
401  float rotation = m_clickRotation + (m_speed * dist) / 100;
402  if (rotation < 0.f) rotation = 0.f;
403  if (rotation > 1.f) rotation = 1.f;
404  int value = lrintf(m_min + (m_max - m_min) * m_rotation);
405  if (value != m_value) {
406  setValue(value);
407  if (m_tracking) emit valueChanged(getValue());
408  m_rotation = rotation;
409  } else if (fabsf(rotation - m_rotation) > 0.001) {
410  m_rotation = rotation;
411  repaint();
412  }
413 }
414 
415 void
417 {
418  if (!m_clicked) return;
419  bool reallyTracking = m_tracking;
420  m_tracking = true;
421  mouseMoveEvent(e);
422  m_tracking = reallyTracking;
423  m_clicked = false;
424 }
425 
426 void
427 Thumbwheel::wheelEvent(QWheelEvent *e)
428 {
429  int step = lrintf(m_speed);
430  if (step == 0) step = 1;
431 
432  if (e->delta() > 0) {
433  setValue(m_value + step);
434  } else {
435  setValue(m_value - step);
436  }
437 
438  emit valueChanged(getValue());
439 }
440 
441 void
443 {
444  Profiler profiler("Thumbwheel::paintEvent");
445 
446  if (!m_cache.isNull()) {
447  QPainter paint(this);
448  paint.drawImage(0, 0, m_cache);
449  return;
450  }
451 
452  Profiler profiler2("Thumbwheel::paintEvent (no cache)");
453 
454  m_cache = QImage(size(), QImage::Format_ARGB32);
455  m_cache.fill(Qt::transparent);
456 
457  int bw = 3;
458 
459  QRect subclip;
460  if (m_orientation == Qt::Horizontal) {
461  subclip = QRect(bw, bw+1, width() - bw*2, height() - bw*2 - 2);
462  } else {
463  subclip = QRect(bw+1, bw, width() - bw*2 - 2, height() - bw*2);
464  }
465 
466  QPainter paint(&m_cache);
467  paint.setClipRect(rect());
468  paint.fillRect(subclip, palette().background().color());
469 
470  paint.setRenderHint(QPainter::Antialiasing, true);
471 
472  float w = width();
473  float w0 = 0.5;
474  float w1 = w - 0.5;
475 
476  float h = height();
477  float h0 = 0.5;
478  float h1 = h - 0.5;
479 
480  for (int i = bw-1; i >= 0; --i) {
481 
482  int grey = (i + 1) * (256 / (bw + 1));
483  QColor fc = QColor(grey, grey, grey);
484  paint.setPen(fc);
485 
486  QPainterPath path;
487 
488  if (m_orientation == Qt::Horizontal) {
489  path.moveTo(w0 + i, h0 + i + 2);
490  path.quadTo(w/2, i * 1.25, w1 - i, h0 + i + 2);
491  path.lineTo(w1 - i, h1 - i - 2);
492  path.quadTo(w/2, h - i * 1.25, w0 + i, h1 - i - 2);
493  path.closeSubpath();
494  } else {
495  path.moveTo(w0 + i + 2, h0 + i);
496  path.quadTo(i * 1.25, h/2, w0 + i + 2, h1 - i);
497  path.lineTo(w1 - i - 2, h1 - i);
498  path.quadTo(w - i * 1.25, h/2, w1 - i - 2, h0 + i);
499  path.closeSubpath();
500  }
501 
502  paint.drawPath(path);
503  }
504 
505  paint.setClipRect(subclip);
506 
507  float radians = m_rotation * 1.5f * M_PI;
508 
509 // cerr << "value = " << m_value << ", min = " << m_min << ", max = " << m_max << ", rotation = " << rotation << endl;
510 
511  w = (m_orientation == Qt::Horizontal ? width() : height()) - bw*2;
512 
513  // total number of notches on the entire wheel
514  int notches = 25;
515 
516  // radius of the wheel including invisible part
517  int radius = int(w / 2 + 2);
518 
519  for (int i = 0; i < notches; ++i) {
520 
521  float a0 = (2.f * M_PI * i) / notches + radians;
522  float a1 = a0 + M_PI / (notches * 2);
523  float a2 = (2.f * M_PI * (i + 1)) / notches + radians;
524 
525  float depth = cosf((a0 + a2) / 2);
526  if (depth < 0) continue;
527 
528  float x0 = radius * sinf(a0) + w/2;
529  float x1 = radius * sinf(a1) + w/2;
530  float x2 = radius * sinf(a2) + w/2;
531  if (x2 < 0 || x0 > w) continue;
532 
533  if (x0 < 0) x0 = 0;
534  if (x2 > w) x2 = w;
535 
536  x0 += bw;
537  x1 += bw;
538  x2 += bw;
539 
540  int grey = lrintf(120 * depth);
541 
542  QColor fc = QColor(grey, grey, grey);
543  QColor oc = palette().highlight().color();
544 
545  paint.setPen(fc);
546 
547  if (m_showScale) {
548 
549  paint.setBrush(oc);
550 
551  float prop;
552  if (i >= notches / 4) {
553  prop = float(notches - (((i - float(notches) / 4.f) * 4.f) / 3.f))
554  / notches;
555  } else {
556  prop = 0.f;
557  }
558 
559  if (m_orientation == Qt::Horizontal) {
560  paint.drawRect(QRectF(x1, height() - (height() - bw*2) * prop - bw,
561  x2 - x1, height() * prop));
562  } else {
563  paint.drawRect(QRectF(bw, x1, (width() - bw*2) * prop, x2 - x1));
564  }
565  }
566 
567  paint.setPen(fc);
568  paint.setBrush(palette().background().color());
569 
570  if (m_orientation == Qt::Horizontal) {
571  paint.drawRect(QRectF(x0, bw, x1 - x0, height() - bw*2));
572  } else {
573  paint.drawRect(QRectF(bw, x0, width() - bw*2, x1 - x0));
574  }
575  }
576 
577  QPainter paint2(this);
578  paint2.drawImage(0, 0, m_cache);
579 }
580 
581 QSize
583 {
584  if (m_orientation == Qt::Horizontal) {
585  return QSize(80, 12);
586  } else {
587  return QSize(12, 80);
588  }
589 }
590 
void setDefaultValue(int deft)
Definition: Thumbwheel.cpp:124
void setRangeMapper(RangeMapper *mapper)
Definition: Thumbwheel.cpp:59
float m_speed
Definition: Thumbwheel.h:89
bool m_atDefault
Definition: Thumbwheel.h:93
virtual void enterEvent(QEvent *)
Definition: Thumbwheel.cpp:292
int getDefaultValue() const
Definition: Thumbwheel.cpp:164
QPoint m_clickPos
Definition: Thumbwheel.h:94
virtual void leaveEvent(QEvent *)
Definition: Thumbwheel.cpp:298
Thumbwheel(Qt::Orientation orientation, QWidget *parent=0)
Definition: Thumbwheel.cpp:31
int getMinimumValue() const
Definition: Thumbwheel.cpp:98
bool m_showTooltip
Definition: Thumbwheel.h:96
bool m_clicked
Definition: Thumbwheel.h:92
virtual void mouseMoveEvent(QMouseEvent *e)
Definition: Thumbwheel.cpp:391
virtual void wheelEvent(QWheelEvent *e)
Definition: Thumbwheel.cpp:427
void setShowScale(bool show)
Definition: Thumbwheel.cpp:280
virtual void paintEvent(QPaintEvent *e)
Definition: Thumbwheel.cpp:442
bool getTracking() const
Definition: Thumbwheel.cpp:274
float m_rotation
Definition: Thumbwheel.h:87
virtual void mouseDoubleClickEvent(QMouseEvent *e)
Definition: Thumbwheel.cpp:318
void setMappedValue(float mappedValue)
Definition: Thumbwheel.cpp:138
bool m_tracking
Definition: Thumbwheel.h:90
void setMinimumValue(int min)
Definition: Thumbwheel.cpp:84
RangeMapper * m_rangeMapper
Definition: Thumbwheel.h:97
void setMaximumValue(int max)
Definition: Thumbwheel.cpp:104
bool getShowScale() const
Definition: Thumbwheel.cpp:286
void setTracking(bool tracking)
Definition: Thumbwheel.cpp:268
float m_clickRotation
Definition: Thumbwheel.h:95
void updateMappedValue(int value)
Definition: Thumbwheel.cpp:216
void setValue(int value)
Definition: Thumbwheel.cpp:170
virtual ~Thumbwheel()
Definition: Thumbwheel.cpp:53
bool m_showScale
Definition: Thumbwheel.h:91
void scroll(bool up)
Definition: Thumbwheel.cpp:241
float m_mappedValue
Definition: Thumbwheel.h:85
QImage m_cache
Definition: Thumbwheel.h:98
int getValue() const
Definition: Thumbwheel.cpp:200
void setSpeed(float speed)
Definition: Thumbwheel.cpp:256
float getMappedValue() const
Definition: Thumbwheel.cpp:206
Qt::Orientation m_orientation
Definition: Thumbwheel.h:88
bool m_noMappedUpdate
Definition: Thumbwheel.h:86
int getMaximumValue() const
Definition: Thumbwheel.cpp:118
virtual void mousePressEvent(QMouseEvent *e)
Definition: Thumbwheel.cpp:304
void resetToDefault()
Definition: Thumbwheel.cpp:190
int m_default
Definition: Thumbwheel.h:83
float getSpeed() const
Definition: Thumbwheel.cpp:262
int m_value
Definition: Thumbwheel.h:84
void setShowToolTip(bool show)
Definition: Thumbwheel.cpp:75
QSize sizeHint() const
Definition: Thumbwheel.cpp:582
void mouseLeft()
virtual void mouseReleaseEvent(QMouseEvent *e)
Definition: Thumbwheel.cpp:416
void valueChanged(int)
void mouseEntered()