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 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