Mercurial > hg > easaier-soundaccess
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 |