comparison widgets/Thumbwheel.cpp @ 0:fc9323a41f5a

start base : Sonic Visualiser sv1-1.0rc1
author lbajardsilogic
date Fri, 11 May 2007 09:08:14 +0000
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:fc9323a41f5a
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
53 Thumbwheel::~Thumbwheel()
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
71 updateMappedValue(getValue());
72 }
73
74 void
75 Thumbwheel::setShowToolTip(bool show)
76 {
77 m_showTooltip = show;
78 m_noMappedUpdate = true;
79 updateMappedValue(getValue());
80 m_noMappedUpdate = false;
81 }
82
83 void
84 Thumbwheel::setMinimumValue(int min)
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
98 Thumbwheel::getMinimumValue() const
99 {
100 return m_min;
101 }
102
103 void
104 Thumbwheel::setMaximumValue(int max)
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
118 Thumbwheel::getMaximumValue() const
119 {
120 return m_max;
121 }
122
123 void
124 Thumbwheel::setDefaultValue(int deft)
125 {
126 if (m_default == deft) return;
127
128 m_default = deft;
129 if (m_atDefault) {
130 setValue(m_default);
131 m_atDefault = true; // setValue unsets this
132 emit valueChanged(getValue());
133 }
134 }
135
136 void
137 Thumbwheel::setMappedValue(float mappedValue)
138 {
139 if (m_rangeMapper) {
140 int newValue = m_rangeMapper->getPositionForValue(mappedValue);
141 bool changed = (m_mappedValue != mappedValue);
142 m_mappedValue = mappedValue;
143 m_noMappedUpdate = true;
144 // std::cerr << "Thumbwheel::setMappedValue(" << mappedValue << "): new value is " << newValue << " (visible " << isVisible() << ")" << std::endl;
145 if (newValue != getValue()) {
146 setValue(newValue);
147 changed = true;
148 }
149 if (changed) emit valueChanged(newValue);
150 m_noMappedUpdate = false;
151 } else {
152 int v = int(mappedValue);
153 if (v != getValue()) {
154 setValue(v);
155 emit valueChanged(v);
156 }
157 }
158 }
159
160 int
161 Thumbwheel::getDefaultValue() const
162 {
163 return m_default;
164 }
165
166 void
167 Thumbwheel::setValue(int value)
168 {
169 // std::cerr << "Thumbwheel::setValue(" << value << ") (from " << m_value
170 // << ", rotation " << m_rotation << ")" << " (visible " << isVisible() << ")" << std::endl;
171
172 if (m_value != value) {
173
174 m_atDefault = false;
175
176 if (value < m_min) value = m_min;
177 if (value > m_max) value = m_max;
178 m_value = value;
179 }
180
181 m_rotation = float(m_value - m_min) / float(m_max - m_min);
182 if (isVisible()) update();
183 }
184
185 void
186 Thumbwheel::resetToDefault()
187 {
188 if (m_default == m_value) return;
189 setValue(m_default);
190 m_atDefault = true;
191 emit valueChanged(getValue());
192 }
193
194 int
195 Thumbwheel::getValue() const
196 {
197 return m_value;
198 }
199
200 float
201 Thumbwheel::getMappedValue() const
202 {
203 if (m_rangeMapper) {
204 // std::cerr << "Thumbwheel::getMappedValue(): value = " << getValue() << ", mappedValue = " << m_mappedValue << std::endl;
205 return m_mappedValue;
206 }
207 return getValue();
208 }
209
210 void
211 Thumbwheel::updateMappedValue(int value)
212 {
213 if (!m_noMappedUpdate) {
214 if (m_rangeMapper) {
215 m_mappedValue = m_rangeMapper->getValueForPosition(value);
216 } else {
217 m_mappedValue = value;
218 }
219 }
220
221 if (m_showTooltip) {
222 QString name = objectName();
223 QString unit = "";
224 QString text;
225 if (m_rangeMapper) unit = m_rangeMapper->getUnit();
226 if (name != "") {
227 text = tr("%1: %2%3").arg(name).arg(m_mappedValue).arg(unit);
228 } else {
229 text = tr("%2%3").arg(m_mappedValue).arg(unit);
230 }
231 setToolTip(text);
232 }
233 }
234
235 void
236 Thumbwheel::setSpeed(float speed)
237 {
238 m_speed = speed;
239 }
240
241 float
242 Thumbwheel::getSpeed() const
243 {
244 return m_speed;
245 }
246
247 void
248 Thumbwheel::setTracking(bool tracking)
249 {
250 m_tracking = tracking;
251 }
252
253 bool
254 Thumbwheel::getTracking() const
255 {
256 return m_tracking;
257 }
258
259 void
260 Thumbwheel::setShowScale(bool showScale)
261 {
262 m_showScale = showScale;
263 }
264
265 bool
266 Thumbwheel::getShowScale() const
267 {
268 return m_showScale;
269 }
270
271 void
272 Thumbwheel::enterEvent(QEvent *)
273 {
274 emit mouseEntered();
275 }
276
277 void
278 Thumbwheel::leaveEvent(QEvent *)
279 {
280 emit mouseLeft();
281 }
282
283 void
284 Thumbwheel::mousePressEvent(QMouseEvent *e)
285 {
286 if (e->button() == Qt::MidButton ||
287 ((e->button() == Qt::LeftButton) &&
288 (e->modifiers() & Qt::ControlModifier))) {
289 resetToDefault();
290 } else if (e->button() == Qt::LeftButton) {
291 m_clicked = true;
292 m_clickPos = e->pos();
293 m_clickRotation = m_rotation;
294 }
295 }
296
297 void
298 Thumbwheel::mouseDoubleClickEvent(QMouseEvent *mouseEvent)
299 {
300 //!!! needs a common base class with AudioDial (and Panner?)
301
302 if (mouseEvent->button() != Qt::LeftButton) {
303 return;
304 }
305
306 bool ok = false;
307
308 if (m_rangeMapper) {
309
310 float min = m_rangeMapper->getValueForPosition(m_min);
311 float max = m_rangeMapper->getValueForPosition(m_max);
312
313 if (min > max) {
314 float tmp = min;
315 min = max;
316 max = tmp;
317 }
318
319 QString unit = m_rangeMapper->getUnit();
320
321 QString text;
322 if (objectName() != "") {
323 if (unit != "") {
324 text = tr("New value for %1, from %2 to %3 %4:")
325 .arg(objectName()).arg(min).arg(max).arg(unit);
326 } else {
327 text = tr("New value for %1, from %2 to %3:")
328 .arg(objectName()).arg(min).arg(max);
329 }
330 } else {
331 if (unit != "") {
332 text = tr("Enter a new value from %1 to %2 %3:")
333 .arg(min).arg(max).arg(unit);
334 } else {
335 text = tr("Enter a new value from %1 to %2:")
336 .arg(min).arg(max);
337 }
338 }
339
340 float newValue = QInputDialog::getDouble
341 (this,
342 tr("Enter new value"),
343 text,
344 m_mappedValue,
345 min,
346 max,
347 4,
348 &ok);
349
350 if (ok) {
351 setMappedValue(newValue);
352 }
353
354 } else {
355
356 int newValue = QInputDialog::getInteger
357 (this,
358 tr("Enter new value"),
359 tr("Enter a new value from %1 to %2:")
360 .arg(m_min).arg(m_max),
361 getValue(), m_min, m_max, 1, &ok);
362
363 if (ok) {
364 setValue(newValue);
365 }
366 }
367 }
368
369
370 void
371 Thumbwheel::mouseMoveEvent(QMouseEvent *e)
372 {
373 if (!m_clicked) return;
374 int dist = 0;
375 if (m_orientation == Qt::Horizontal) {
376 dist = e->x() - m_clickPos.x();
377 } else {
378 dist = e->y() - m_clickPos.y();
379 }
380
381 float rotation = m_clickRotation + (m_speed * dist) / 100;
382 if (rotation < 0.f) rotation = 0.f;
383 if (rotation > 1.f) rotation = 1.f;
384 int value = lrintf(m_min + (m_max - m_min) * m_rotation);
385 if (value != m_value) {
386 setValue(value);
387 if (m_tracking) emit valueChanged(getValue());
388 m_rotation = rotation;
389 } else if (fabsf(rotation - m_rotation) > 0.001) {
390 m_rotation = rotation;
391 repaint();
392 }
393 }
394
395 void
396 Thumbwheel::mouseReleaseEvent(QMouseEvent *e)
397 {
398 if (!m_clicked) return;
399 bool reallyTracking = m_tracking;
400 m_tracking = true;
401 mouseMoveEvent(e);
402 m_tracking = reallyTracking;
403 m_clicked = false;
404 }
405
406 void
407 Thumbwheel::wheelEvent(QWheelEvent *e)
408 {
409 int step = lrintf(m_speed);
410 if (step == 0) step = 1;
411
412 if (e->delta() > 0) {
413 setValue(m_value + step);
414 } else {
415 setValue(m_value - step);
416 }
417
418 emit valueChanged(getValue());
419 }
420
421 void
422 Thumbwheel::paintEvent(QPaintEvent *)
423 {
424 Profiler profiler("Thumbwheel::paintEvent", true);
425
426 int bw = 3;
427
428 QRect subclip;
429 if (m_orientation == Qt::Horizontal) {
430 subclip = QRect(bw, bw+1, width() - bw*2, height() - bw*2 - 2);
431 } else {
432 subclip = QRect(bw+1, bw, width() - bw*2 - 2, height() - bw*2);
433 }
434
435 QPainter paint(this);
436 paint.fillRect(subclip, palette().background().color());
437
438 paint.setRenderHint(QPainter::Antialiasing, true);
439
440 float w = width();
441 float w0 = 0.5;
442 float w1 = w - 0.5;
443
444 float h = height();
445 float h0 = 0.5;
446 float h1 = h - 0.5;
447
448 for (int i = bw-1; i >= 0; --i) {
449 // for (int i = 0; i < 1; ++i) {
450
451 int grey = (i + 1) * (256 / (bw + 1));
452 QColor fc = QColor(grey, grey, grey);
453 paint.setPen(fc);
454
455 QPainterPath path;
456
457 if (m_orientation == Qt::Horizontal) {
458 path.moveTo(w0 + i, h0 + i + 2);
459 path.quadTo(w/2, i * 1.25, w1 - i, h0 + i + 2);
460 path.lineTo(w1 - i, h1 - i - 2);
461 path.quadTo(w/2, h - i * 1.25, w0 + i, h1 - i - 2);
462 path.closeSubpath();
463 } else {
464 path.moveTo(w0 + i + 2, h0 + i);
465 path.quadTo(i * 1.25, h/2, w0 + i + 2, h1 - i);
466 path.lineTo(w1 - i - 2, h1 - i);
467 path.quadTo(w - i * 1.25, h/2, w1 - i - 2, h0 + i);
468 path.closeSubpath();
469 }
470
471 paint.drawPath(path);
472 }
473
474 paint.setClipRect(subclip);
475
476 float radians = m_rotation * 1.5f * M_PI;
477
478 // std::cerr << "value = " << m_value << ", min = " << m_min << ", max = " << m_max << ", rotation = " << rotation << std::endl;
479
480 w = (m_orientation == Qt::Horizontal ? width() : height()) - bw*2;
481
482 // total number of notches on the entire wheel
483 int notches = 25;
484
485 // radius of the wheel including invisible part
486 int radius = int(w / 2 + 2);
487
488 for (int i = 0; i < notches; ++i) {
489
490 float a0 = (2.f * M_PI * i) / notches + radians;
491 float a1 = a0 + M_PI / (notches * 2);
492 float a2 = (2.f * M_PI * (i + 1)) / notches + radians;
493
494 float depth = cosf((a0 + a2) / 2);
495 if (depth < 0) continue;
496
497 float x0 = radius * sinf(a0) + w/2;
498 float x1 = radius * sinf(a1) + w/2;
499 float x2 = radius * sinf(a2) + w/2;
500 if (x2 < 0 || x0 > w) continue;
501
502 if (x0 < 0) x0 = 0;
503 if (x2 > w) x2 = w;
504
505 x0 += bw;
506 x1 += bw;
507 x2 += bw;
508
509 int grey = lrintf(255 * depth);
510 QColor fc = QColor(grey, grey, grey);
511 QColor oc = palette().dark().color();
512
513 paint.setPen(oc);
514 paint.setBrush(fc);
515
516 if (m_orientation == Qt::Horizontal) {
517 paint.drawRect(QRectF(x1, bw, x2 - x1, height() - bw*2));
518 } else {
519 paint.drawRect(QRectF(bw, x1, width() - bw*2, x2 - x1));
520 }
521
522 if (m_showScale) {
523
524 paint.setBrush(oc);
525
526 float prop;
527 if (i >= notches / 4) {
528 prop = float(notches - (((i - float(notches) / 4.f) * 4.f) / 3.f))
529 / notches;
530 } else {
531 prop = 0.f;
532 }
533
534 if (m_orientation == Qt::Horizontal) {
535 paint.drawRect(QRectF(x1, height() - (height() - bw*2) * prop - bw,
536 x2 - x1, height() * prop));
537 } else {
538 paint.drawRect(QRectF(bw, x1, (width() - bw*2) * prop, x2 - x1));
539 }
540 }
541
542 paint.setPen(oc);
543 paint.setBrush(palette().background().color());
544
545 if (m_orientation == Qt::Horizontal) {
546 paint.drawRect(QRectF(x0, bw, x1 - x0, height() - bw*2));
547 } else {
548 paint.drawRect(QRectF(bw, x0, width() - bw*2, x1 - x0));
549 }
550 }
551 }
552
553 QSize
554 Thumbwheel::sizeHint() const
555 {
556 if (m_orientation == Qt::Horizontal) {
557 return QSize(80, 12);
558 } else {
559 return QSize(12, 80);
560 }
561 }
562