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