annotate widgets/Thumbwheel.cpp @ 189:5b7472db612b

* Add large chunks of context help in the optional status bar * Add an extra overlay mode in which even the centre frame is disabled * Fixes to FTP retrieval
author Chris Cannam
date Fri, 19 Jan 2007 13:13:14 +0000
parents dd573e090eed
children 53835534a9d3
rev   line source
Chris@132 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@132 2
Chris@132 3 /*
Chris@132 4 Sonic Visualiser
Chris@132 5 An audio file viewer and annotation editor.
Chris@132 6 Centre for Digital Music, Queen Mary, University of London.
Chris@182 7 This file copyright 2006 QMUL.
Chris@132 8
Chris@132 9 This program is free software; you can redistribute it and/or
Chris@132 10 modify it under the terms of the GNU General Public License as
Chris@132 11 published by the Free Software Foundation; either version 2 of the
Chris@132 12 License, or (at your option) any later version. See the file
Chris@132 13 COPYING included with this distribution for more information.
Chris@132 14 */
Chris@132 15
Chris@132 16 #include "Thumbwheel.h"
Chris@132 17
Chris@187 18 #include "base/RangeMapper.h"
Chris@187 19
Chris@132 20 #include <QMouseEvent>
Chris@132 21 #include <QPaintEvent>
Chris@132 22 #include <QWheelEvent>
Chris@187 23 #include <QInputDialog>
Chris@132 24 #include <QPainter>
Chris@132 25
Chris@132 26 #include <cmath>
Chris@132 27 #include <iostream>
Chris@132 28
Chris@133 29 Thumbwheel::Thumbwheel(Qt::Orientation orientation,
Chris@132 30 QWidget *parent) :
Chris@132 31 QWidget(parent),
Chris@133 32 m_min(0),
Chris@133 33 m_max(100),
Chris@133 34 m_default(50),
Chris@133 35 m_value(50),
Chris@187 36 m_mappedValue(50),
Chris@187 37 m_noMappedUpdate(false),
Chris@165 38 m_rotation(0.5),
Chris@132 39 m_orientation(orientation),
Chris@165 40 m_speed(1.0),
Chris@132 41 m_tracking(true),
Chris@132 42 m_showScale(true),
Chris@132 43 m_clicked(false),
Chris@133 44 m_atDefault(true),
Chris@187 45 m_clickRotation(m_rotation),
Chris@187 46 m_showTooltip(true),
Chris@187 47 m_rangeMapper(0)
Chris@132 48 {
Chris@132 49 }
Chris@132 50
Chris@132 51 Thumbwheel::~Thumbwheel()
Chris@132 52 {
Chris@187 53 delete m_rangeMapper;
Chris@187 54 }
Chris@187 55
Chris@187 56 void
Chris@187 57 Thumbwheel::setRangeMapper(RangeMapper *mapper)
Chris@187 58 {
Chris@187 59 if (m_rangeMapper == mapper) return;
Chris@187 60
Chris@187 61 if (!m_rangeMapper && mapper) {
Chris@187 62 connect(this, SIGNAL(valueChanged(int)),
Chris@187 63 this, SLOT(updateMappedValue(int)));
Chris@187 64 }
Chris@187 65
Chris@187 66 delete m_rangeMapper;
Chris@187 67 m_rangeMapper = mapper;
Chris@187 68
Chris@187 69 updateMappedValue(getValue());
Chris@187 70 }
Chris@187 71
Chris@187 72 void
Chris@187 73 Thumbwheel::setShowToolTip(bool show)
Chris@187 74 {
Chris@187 75 m_showTooltip = show;
Chris@187 76 m_noMappedUpdate = true;
Chris@187 77 updateMappedValue(getValue());
Chris@187 78 m_noMappedUpdate = false;
Chris@132 79 }
Chris@132 80
Chris@132 81 void
Chris@133 82 Thumbwheel::setMinimumValue(int min)
Chris@133 83 {
Chris@133 84 if (m_min == min) return;
Chris@133 85
Chris@133 86 m_min = min;
Chris@133 87 if (m_max <= m_min) m_max = m_min + 1;
Chris@133 88 if (m_value < m_min) m_value = m_min;
Chris@133 89 if (m_value > m_max) m_value = m_max;
Chris@165 90
Chris@165 91 m_rotation = float(m_value - m_min) / float(m_max - m_min);
Chris@165 92 update();
Chris@133 93 }
Chris@133 94
Chris@133 95 int
Chris@133 96 Thumbwheel::getMinimumValue() const
Chris@133 97 {
Chris@133 98 return m_min;
Chris@133 99 }
Chris@133 100
Chris@133 101 void
Chris@133 102 Thumbwheel::setMaximumValue(int max)
Chris@133 103 {
Chris@133 104 if (m_max == max) return;
Chris@133 105
Chris@133 106 m_max = max;
Chris@133 107 if (m_min >= m_max) m_min = m_max - 1;
Chris@133 108 if (m_value < m_min) m_value = m_min;
Chris@133 109 if (m_value > m_max) m_value = m_max;
Chris@165 110
Chris@165 111 m_rotation = float(m_value - m_min) / float(m_max - m_min);
Chris@165 112 update();
Chris@133 113 }
Chris@133 114
Chris@133 115 int
Chris@133 116 Thumbwheel::getMaximumValue() const
Chris@133 117 {
Chris@133 118 return m_max;
Chris@133 119 }
Chris@133 120
Chris@133 121 void
Chris@133 122 Thumbwheel::setDefaultValue(int deft)
Chris@133 123 {
Chris@133 124 if (m_default == deft) return;
Chris@133 125
Chris@133 126 m_default = deft;
Chris@133 127 if (m_atDefault) {
Chris@133 128 setValue(m_default);
Chris@165 129 m_atDefault = true; // setValue unsets this
Chris@133 130 emit valueChanged(getValue());
Chris@133 131 }
Chris@133 132 }
Chris@133 133
Chris@187 134 void
Chris@187 135 Thumbwheel::setMappedValue(float mappedValue)
Chris@187 136 {
Chris@187 137 if (m_rangeMapper) {
Chris@187 138 int newValue = m_rangeMapper->getPositionForValue(mappedValue);
Chris@187 139 m_mappedValue = mappedValue;
Chris@187 140 m_noMappedUpdate = true;
Chris@187 141 std::cerr << "Thumbwheel::setMappedValue(" << mappedValue << "): new value is " << newValue << std::endl;
Chris@187 142 if (newValue != getValue()) {
Chris@187 143 setValue(newValue);
Chris@187 144 }
Chris@187 145 emit valueChanged(newValue);
Chris@187 146 m_noMappedUpdate = false;
Chris@187 147 } else {
Chris@187 148 setValue(int(mappedValue));
Chris@187 149 emit valueChanged(int(mappedValue));
Chris@187 150 }
Chris@187 151 }
Chris@187 152
Chris@133 153 int
Chris@133 154 Thumbwheel::getDefaultValue() const
Chris@133 155 {
Chris@133 156 return m_default;
Chris@133 157 }
Chris@133 158
Chris@133 159 void
Chris@132 160 Thumbwheel::setValue(int value)
Chris@132 161 {
Chris@187 162 std::cerr << "Thumbwheel::setValue(" << value << ") (from " << m_value
Chris@187 163 << ", rotation " << m_rotation << ")" << std::endl;
Chris@133 164
Chris@165 165 if (m_value != value) {
Chris@165 166
Chris@165 167 m_atDefault = false;
Chris@165 168
Chris@165 169 if (value < m_min) value = m_min;
Chris@165 170 if (value > m_max) value = m_max;
Chris@165 171 m_value = value;
Chris@165 172 }
Chris@165 173
Chris@165 174 m_rotation = float(m_value - m_min) / float(m_max - m_min);
Chris@132 175 update();
Chris@132 176 }
Chris@132 177
Chris@133 178 void
Chris@133 179 Thumbwheel::resetToDefault()
Chris@133 180 {
Chris@133 181 if (m_default == m_value) return;
Chris@133 182 setValue(m_default);
Chris@133 183 m_atDefault = true;
Chris@133 184 emit valueChanged(getValue());
Chris@133 185 }
Chris@133 186
Chris@132 187 int
Chris@132 188 Thumbwheel::getValue() const
Chris@132 189 {
Chris@132 190 return m_value;
Chris@132 191 }
Chris@132 192
Chris@187 193 float
Chris@187 194 Thumbwheel::getMappedValue() const
Chris@187 195 {
Chris@187 196 if (m_rangeMapper) {
Chris@187 197 std::cerr << "Thumbwheel::getMappedValue(): value = " << getValue() << ", mappedValue = " << m_mappedValue << std::endl;
Chris@187 198 return m_mappedValue;
Chris@187 199 }
Chris@187 200 return getValue();
Chris@187 201 }
Chris@187 202
Chris@187 203 void
Chris@187 204 Thumbwheel::updateMappedValue(int value)
Chris@187 205 {
Chris@187 206 if (!m_noMappedUpdate) {
Chris@187 207 if (m_rangeMapper) {
Chris@187 208 m_mappedValue = m_rangeMapper->getValueForPosition(value);
Chris@187 209 } else {
Chris@187 210 m_mappedValue = value;
Chris@187 211 }
Chris@187 212 }
Chris@187 213
Chris@187 214 if (m_showTooltip) {
Chris@187 215 QString name = objectName();
Chris@187 216 QString unit = "";
Chris@187 217 QString text;
Chris@187 218 if (m_rangeMapper) unit = m_rangeMapper->getUnit();
Chris@187 219 if (name != "") {
Chris@187 220 text = tr("%1: %2%3").arg(name).arg(m_mappedValue).arg(unit);
Chris@187 221 } else {
Chris@187 222 text = tr("%2%3").arg(m_mappedValue).arg(unit);
Chris@187 223 }
Chris@187 224 setToolTip(text);
Chris@187 225 }
Chris@187 226 }
Chris@187 227
Chris@132 228 void
Chris@132 229 Thumbwheel::setSpeed(float speed)
Chris@132 230 {
Chris@132 231 m_speed = speed;
Chris@132 232 }
Chris@132 233
Chris@132 234 float
Chris@132 235 Thumbwheel::getSpeed() const
Chris@132 236 {
Chris@132 237 return m_speed;
Chris@132 238 }
Chris@132 239
Chris@132 240 void
Chris@132 241 Thumbwheel::setTracking(bool tracking)
Chris@132 242 {
Chris@132 243 m_tracking = tracking;
Chris@132 244 }
Chris@132 245
Chris@132 246 bool
Chris@132 247 Thumbwheel::getTracking() const
Chris@132 248 {
Chris@132 249 return m_tracking;
Chris@132 250 }
Chris@132 251
Chris@132 252 void
Chris@132 253 Thumbwheel::setShowScale(bool showScale)
Chris@132 254 {
Chris@132 255 m_showScale = showScale;
Chris@132 256 }
Chris@132 257
Chris@132 258 bool
Chris@132 259 Thumbwheel::getShowScale() const
Chris@132 260 {
Chris@132 261 return m_showScale;
Chris@132 262 }
Chris@132 263
Chris@132 264 void
Chris@189 265 Thumbwheel::enterEvent(QEvent *)
Chris@189 266 {
Chris@189 267 emit mouseEntered();
Chris@189 268 }
Chris@189 269
Chris@189 270 void
Chris@189 271 Thumbwheel::leaveEvent(QEvent *)
Chris@189 272 {
Chris@189 273 emit mouseLeft();
Chris@189 274 }
Chris@189 275
Chris@189 276 void
Chris@132 277 Thumbwheel::mousePressEvent(QMouseEvent *e)
Chris@132 278 {
Chris@187 279 if (e->button() == Qt::MidButton ||
Chris@187 280 ((e->button() == Qt::LeftButton) &&
Chris@187 281 (e->modifiers() & Qt::ControlModifier))) {
Chris@187 282 resetToDefault();
Chris@187 283 } else if (e->button() == Qt::LeftButton) {
Chris@133 284 m_clicked = true;
Chris@133 285 m_clickPos = e->pos();
Chris@165 286 m_clickRotation = m_rotation;
Chris@133 287 }
Chris@132 288 }
Chris@132 289
Chris@132 290 void
Chris@187 291 Thumbwheel::mouseDoubleClickEvent(QMouseEvent *mouseEvent)
Chris@132 292 {
Chris@188 293 //!!! needs a common base class with AudioDial (and Panner?)
Chris@187 294
Chris@187 295 if (mouseEvent->button() != Qt::LeftButton) {
Chris@187 296 return;
Chris@187 297 }
Chris@187 298
Chris@187 299 bool ok = false;
Chris@187 300
Chris@187 301 if (m_rangeMapper) {
Chris@187 302
Chris@187 303 float min = m_rangeMapper->getValueForPosition(m_min);
Chris@187 304 float max = m_rangeMapper->getValueForPosition(m_max);
Chris@187 305
Chris@187 306 if (min > max) {
Chris@187 307 float tmp = min;
Chris@187 308 min = max;
Chris@187 309 max = tmp;
Chris@187 310 }
Chris@187 311
Chris@187 312 QString unit = m_rangeMapper->getUnit();
Chris@187 313
Chris@187 314 QString text;
Chris@187 315 if (objectName() != "") {
Chris@187 316 if (unit != "") {
Chris@187 317 text = tr("New value for %1, from %2 to %3 %4:")
Chris@187 318 .arg(objectName()).arg(min).arg(max).arg(unit);
Chris@187 319 } else {
Chris@187 320 text = tr("New value for %1, from %2 to %3:")
Chris@187 321 .arg(objectName()).arg(min).arg(max);
Chris@187 322 }
Chris@187 323 } else {
Chris@187 324 if (unit != "") {
Chris@187 325 text = tr("Enter a new value from %1 to %2 %3:")
Chris@187 326 .arg(min).arg(max).arg(unit);
Chris@187 327 } else {
Chris@187 328 text = tr("Enter a new value from %1 to %2:")
Chris@187 329 .arg(min).arg(max);
Chris@187 330 }
Chris@187 331 }
Chris@187 332
Chris@187 333 float newValue = QInputDialog::getDouble
Chris@187 334 (this,
Chris@187 335 tr("Enter new value"),
Chris@187 336 text,
Chris@187 337 m_mappedValue,
Chris@187 338 min,
Chris@187 339 max,
Chris@187 340 4,
Chris@187 341 &ok);
Chris@187 342
Chris@187 343 if (ok) {
Chris@187 344 setMappedValue(newValue);
Chris@187 345 }
Chris@187 346
Chris@187 347 } else {
Chris@187 348
Chris@187 349 int newValue = QInputDialog::getInteger
Chris@187 350 (this,
Chris@187 351 tr("Enter new value"),
Chris@187 352 tr("Enter a new value from %1 to %2:")
Chris@187 353 .arg(m_min).arg(m_max),
Chris@187 354 getValue(), m_min, m_max, 1, &ok);
Chris@187 355
Chris@187 356 if (ok) {
Chris@187 357 setValue(newValue);
Chris@187 358 }
Chris@187 359 }
Chris@132 360 }
Chris@132 361
Chris@187 362
Chris@132 363 void
Chris@132 364 Thumbwheel::mouseMoveEvent(QMouseEvent *e)
Chris@132 365 {
Chris@133 366 if (!m_clicked) return;
Chris@132 367 int dist = 0;
Chris@132 368 if (m_orientation == Qt::Horizontal) {
Chris@132 369 dist = e->x() - m_clickPos.x();
Chris@132 370 } else {
Chris@132 371 dist = e->y() - m_clickPos.y();
Chris@132 372 }
Chris@165 373
Chris@165 374 float rotation = m_clickRotation + (m_speed * dist) / 100;
Chris@165 375 if (rotation < 0.f) rotation = 0.f;
Chris@165 376 if (rotation > 1.f) rotation = 1.f;
Chris@165 377 int value = lrintf(m_min + (m_max - m_min) * m_rotation);
Chris@132 378 if (value != m_value) {
Chris@132 379 setValue(value);
Chris@132 380 if (m_tracking) emit valueChanged(getValue());
Chris@165 381 m_rotation = rotation;
Chris@165 382 } else if (fabsf(rotation - m_rotation) > 0.001) {
Chris@165 383 m_rotation = rotation;
Chris@165 384 repaint();
Chris@165 385 }
Chris@132 386 }
Chris@132 387
Chris@132 388 void
Chris@132 389 Thumbwheel::mouseReleaseEvent(QMouseEvent *e)
Chris@132 390 {
Chris@133 391 if (!m_clicked) return;
Chris@132 392 bool reallyTracking = m_tracking;
Chris@132 393 m_tracking = true;
Chris@132 394 mouseMoveEvent(e);
Chris@132 395 m_tracking = reallyTracking;
Chris@133 396 m_clicked = false;
Chris@132 397 }
Chris@132 398
Chris@132 399 void
Chris@132 400 Thumbwheel::wheelEvent(QWheelEvent *e)
Chris@132 401 {
Chris@132 402 int step = lrintf(m_speed);
Chris@132 403 if (step == 0) step = 1;
Chris@132 404
Chris@132 405 if (e->delta() > 0) {
Chris@132 406 setValue(m_value + step);
Chris@132 407 } else {
Chris@132 408 setValue(m_value - step);
Chris@132 409 }
Chris@132 410
Chris@132 411 emit valueChanged(getValue());
Chris@132 412 }
Chris@132 413
Chris@132 414 void
Chris@132 415 Thumbwheel::paintEvent(QPaintEvent *)
Chris@132 416 {
Chris@133 417 QPainter paint(this);
Chris@133 418 paint.fillRect(rect(), palette().background().color());
Chris@133 419 paint.setRenderHint(QPainter::Antialiasing, false);
Chris@133 420
Chris@133 421 int bw = 3;
Chris@133 422
Chris@133 423 for (int i = 0; i < bw; ++i) {
Chris@133 424 int grey = (i + 1) * (256 / (bw + 1));
Chris@133 425 QColor fc = QColor(grey, grey, grey);
Chris@133 426 paint.setPen(fc);
Chris@133 427 paint.drawRect(i, i, width() - i*2 - 1, height() - i*2 - 1);
Chris@133 428 }
Chris@133 429
Chris@133 430 paint.setClipRect(QRect(bw, bw, width() - bw*2, height() - bw*2));
Chris@133 431
Chris@165 432 float radians = m_rotation * 1.5f * M_PI;
Chris@132 433
Chris@132 434 // std::cerr << "value = " << m_value << ", min = " << m_min << ", max = " << m_max << ", rotation = " << rotation << std::endl;
Chris@132 435
Chris@133 436 int w = (m_orientation == Qt::Horizontal ? width() : height()) - bw*2;
Chris@132 437
Chris@132 438 // total number of notches on the entire wheel
Chris@132 439 int notches = 25;
Chris@132 440
Chris@132 441 // radius of the wheel including invisible part
Chris@132 442 int radius = w / 2 + 2;
Chris@132 443
Chris@132 444 paint.setRenderHint(QPainter::Antialiasing, true);
Chris@132 445
Chris@132 446 for (int i = 0; i < notches; ++i) {
Chris@132 447
Chris@165 448 float a0 = (2.f * M_PI * i) / notches + radians;
Chris@132 449 float a1 = a0 + M_PI / (notches * 2);
Chris@165 450 float a2 = (2.f * M_PI * (i + 1)) / notches + radians;
Chris@132 451
Chris@132 452 float depth = cosf((a0 + a2) / 2);
Chris@132 453 if (depth < 0) continue;
Chris@132 454
Chris@132 455 float x0 = radius * sinf(a0) + w/2;
Chris@132 456 float x1 = radius * sinf(a1) + w/2;
Chris@132 457 float x2 = radius * sinf(a2) + w/2;
Chris@132 458 if (x2 < 0 || x0 > w) continue;
Chris@132 459
Chris@132 460 if (x0 < 0) x0 = 0;
Chris@132 461 if (x2 > w) x2 = w;
Chris@132 462
Chris@133 463 x0 += bw;
Chris@133 464 x1 += bw;
Chris@133 465 x2 += bw;
Chris@133 466
Chris@132 467 int grey = lrintf(255 * depth);
Chris@132 468 QColor fc = QColor(grey, grey, grey);
Chris@132 469 QColor oc = palette().dark().color();
Chris@132 470
Chris@132 471 paint.setPen(oc);
Chris@132 472 paint.setBrush(fc);
Chris@132 473
Chris@132 474 if (m_orientation == Qt::Horizontal) {
Chris@133 475 paint.drawRect(QRectF(x1, bw, x2 - x1, height() - bw*2));
Chris@132 476 } else {
Chris@133 477 paint.drawRect(QRectF(bw, x1, width() - bw*2, x2 - x1));
Chris@132 478 }
Chris@132 479
Chris@132 480 if (m_showScale) {
Chris@132 481
Chris@132 482 paint.setBrush(oc);
Chris@132 483
Chris@132 484 float prop;
Chris@132 485 if (i >= notches / 4) {
Chris@132 486 prop = float(notches - (((i - float(notches) / 4.f) * 4.f) / 3.f))
Chris@132 487 / notches;
Chris@132 488 } else {
Chris@132 489 prop = 0.f;
Chris@132 490 }
Chris@132 491
Chris@132 492 if (m_orientation == Qt::Horizontal) {
Chris@133 493 paint.drawRect(QRectF(x1, height() - (height() - bw*2) * prop - bw,
Chris@132 494 x2 - x1, height() * prop));
Chris@132 495 } else {
Chris@133 496 paint.drawRect(QRectF(bw, x1, (width() - bw*2) * prop, x2 - x1));
Chris@132 497 }
Chris@132 498 }
Chris@132 499
Chris@132 500 paint.setPen(oc);
Chris@132 501 paint.setBrush(palette().background().color());
Chris@132 502
Chris@132 503 if (m_orientation == Qt::Horizontal) {
Chris@133 504 paint.drawRect(QRectF(x0, bw, x1 - x0, height() - bw*2));
Chris@132 505 } else {
Chris@133 506 paint.drawRect(QRectF(bw, x0, width() - bw*2, x1 - x0));
Chris@132 507 }
Chris@132 508 }
Chris@132 509 }
Chris@132 510
Chris@132 511 QSize
Chris@132 512 Thumbwheel::sizeHint() const
Chris@132 513 {
Chris@132 514 if (m_orientation == Qt::Horizontal) {
Chris@132 515 return QSize(80, 12);
Chris@132 516 } else {
Chris@132 517 return QSize(12, 80);
Chris@132 518 }
Chris@132 519 }
Chris@132 520