svgui  1.9
Thumbwheel.cpp
Go to the documentation of this file.
00001 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
00002 
00003 /*
00004     Sonic Visualiser
00005     An audio file viewer and annotation editor.
00006     Centre for Digital Music, Queen Mary, University of London.
00007     This file copyright 2006 QMUL.
00008 
00009     This program is free software; you can redistribute it and/or
00010     modify it under the terms of the GNU General Public License as
00011     published by the Free Software Foundation; either version 2 of the
00012     License, or (at your option) any later version.  See the file
00013     COPYING included with this distribution for more information.
00014 */
00015 
00016 #include "Thumbwheel.h"
00017 
00018 #include "base/RangeMapper.h"
00019 #include "base/Profiler.h"
00020 
00021 #include <QMouseEvent>
00022 #include <QPaintEvent>
00023 #include <QWheelEvent>
00024 #include <QInputDialog>
00025 #include <QPainter>
00026 #include <QPainterPath>
00027 
00028 #include <cmath>
00029 #include <iostream>
00030 
00031 Thumbwheel::Thumbwheel(Qt::Orientation orientation,
00032                        QWidget *parent) :
00033     QWidget(parent),
00034     m_min(0),
00035     m_max(100),
00036     m_default(50),
00037     m_value(50),
00038     m_mappedValue(50),
00039     m_noMappedUpdate(false),
00040     m_rotation(0.5),
00041     m_orientation(orientation),
00042     m_speed(1.0),
00043     m_tracking(true),
00044     m_showScale(true),
00045     m_clicked(false),
00046     m_atDefault(true),
00047     m_clickRotation(m_rotation),
00048     m_showTooltip(true),
00049     m_rangeMapper(0)
00050 {
00051 }
00052 
00053 Thumbwheel::~Thumbwheel()
00054 {
00055     delete m_rangeMapper;
00056 }
00057 
00058 void
00059 Thumbwheel::setRangeMapper(RangeMapper *mapper)
00060 {
00061     if (m_rangeMapper == mapper) return;
00062 
00063     if (!m_rangeMapper && mapper) {
00064         connect(this, SIGNAL(valueChanged(int)),
00065                 this, SLOT(updateMappedValue(int)));
00066     }
00067 
00068     delete m_rangeMapper;
00069     m_rangeMapper = mapper;
00070 
00071     updateMappedValue(getValue());
00072 }
00073 
00074 void
00075 Thumbwheel::setShowToolTip(bool show)
00076 {
00077     m_showTooltip = show;
00078     m_noMappedUpdate = true;
00079     updateMappedValue(getValue());
00080     m_noMappedUpdate = false;
00081 }
00082 
00083 void
00084 Thumbwheel::setMinimumValue(int min)
00085 {
00086     if (m_min == min) return;
00087 
00088     m_min = min;
00089     if (m_max <= m_min) m_max = m_min + 1;
00090     if (m_value < m_min) m_value = m_min;
00091     if (m_value > m_max) m_value = m_max;
00092 
00093     m_rotation = float(m_value - m_min) / float(m_max - m_min);
00094     update();
00095 }
00096 
00097 int
00098 Thumbwheel::getMinimumValue() const
00099 {
00100     return m_min;
00101 }
00102 
00103 void
00104 Thumbwheel::setMaximumValue(int max)
00105 {
00106     if (m_max == max) return;
00107 
00108     m_max = max;
00109     if (m_min >= m_max) m_min = m_max - 1;
00110     if (m_value < m_min) m_value = m_min;
00111     if (m_value > m_max) m_value = m_max;
00112 
00113     m_rotation = float(m_value - m_min) / float(m_max - m_min);
00114     update();
00115 }
00116 
00117 int
00118 Thumbwheel::getMaximumValue() const
00119 {
00120     return m_max;
00121 }
00122 
00123 void
00124 Thumbwheel::setDefaultValue(int deft)
00125 {
00126     if (m_default == deft) return;
00127 
00128     m_default = deft;
00129     if (m_atDefault) {
00130         setValue(m_default);
00131         m_atDefault = true; // setValue unsets this
00132         m_cache = QImage();
00133         emit valueChanged(getValue());
00134     }
00135 }
00136 
00137 void
00138 Thumbwheel::setMappedValue(float mappedValue)
00139 {
00140     if (m_rangeMapper) {
00141         int newValue = m_rangeMapper->getPositionForValue(mappedValue);
00142         bool changed = (m_mappedValue != mappedValue);
00143         m_mappedValue = mappedValue;
00144         m_noMappedUpdate = true;
00145 //        SVDEBUG << "Thumbwheel::setMappedValue(" << mappedValue << "): new value is " << newValue << " (visible " << isVisible() << ")" << endl;
00146         if (newValue != getValue()) {
00147             setValue(newValue);
00148             changed = true;
00149             m_cache = QImage();
00150         }
00151         if (changed) emit valueChanged(newValue);
00152         m_noMappedUpdate = false;
00153     } else {
00154         int v = int(mappedValue);
00155         if (v != getValue()) {
00156             setValue(v);
00157             m_cache = QImage();
00158             emit valueChanged(v);
00159         }
00160     }
00161 }
00162 
00163 int
00164 Thumbwheel::getDefaultValue() const
00165 {
00166     return m_default;
00167 }
00168 
00169 void
00170 Thumbwheel::setValue(int value)
00171 {
00172 //    SVDEBUG << "Thumbwheel::setValue(" << value << ") (from " << m_value
00173 //              << ", rotation " << m_rotation << ")" << " (visible " << isVisible() << ")" << endl;
00174 
00175     if (m_value != value) {
00176 
00177         m_atDefault = false;
00178 
00179         if (value < m_min) value = m_min;
00180         if (value > m_max) value = m_max;
00181         m_value = value;
00182     }
00183 
00184     m_rotation = float(m_value - m_min) / float(m_max - m_min);
00185     m_cache = QImage();
00186     if (isVisible()) update();
00187 }
00188 
00189 void
00190 Thumbwheel::resetToDefault()
00191 {
00192     if (m_default == m_value) return;
00193     setValue(m_default);
00194     m_atDefault = true;
00195     m_cache = QImage();
00196     emit valueChanged(getValue());
00197 }
00198 
00199 int
00200 Thumbwheel::getValue() const
00201 {
00202     return m_value;
00203 }
00204 
00205 float
00206 Thumbwheel::getMappedValue() const
00207 {
00208     if (m_rangeMapper) {
00209 //        SVDEBUG << "Thumbwheel::getMappedValue(): value = " << getValue() << ", mappedValue = " << m_mappedValue << endl;
00210         return m_mappedValue;
00211     }
00212     return getValue();
00213 }
00214 
00215 void
00216 Thumbwheel::updateMappedValue(int value)
00217 {
00218     if (!m_noMappedUpdate) {
00219         if (m_rangeMapper) {
00220             m_mappedValue = m_rangeMapper->getValueForPosition(value);
00221         } else {
00222             m_mappedValue = value;
00223         }
00224     }
00225 
00226     if (m_showTooltip) {
00227         QString name = objectName();
00228         QString unit = "";
00229         QString text;
00230         if (m_rangeMapper) unit = m_rangeMapper->getUnit();
00231         if (name != "") {
00232             text = tr("%1: %2%3").arg(name).arg(m_mappedValue).arg(unit);
00233         } else {
00234             text = tr("%2%3").arg(m_mappedValue).arg(unit);
00235         }
00236         setToolTip(text);
00237     }
00238 }
00239 
00240 void
00241 Thumbwheel::scroll(bool up)
00242 {
00243     int step = lrintf(m_speed);
00244     if (step == 0) step = 1;
00245 
00246     if (up) {
00247         setValue(m_value + step);
00248     } else {
00249         setValue(m_value - step);
00250     }
00251     
00252     emit valueChanged(getValue());
00253 }
00254 
00255 void
00256 Thumbwheel::setSpeed(float speed)
00257 {
00258     m_speed = speed;
00259 }
00260 
00261 float
00262 Thumbwheel::getSpeed() const
00263 {
00264     return m_speed;
00265 }
00266 
00267 void
00268 Thumbwheel::setTracking(bool tracking)
00269 {
00270     m_tracking = tracking;
00271 }
00272 
00273 bool
00274 Thumbwheel::getTracking() const
00275 {
00276     return m_tracking;
00277 }
00278 
00279 void
00280 Thumbwheel::setShowScale(bool showScale)
00281 {
00282     m_showScale = showScale;
00283 }
00284 
00285 bool
00286 Thumbwheel::getShowScale() const
00287 {
00288     return m_showScale;
00289 }
00290 
00291 void
00292 Thumbwheel::enterEvent(QEvent *)
00293 {
00294     emit mouseEntered();
00295 }
00296 
00297 void
00298 Thumbwheel::leaveEvent(QEvent *)
00299 {
00300     emit mouseLeft();
00301 }
00302 
00303 void
00304 Thumbwheel::mousePressEvent(QMouseEvent *e)
00305 {
00306     if (e->button() == Qt::MidButton ||
00307         ((e->button() == Qt::LeftButton) &&
00308          (e->modifiers() & Qt::ControlModifier))) {
00309         resetToDefault();
00310     } else if (e->button() == Qt::LeftButton) {
00311         m_clicked = true;
00312         m_clickPos = e->pos();
00313         m_clickRotation = m_rotation;
00314     }
00315 }
00316 
00317 void
00318 Thumbwheel::mouseDoubleClickEvent(QMouseEvent *mouseEvent)
00319 {
00321 
00322     if (mouseEvent->button() != Qt::LeftButton) {
00323         return;
00324     }
00325 
00326     bool ok = false;
00327 
00328     if (m_rangeMapper) {
00329         
00330         float min = m_rangeMapper->getValueForPosition(m_min);
00331         float max = m_rangeMapper->getValueForPosition(m_max);
00332                 
00333         if (min > max) { 
00334             float tmp = min;
00335             min = max;
00336             max = tmp;
00337         }
00338 
00339         QString unit = m_rangeMapper->getUnit();
00340         
00341         QString text;
00342         if (objectName() != "") {
00343             if (unit != "") {
00344                 text = tr("New value for %1, from %2 to %3 %4:")
00345                     .arg(objectName()).arg(min).arg(max).arg(unit);
00346             } else {
00347                 text = tr("New value for %1, from %2 to %3:")
00348                     .arg(objectName()).arg(min).arg(max);
00349             }
00350         } else {
00351             if (unit != "") {
00352                 text = tr("Enter a new value from %1 to %2 %3:")
00353                     .arg(min).arg(max).arg(unit);
00354             } else {
00355                 text = tr("Enter a new value from %1 to %2:")
00356                     .arg(min).arg(max);
00357             }
00358         }
00359         
00360         float newValue = QInputDialog::getDouble
00361             (this,
00362              tr("Enter new value"),
00363              text,
00364              m_mappedValue,
00365              min,
00366              max,
00367              4, 
00368              &ok);
00369         
00370         if (ok) {
00371             setMappedValue(newValue);
00372         }
00373         
00374     } else {
00375         
00376         int newValue = QInputDialog::getInt
00377             (this,
00378              tr("Enter new value"),
00379              tr("Enter a new value from %1 to %2:")
00380              .arg(m_min).arg(m_max),
00381              getValue(), m_min, m_max, 1, &ok);
00382         
00383         if (ok) {
00384             setValue(newValue);
00385         }
00386     }
00387 }
00388 
00389 
00390 void
00391 Thumbwheel::mouseMoveEvent(QMouseEvent *e)
00392 {
00393     if (!m_clicked) return;
00394     int dist = 0;
00395     if (m_orientation == Qt::Horizontal) {
00396         dist = e->x() - m_clickPos.x();
00397     } else {
00398         dist = e->y() - m_clickPos.y();
00399     }
00400 
00401     float rotation = m_clickRotation + (m_speed * dist) / 100;
00402     if (rotation < 0.f) rotation = 0.f;
00403     if (rotation > 1.f) rotation = 1.f;
00404     int value = lrintf(m_min + (m_max - m_min) * m_rotation);
00405     if (value != m_value) {
00406         setValue(value);
00407         if (m_tracking) emit valueChanged(getValue());
00408         m_rotation = rotation;
00409     } else if (fabsf(rotation - m_rotation) > 0.001) {
00410         m_rotation = rotation;
00411         repaint();
00412     }
00413 }
00414 
00415 void
00416 Thumbwheel::mouseReleaseEvent(QMouseEvent *e)
00417 {
00418     if (!m_clicked) return;
00419     bool reallyTracking = m_tracking;
00420     m_tracking = true;
00421     mouseMoveEvent(e);
00422     m_tracking = reallyTracking;
00423     m_clicked = false;
00424 }
00425 
00426 void
00427 Thumbwheel::wheelEvent(QWheelEvent *e)
00428 {
00429     int step = lrintf(m_speed);
00430     if (step == 0) step = 1;
00431 
00432     if (e->delta() > 0) {
00433         setValue(m_value + step);
00434     } else {
00435         setValue(m_value - step);
00436     }
00437     
00438     emit valueChanged(getValue());
00439 }
00440 
00441 void
00442 Thumbwheel::paintEvent(QPaintEvent *)
00443 {
00444     Profiler profiler("Thumbwheel::paintEvent");
00445 
00446     if (!m_cache.isNull()) {
00447         QPainter paint(this);
00448         paint.drawImage(0, 0, m_cache);
00449         return;
00450     }
00451 
00452     Profiler profiler2("Thumbwheel::paintEvent (no cache)");
00453 
00454     m_cache = QImage(size(), QImage::Format_ARGB32);
00455     m_cache.fill(Qt::transparent);
00456 
00457     int bw = 3;
00458 
00459     QRect subclip;
00460     if (m_orientation == Qt::Horizontal) {
00461         subclip = QRect(bw, bw+1, width() - bw*2, height() - bw*2 - 2);
00462     } else {
00463         subclip = QRect(bw+1, bw, width() - bw*2 - 2, height() - bw*2);
00464     }
00465 
00466     QPainter paint(&m_cache);
00467     paint.setClipRect(rect());
00468     paint.fillRect(subclip, palette().background().color());
00469 
00470     paint.setRenderHint(QPainter::Antialiasing, true);
00471 
00472     float w  = width();
00473     float w0 = 0.5;
00474     float w1 = w - 0.5;
00475 
00476     float h  = height();
00477     float h0 = 0.5;
00478     float h1 = h - 0.5;
00479 
00480     for (int i = bw-1; i >= 0; --i) {
00481 
00482         int grey = (i + 1) * (256 / (bw + 1));
00483         QColor fc = QColor(grey, grey, grey);
00484         paint.setPen(fc);
00485 
00486         QPainterPath path;
00487 
00488         if (m_orientation == Qt::Horizontal) {
00489             path.moveTo(w0 + i, h0 + i + 2);
00490             path.quadTo(w/2, i * 1.25, w1 - i, h0 + i + 2);
00491             path.lineTo(w1 - i, h1 - i - 2);
00492             path.quadTo(w/2, h - i * 1.25, w0 + i, h1 - i - 2);
00493             path.closeSubpath();
00494         } else {
00495             path.moveTo(w0 + i + 2, h0 + i);
00496             path.quadTo(i * 1.25, h/2, w0 + i + 2, h1 - i);
00497             path.lineTo(w1 - i - 2, h1 - i);
00498             path.quadTo(w - i * 1.25, h/2, w1 - i - 2, h0 + i);
00499             path.closeSubpath();
00500         }
00501 
00502         paint.drawPath(path);
00503     }
00504 
00505     paint.setClipRect(subclip);
00506 
00507     float radians = m_rotation * 1.5f * M_PI;
00508 
00509 //    cerr << "value = " << m_value << ", min = " << m_min << ", max = " << m_max << ", rotation = " << rotation << endl;
00510 
00511     w = (m_orientation == Qt::Horizontal ? width() : height()) - bw*2;
00512 
00513     // total number of notches on the entire wheel
00514     int notches = 25;
00515     
00516     // radius of the wheel including invisible part
00517     int radius = int(w / 2 + 2);
00518 
00519     for (int i = 0; i < notches; ++i) {
00520 
00521         float a0 = (2.f * M_PI * i) / notches + radians;
00522         float a1 = a0 + M_PI / (notches * 2);
00523         float a2 = (2.f * M_PI * (i + 1)) / notches + radians;
00524 
00525         float depth = cosf((a0 + a2) / 2);
00526         if (depth < 0) continue;
00527 
00528         float x0 = radius * sinf(a0) + w/2;
00529         float x1 = radius * sinf(a1) + w/2;
00530         float x2 = radius * sinf(a2) + w/2;
00531         if (x2 < 0 || x0 > w) continue;
00532 
00533         if (x0 < 0) x0 = 0;
00534         if (x2 > w) x2 = w;
00535 
00536         x0 += bw;
00537         x1 += bw;
00538         x2 += bw;
00539 
00540         int grey = lrintf(120 * depth);
00541 
00542         QColor fc = QColor(grey, grey, grey);
00543         QColor oc = palette().highlight().color();
00544 
00545         paint.setPen(fc);
00546 
00547         if (m_showScale) {
00548 
00549             paint.setBrush(oc);
00550 
00551             float prop;
00552             if (i >= notches / 4) {
00553                 prop = float(notches - (((i - float(notches) / 4.f) * 4.f) / 3.f))
00554                     / notches;
00555             } else {
00556                 prop = 0.f;
00557             }
00558             
00559             if (m_orientation == Qt::Horizontal) {
00560                 paint.drawRect(QRectF(x1, height() - (height() - bw*2) * prop - bw,
00561                                       x2 - x1, height() * prop));
00562             } else {
00563                 paint.drawRect(QRectF(bw, x1, (width() - bw*2) * prop, x2 - x1));
00564             }
00565         }
00566 
00567         paint.setPen(fc);
00568         paint.setBrush(palette().background().color());
00569 
00570         if (m_orientation == Qt::Horizontal) {
00571             paint.drawRect(QRectF(x0, bw, x1 - x0, height() - bw*2));
00572         } else {
00573             paint.drawRect(QRectF(bw, x0, width() - bw*2, x1 - x0));
00574         }
00575     }
00576 
00577     QPainter paint2(this);
00578     paint2.drawImage(0, 0, m_cache);
00579 }
00580 
00581 QSize
00582 Thumbwheel::sizeHint() const
00583 {
00584     if (m_orientation == Qt::Horizontal) {
00585         return QSize(80, 12);
00586     } else {
00587         return QSize(12, 80);
00588     }
00589 }
00590