AudioDial.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     
00008     This program is free software; you can redistribute it and/or
00009     modify it under the terms of the GNU General Public License as
00010     published by the Free Software Foundation; either version 2 of the
00011     License, or (at your option) any later version.  See the file
00012     COPYING included with this distribution for more information.
00013 */
00014 
00038 #include "AudioDial.h"
00039 
00040 #include "base/RangeMapper.h"
00041 
00042 #include <cmath>
00043 #include <iostream>
00044 
00045 #include <QTimer>
00046 #include <QPainter>
00047 #include <QPixmap>
00048 #include <QColormap>
00049 #include <QMouseEvent>
00050 #include <QPaintEvent>
00051 #include <QInputDialog>
00052 
00053 using std::endl;
00054 using std::cerr;
00055 
00056 
00058 
00059 
00060 //-------------------------------------------------------------------------
00061 // AudioDial - Instance knob widget class.
00062 //
00063 
00064 #define AUDIO_DIAL_MIN (0.25 * M_PI)
00065 #define AUDIO_DIAL_MAX (1.75 * M_PI)
00066 #define AUDIO_DIAL_RANGE (AUDIO_DIAL_MAX - AUDIO_DIAL_MIN)
00067 
00068 
00069 //static int dialsExtant = 0;
00070 
00071 
00072 // Constructor.
00073 AudioDial::AudioDial(QWidget *parent) :
00074     QDial(parent),
00075     m_knobColor(Qt::black),
00076     m_meterColor(Qt::white),
00077     m_defaultValue(0),
00078     m_defaultMappedValue(0),
00079     m_mappedValue(0),
00080     m_noMappedUpdate(false),
00081     m_showTooltip(true),
00082     m_rangeMapper(0)
00083 {
00084     m_mouseDial = false;
00085     m_mousePressed = false;
00086 //    ++dialsExtant;
00087 }
00088 
00089 
00090 // Destructor.
00091 AudioDial::~AudioDial (void)
00092 {
00093     delete m_rangeMapper;
00094 //    --dialsExtant;
00095 }
00096 
00097 
00098 void AudioDial::setRangeMapper(RangeMapper *mapper)
00099 {
00100 //    std::cerr << "AudioDial[" << this << "][\"" << objectName().toStdString() << "\"::setRangeMapper(" << mapper << ") [current is " << m_rangeMapper << "] (have " << dialsExtant << " dials extant)" << std::endl;
00101 
00102     if (m_rangeMapper == mapper) return;
00103 
00104     if (!m_rangeMapper && mapper) {
00105         connect(this, SIGNAL(valueChanged(int)),
00106                 this, SLOT(updateMappedValue(int)));
00107     }
00108 
00109     delete m_rangeMapper;
00110     m_rangeMapper = mapper;
00111 
00112     updateMappedValue(value());
00113 }
00114 
00115 
00116 void AudioDial::paintEvent(QPaintEvent *)
00117 {
00118     QPainter paint;
00119 
00120     float angle = AUDIO_DIAL_MIN // offset
00121         + (AUDIO_DIAL_RANGE *
00122            (float(QDial::value() - QDial::minimum()) /
00123             (float(QDial::maximum() - QDial::minimum()))));
00124     int degrees = int(angle * 180.0 / M_PI);
00125 
00126     int ns = notchSize();
00127     int numTicks = 1 + (maximum() + ns - minimum()) / ns;
00128         
00129     QColor knobColor(m_knobColor);
00130     if (knobColor == Qt::black)
00131         knobColor = palette().window().color();
00132 
00133     QColor meterColor(m_meterColor);
00134     if (!isEnabled())
00135         meterColor = palette().mid().color();
00136     else if (m_meterColor == Qt::white)
00137         meterColor = palette().highlight().color();
00138 
00139     int m_size = width() < height() ? width() : height();
00140     int scale = 1;
00141     int width = m_size - 2*scale;
00142 
00143     paint.begin(this);
00144     paint.setRenderHint(QPainter::Antialiasing, true);
00145     paint.translate(1, 1);
00146 
00147     QPen pen;
00148     QColor c;
00149 
00150     // Knob body and face...
00151 
00152     c = knobColor;
00153     pen.setColor(knobColor);
00154     pen.setWidth(scale * 2);
00155     pen.setCapStyle(Qt::FlatCap);
00156         
00157     paint.setPen(pen);
00158     paint.setBrush(c);
00159 
00160     int indent = (int)(width * 0.15 + 1);
00161 
00162     paint.drawEllipse(indent-1, indent-1, width-2*indent, width-2*indent);
00163 
00164     pen.setWidth(3 * scale);
00165     int pos = indent-1 + (width-2*indent) / 20;
00166     int darkWidth = (width-2*indent) * 3 / 4;
00167     while (darkWidth) {
00168         c = c.light(102);
00169         pen.setColor(c);
00170         paint.setPen(pen);
00171         paint.drawEllipse(pos, pos, darkWidth, darkWidth);
00172         if (!--darkWidth) break;
00173         paint.drawEllipse(pos, pos, darkWidth, darkWidth);
00174         if (!--darkWidth) break;
00175         paint.drawEllipse(pos, pos, darkWidth, darkWidth);
00176         ++pos; --darkWidth;
00177     }
00178 
00179     // Tick notches...
00180 
00181     if ( notchesVisible() ) {
00182 //      std::cerr << "Notches visible" << std::endl;
00183         pen.setColor(palette().dark().color());
00184         pen.setWidth(scale);
00185         paint.setPen(pen);
00186         for (int i = 0; i < numTicks; ++i) {
00187             int div = numTicks;
00188             if (div > 1) --div;
00189             drawTick(paint, AUDIO_DIAL_MIN + (AUDIO_DIAL_MAX - AUDIO_DIAL_MIN) * i / div,
00190                      width, true);
00191         }
00192     }
00193 
00194     // The bright metering bit...
00195 
00196     c = meterColor;
00197     pen.setColor(c);
00198     pen.setWidth(indent);
00199     paint.setPen(pen);
00200 
00201 //    std::cerr << "degrees " << degrees << ", gives us " << -(degrees - 45) * 16 << std::endl;
00202 
00203     int arcLen = -(degrees - 45) * 16;
00204     if (arcLen == 0) arcLen = -16;
00205 
00206     paint.drawArc(indent/2, indent/2,
00207                   width-indent, width-indent, (180 + 45) * 16, arcLen);
00208 
00209     paint.setBrush(Qt::NoBrush);
00210 
00211     // Shadowing...
00212 
00213     pen.setWidth(scale);
00214     paint.setPen(pen);
00215 
00216     // Knob shadow...
00217 
00218     int shadowAngle = -720;
00219     c = knobColor.dark();
00220     for (int arc = 120; arc < 2880; arc += 240) {
00221         pen.setColor(c);
00222         paint.setPen(pen);
00223         paint.drawArc(indent, indent,
00224                       width-2*indent, width-2*indent, shadowAngle + arc, 240);
00225         paint.drawArc(indent, indent,
00226                       width-2*indent, width-2*indent, shadowAngle - arc, 240);
00227         c = c.light(110);
00228     }
00229 
00230     // Scale shadow...
00231 
00232     shadowAngle = 2160;
00233     c = palette().dark().color();
00234     for (int arc = 120; arc < 2880; arc += 240) {
00235         pen.setColor(c);
00236         paint.setPen(pen);
00237         paint.drawArc(scale/2, scale/2,
00238                       width-scale, width-scale, shadowAngle + arc, 240);
00239         paint.drawArc(scale/2, scale/2,
00240                       width-scale, width-scale, shadowAngle - arc, 240);
00241         c = c.light(108);
00242     }
00243 
00244     // Undraw the bottom part...
00245 
00246     pen.setColor(palette().background().color());
00247     pen.setWidth(scale * 4);
00248     paint.setPen(pen);
00249     paint.drawArc(scale/2, scale/2,
00250                   width-scale, width-scale, -45 * 16, -92 * 16);
00251 
00252     // Scale ends...
00253 
00254     pen.setColor(palette().dark().color());
00255     pen.setWidth(scale);
00256     paint.setPen(pen);
00257     for (int i = 0; i < numTicks; ++i) {
00258         if (i != 0 && i != numTicks - 1) continue;
00259         int div = numTicks;
00260         if (div > 1) --div;
00261         drawTick(paint, AUDIO_DIAL_MIN + (AUDIO_DIAL_MAX - AUDIO_DIAL_MIN) * i / div,
00262                  width, false);
00263     }
00264 
00265     // Pointer notch...
00266 
00267     float hyp = float(width) / 2.0;
00268     float len = hyp - indent;
00269     --len;
00270 
00271     float x0 = hyp;
00272     float y0 = hyp;
00273 
00274     float x = hyp - len * sin(angle);
00275     float y = hyp + len * cos(angle);
00276 
00277     c = palette().dark().color();
00278     pen.setColor(isEnabled() ? c.dark(130) : c);
00279     pen.setWidth(scale * 2);
00280     paint.setPen(pen);
00281     paint.drawLine(int(x0), int(y0), int(x), int(y));
00282 
00283     paint.end();
00284 }
00285 
00286 
00287 void AudioDial::drawTick(QPainter &paint,
00288                          float angle, int size, bool internal)
00289 {
00290     float hyp = float(size) / 2.0;
00291     float x0 = hyp - (hyp - 1) * sin(angle);
00292     float y0 = hyp + (hyp - 1) * cos(angle);
00293 
00294 //    cerr << "drawTick: angle " << angle << ", size " << size << ", internal " << internal << endl;
00295     
00296     if (internal) {
00297 
00298         float len = hyp / 4;
00299         float x1 = hyp - (hyp - len) * sin(angle);
00300         float y1 = hyp + (hyp - len) * cos(angle);
00301                 
00302         paint.drawLine(int(x0), int(y0), int(x1), int(y1));
00303 
00304     } else {
00305 
00306         float len = hyp / 4;
00307         float x1 = hyp - (hyp + len) * sin(angle);
00308         float y1 = hyp + (hyp + len) * cos(angle);
00309 
00310         paint.drawLine(int(x0), int(y0), int(x1), int(y1));
00311     }
00312 }
00313 
00314 
00315 void AudioDial::setKnobColor(const QColor& color)
00316 {
00317     m_knobColor = color;
00318     update();
00319 }
00320 
00321 
00322 void AudioDial::setMeterColor(const QColor& color)
00323 {
00324     m_meterColor = color;
00325     update();
00326 }
00327 
00328 
00329 void AudioDial::setMouseDial(bool mouseDial)
00330 {
00331     m_mouseDial = mouseDial;
00332 }
00333 
00334 
00335 void AudioDial::setDefaultValue(int defaultValue)
00336 {
00337     m_defaultValue = defaultValue;
00338     if (m_rangeMapper) {
00339         m_defaultMappedValue = m_rangeMapper->getValueForPosition(defaultValue);
00340     }
00341 }
00342 
00343 void AudioDial::setValue(int value)
00344 {
00345     QDial::setValue(value);
00346     updateMappedValue(value);
00347 }
00348 
00349 void AudioDial::setDefaultMappedValue(float value)
00350 {
00351     m_defaultMappedValue = value;
00352     if (m_rangeMapper) {
00353         m_defaultValue = m_rangeMapper->getPositionForValue(value);
00354     }
00355 }
00356 
00357 void AudioDial::setMappedValue(float mappedValue)
00358 {
00359     if (m_rangeMapper) {
00360         int newPosition = m_rangeMapper->getPositionForValue(mappedValue);
00361         bool changed = (m_mappedValue != mappedValue);
00362         m_mappedValue = mappedValue;
00363         m_noMappedUpdate = true;
00364         std::cerr << "AudioDial::setMappedValue(" << mappedValue << "): new position is " << newPosition << std::endl;
00365         if (newPosition != value()) {
00366             setValue(newPosition);
00367         } else if (changed) {
00368             emit valueChanged(newPosition);
00369         }
00370         m_noMappedUpdate = false;
00371     } else {
00372         setValue(int(mappedValue));
00373     }
00374 }
00375 
00376 
00377 void AudioDial::setShowToolTip(bool show)
00378 {
00379     m_showTooltip = show;
00380     m_noMappedUpdate = true;
00381     updateMappedValue(value());
00382     m_noMappedUpdate = false;
00383 }
00384 
00385 
00386 float AudioDial::mappedValue() const
00387 {
00388     if (m_rangeMapper) {
00389 //        std::cerr << "AudioDial::mappedValue(): value = " << value() << ", mappedValue = " << m_mappedValue << std::endl;
00390         return m_mappedValue;
00391     }
00392     return value();
00393 }
00394 
00395 
00396 void AudioDial::updateMappedValue(int value)
00397 {
00398     if (!m_noMappedUpdate) {
00399         if (m_rangeMapper) {
00400             m_mappedValue = m_rangeMapper->getValueForPosition(value);
00401         } else {
00402             m_mappedValue = value;
00403         }
00404     }
00405 
00406     if (m_showTooltip) {
00407         QString name = objectName();
00408         QString unit = "";
00409         QString text;
00410         if (m_rangeMapper) unit = m_rangeMapper->getUnit();
00411         if (name != "") {
00412             text = tr("%1: %2%3").arg(name).arg(m_mappedValue).arg(unit);
00413         } else {
00414             text = tr("%2%3").arg(m_mappedValue).arg(unit);
00415         }
00416         setToolTip(text);
00417     }
00418 }
00419 
00420 void
00421 AudioDial::setToDefault()
00422 {
00423     if (m_rangeMapper) {
00424         setMappedValue(m_defaultMappedValue);
00425         return;
00426     }
00427     int dv = m_defaultValue;
00428     if (dv < minimum()) dv = minimum();
00429     if (dv > maximum()) dv = maximum();
00430     setValue(m_defaultValue);
00431 }
00432 
00433 // Alternate mouse behavior event handlers.
00434 void AudioDial::mousePressEvent(QMouseEvent *mouseEvent)
00435 {
00436     if (m_mouseDial) {
00437         QDial::mousePressEvent(mouseEvent);
00438     } else if (mouseEvent->button() == Qt::MidButton ||
00439                ((mouseEvent->button() == Qt::LeftButton) &&
00440                 (mouseEvent->modifiers() & Qt::ControlModifier))) {
00441         setToDefault();
00442     } else if (mouseEvent->button() == Qt::LeftButton) {
00443         m_mousePressed = true;
00444         m_posMouse = mouseEvent->pos();
00445     }
00446 }
00447 
00448 
00449 void AudioDial::mouseDoubleClickEvent(QMouseEvent *mouseEvent)
00450 {
00452 
00453     if (m_mouseDial) {
00454         QDial::mouseDoubleClickEvent(mouseEvent);
00455     } else if (mouseEvent->button() != Qt::LeftButton) {
00456         return;
00457     }
00458 
00459     bool ok = false;
00460 
00461     if (m_rangeMapper) {
00462         
00463         float min = m_rangeMapper->getValueForPosition(minimum());
00464         float max = m_rangeMapper->getValueForPosition(maximum());
00465         
00466         if (min > max) { 
00467             float tmp = min;
00468             min = max;
00469             max = tmp;
00470         }
00471 
00472         QString unit = m_rangeMapper->getUnit();
00473         
00474         QString text;
00475         if (objectName() != "") {
00476             if (unit != "") {
00477                 text = tr("New value for %1, from %2 to %3 %4:")
00478                     .arg(objectName()).arg(min).arg(max).arg(unit);
00479             } else {
00480                 text = tr("New value for %1, from %2 to %3:")
00481                     .arg(objectName()).arg(min).arg(max);
00482             }
00483         } else {
00484             if (unit != "") {
00485                 text = tr("Enter a new value from %1 to %2 %3:")
00486                     .arg(min).arg(max).arg(unit);
00487             } else {
00488                 text = tr("Enter a new value from %1 to %2:")
00489                     .arg(min).arg(max);
00490             }
00491         }
00492         
00493         float newValue = QInputDialog::getDouble
00494             (this,
00495              tr("Enter new value"),
00496              text,
00497              m_mappedValue,
00498              min,
00499              max,
00500              4, 
00501              &ok);
00502         
00503         if (ok) {
00504             setMappedValue(newValue);
00505         }
00506         
00507     } else {
00508         
00509         int newPosition = QInputDialog::getInteger
00510             (this,
00511              tr("Enter new value"),
00512              tr("Enter a new value from %1 to %2:")
00513              .arg(minimum()).arg(maximum()),
00514              value(), minimum(), maximum(), pageStep(), &ok);
00515         
00516         if (ok) {
00517             setValue(newPosition);
00518         }
00519     }
00520 }
00521 
00522 
00523 void AudioDial::mouseMoveEvent(QMouseEvent *mouseEvent)
00524 {
00525     if (m_mouseDial) {
00526         QDial::mouseMoveEvent(mouseEvent);
00527     } else if (m_mousePressed) {
00528         const QPoint& posMouse = mouseEvent->pos();
00529         int v = QDial::value()
00530             + (posMouse.x() - m_posMouse.x())
00531             + (m_posMouse.y() - posMouse.y());
00532         if (v > QDial::maximum())
00533             v = QDial::maximum();
00534         else
00535             if (v < QDial::minimum())
00536                 v = QDial::minimum();
00537         m_posMouse = posMouse;
00538         QDial::setValue(v);
00539     }
00540 }
00541 
00542 
00543 void AudioDial::mouseReleaseEvent(QMouseEvent *mouseEvent)
00544 {
00545     if (m_mouseDial) {
00546         QDial::mouseReleaseEvent(mouseEvent);
00547     } else if (m_mousePressed) {
00548         m_mousePressed = false;
00549     }
00550 }
00551 
00552 void
00553 AudioDial::enterEvent(QEvent *e)
00554 {
00555     QDial::enterEvent(e);
00556     emit mouseEntered();
00557 }
00558 
00559 void
00560 AudioDial::leaveEvent(QEvent *e)
00561 {
00562     QDial::enterEvent(e);
00563     emit mouseLeft();
00564 }
00565 

Generated on Wed Feb 20 15:45:24 2008 for SonicVisualiser by  doxygen 1.5.1