annotate widgets/LevelPanWidget.cpp @ 930:86df7de08e03 tonioni

A number of fixes to level-pan button (including mute toggle)
author Chris Cannam
date Wed, 25 Mar 2015 10:55:09 +0000
parents 20698aa6a517
children 37bb4b416c52
rev   line source
Chris@923 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@923 2
Chris@923 3 /*
Chris@923 4 Sonic Visualiser
Chris@923 5 An audio file viewer and annotation editor.
Chris@923 6 Centre for Digital Music, Queen Mary, University of London.
Chris@923 7
Chris@923 8 This program is free software; you can redistribute it and/or
Chris@923 9 modify it under the terms of the GNU General Public License as
Chris@923 10 published by the Free Software Foundation; either version 2 of the
Chris@923 11 License, or (at your option) any later version. See the file
Chris@923 12 COPYING included with this distribution for more information.
Chris@923 13 */
Chris@923 14
Chris@923 15 #include "LevelPanWidget.h"
Chris@923 16
Chris@923 17 #include <QPainter>
Chris@923 18 #include <QMouseEvent>
Chris@923 19 #include <QWheelEvent>
Chris@923 20
Chris@923 21 #include "layer/ColourMapper.h"
Chris@925 22 #include "base/AudioLevel.h"
Chris@923 23
Chris@923 24 #include <iostream>
Chris@926 25 #include <cmath>
Chris@923 26
Chris@923 27 using std::cerr;
Chris@923 28 using std::endl;
Chris@923 29
Chris@924 30 static const int maxLevel = 4;
Chris@923 31 static const int maxPan = 2; // range is -maxPan to maxPan
Chris@923 32
Chris@923 33 LevelPanWidget::LevelPanWidget(QWidget *parent) :
Chris@923 34 QWidget(parent),
Chris@923 35 m_level(maxLevel),
Chris@923 36 m_pan(0),
Chris@923 37 m_editable(true)
Chris@923 38 {
Chris@923 39 }
Chris@923 40
Chris@923 41 LevelPanWidget::~LevelPanWidget()
Chris@923 42 {
Chris@923 43 }
Chris@923 44
Chris@929 45 QSize
Chris@929 46 LevelPanWidget::sizeHint() const
Chris@929 47 {
Chris@929 48 static double ratio = 0.0;
Chris@929 49 if (ratio == 0.0) {
Chris@929 50 double baseEm;
Chris@929 51 #ifdef Q_OS_MAC
Chris@929 52 baseEm = 17.0;
Chris@929 53 #else
Chris@929 54 baseEm = 15.0;
Chris@929 55 #endif
Chris@929 56 double em = QFontMetrics(QFont()).height();
Chris@929 57 ratio = em / baseEm;
Chris@929 58 }
Chris@929 59
Chris@929 60 int pixels = 40;
Chris@929 61 int scaled = int(pixels * ratio + 0.5);
Chris@929 62 if (pixels != 0 && scaled == 0) scaled = 1;
Chris@929 63 return QSize(scaled, scaled);
Chris@929 64 }
Chris@929 65
Chris@923 66 void
Chris@925 67 LevelPanWidget::setLevel(float flevel)
Chris@923 68 {
Chris@925 69 int level = AudioLevel::multiplier_to_fader
Chris@925 70 (flevel, maxLevel, AudioLevel::ShortFader);
Chris@925 71 if (level < 0) level = 0;
Chris@925 72 if (level > maxLevel) level = maxLevel;
Chris@925 73 if (level != m_level) {
Chris@925 74 m_level = level;
Chris@925 75 float convertsTo = getLevel();
Chris@925 76 if (fabsf(convertsTo - flevel) > 1e-5) {
Chris@925 77 emitLevelChanged();
Chris@925 78 }
Chris@925 79 update();
Chris@925 80 }
Chris@923 81 }
Chris@923 82
Chris@923 83 void
Chris@923 84 LevelPanWidget::setPan(float pan)
Chris@923 85 {
Chris@923 86 m_pan = int(round(pan * maxPan));
Chris@923 87 if (m_pan < -maxPan) m_pan = -maxPan;
Chris@923 88 if (m_pan > maxPan) m_pan = maxPan;
Chris@923 89 update();
Chris@923 90 }
Chris@923 91
Chris@923 92 void
Chris@923 93 LevelPanWidget::setEditable(bool editable)
Chris@923 94 {
Chris@923 95 m_editable = editable;
Chris@923 96 update();
Chris@923 97 }
Chris@923 98
Chris@923 99 float
Chris@923 100 LevelPanWidget::getLevel() const
Chris@923 101 {
Chris@925 102 return float(AudioLevel::fader_to_multiplier
Chris@925 103 (m_level, maxLevel, AudioLevel::ShortFader));
Chris@923 104 }
Chris@923 105
Chris@923 106 float
Chris@923 107 LevelPanWidget::getPan() const
Chris@923 108 {
Chris@923 109 return float(m_pan) / float(maxPan);
Chris@923 110 }
Chris@923 111
Chris@923 112 void
Chris@923 113 LevelPanWidget::emitLevelChanged()
Chris@923 114 {
Chris@923 115 cerr << "emitting levelChanged(" << getLevel() << ")" << endl;
Chris@923 116 emit levelChanged(getLevel());
Chris@923 117 }
Chris@923 118
Chris@923 119 void
Chris@923 120 LevelPanWidget::emitPanChanged()
Chris@923 121 {
Chris@923 122 cerr << "emitting panChanged(" << getPan() << ")" << endl;
Chris@923 123 emit panChanged(getPan());
Chris@923 124 }
Chris@923 125
Chris@923 126 void
Chris@923 127 LevelPanWidget::mousePressEvent(QMouseEvent *e)
Chris@923 128 {
Chris@923 129 mouseMoveEvent(e);
Chris@923 130 }
Chris@923 131
Chris@923 132 void
Chris@923 133 LevelPanWidget::mouseMoveEvent(QMouseEvent *e)
Chris@923 134 {
Chris@923 135 if (!m_editable) return;
Chris@923 136
Chris@923 137 int level, pan;
Chris@929 138 toCell(rect(), e->pos(), level, pan);
Chris@923 139 if (level == m_level && pan == m_pan) {
Chris@923 140 return;
Chris@923 141 }
Chris@923 142 if (level != m_level) {
Chris@923 143 m_level = level;
Chris@923 144 emitLevelChanged();
Chris@923 145 }
Chris@923 146 if (pan != m_pan) {
Chris@923 147 m_pan = pan;
Chris@923 148 emitPanChanged();
Chris@923 149 }
Chris@923 150 update();
Chris@923 151 }
Chris@923 152
Chris@923 153 void
Chris@923 154 LevelPanWidget::mouseReleaseEvent(QMouseEvent *e)
Chris@923 155 {
Chris@923 156 mouseMoveEvent(e);
Chris@923 157 }
Chris@923 158
Chris@923 159 void
Chris@923 160 LevelPanWidget::wheelEvent(QWheelEvent *e)
Chris@923 161 {
Chris@923 162 if (e->modifiers() & Qt::ControlModifier) {
Chris@923 163 if (e->delta() > 0) {
Chris@923 164 if (m_pan < maxPan) {
Chris@923 165 ++m_pan;
Chris@923 166 emitPanChanged();
Chris@923 167 update();
Chris@923 168 }
Chris@923 169 } else {
Chris@923 170 if (m_pan > -maxPan) {
Chris@923 171 --m_pan;
Chris@923 172 emitPanChanged();
Chris@923 173 update();
Chris@923 174 }
Chris@923 175 }
Chris@923 176 } else {
Chris@923 177 if (e->delta() > 0) {
Chris@923 178 if (m_level < maxLevel) {
Chris@923 179 ++m_level;
Chris@923 180 emitLevelChanged();
Chris@923 181 update();
Chris@923 182 }
Chris@923 183 } else {
Chris@923 184 if (m_level > 0) {
Chris@923 185 --m_level;
Chris@923 186 emitLevelChanged();
Chris@923 187 update();
Chris@923 188 }
Chris@923 189 }
Chris@923 190 }
Chris@923 191 }
Chris@923 192
Chris@923 193 void
Chris@929 194 LevelPanWidget::toCell(QRectF rect, QPointF loc, int &level, int &pan) const
Chris@923 195 {
Chris@929 196 double w = rect.width(), h = rect.height();
Chris@929 197
Chris@923 198 int npan = maxPan * 2 + 1;
Chris@924 199 int nlevel = maxLevel + 1;
Chris@929 200
Chris@924 201 double wcell = w / npan, hcell = h / nlevel;
Chris@929 202
Chris@927 203 level = int((h - loc.y()) / hcell);
Chris@924 204 if (level < 0) level = 0;
Chris@923 205 if (level > maxLevel) level = maxLevel;
Chris@929 206
Chris@923 207 pan = int(loc.x() / wcell) - maxPan;
Chris@923 208 if (pan < -maxPan) pan = -maxPan;
Chris@923 209 if (pan > maxPan) pan = maxPan;
Chris@923 210 }
Chris@923 211
Chris@923 212 QSizeF
Chris@929 213 LevelPanWidget::cellSize(QRectF rect) const
Chris@923 214 {
Chris@929 215 double w = rect.width(), h = rect.height();
Chris@923 216 int npan = maxPan * 2 + 1;
Chris@924 217 int nlevel = maxLevel + 1;
Chris@924 218 double wcell = w / npan, hcell = h / nlevel;
Chris@923 219 return QSizeF(wcell, hcell);
Chris@923 220 }
Chris@923 221
Chris@923 222 QPointF
Chris@929 223 LevelPanWidget::cellCentre(QRectF rect, int level, int pan) const
Chris@923 224 {
Chris@929 225 QSizeF cs = cellSize(rect);
Chris@923 226 return QPointF(cs.width() * (pan + maxPan) + cs.width() / 2.,
Chris@929 227 rect.height() - cs.height() * (level + 1) + cs.height() / 2.);
Chris@923 228 }
Chris@923 229
Chris@923 230 QSizeF
Chris@929 231 LevelPanWidget::cellLightSize(QRectF rect) const
Chris@923 232 {
Chris@923 233 double extent = 3. / 4.;
Chris@929 234 QSizeF cs = cellSize(rect);
Chris@923 235 double m = std::min(cs.width(), cs.height());
Chris@923 236 return QSizeF(m * extent, m * extent);
Chris@923 237 }
Chris@923 238
Chris@923 239 QRectF
Chris@929 240 LevelPanWidget::cellLightRect(QRectF rect, int level, int pan) const
Chris@923 241 {
Chris@929 242 QSizeF cls = cellLightSize(rect);
Chris@929 243 QPointF cc = cellCentre(rect, level, pan);
Chris@923 244 return QRectF(cc.x() - cls.width() / 2.,
Chris@923 245 cc.y() - cls.height() / 2.,
Chris@923 246 cls.width(),
Chris@923 247 cls.height());
Chris@923 248 }
Chris@923 249
Chris@923 250 double
Chris@929 251 LevelPanWidget::thinLineWidth(QRectF rect) const
Chris@923 252 {
Chris@929 253 double tw = ceil(rect.width() / (maxPan * 2. * 10.));
Chris@929 254 double th = ceil(rect.height() / (maxLevel * 10.));
Chris@923 255 return std::min(th, tw);
Chris@923 256 }
Chris@923 257
Chris@923 258 void
Chris@929 259 LevelPanWidget::renderTo(QPaintDevice *dev, QRectF rect, bool asIfEditable) const
Chris@923 260 {
Chris@929 261 QPainter paint(dev);
Chris@923 262 ColourMapper mapper(ColourMapper::Sunset, 0, maxLevel);
Chris@923 263
Chris@923 264 paint.setRenderHint(QPainter::Antialiasing, true);
Chris@923 265
Chris@923 266 QPen pen;
Chris@923 267
Chris@929 268 double thin = thinLineWidth(rect);
Chris@923 269
Chris@923 270 pen.setColor(QColor(127, 127, 127, 127));
Chris@929 271 pen.setWidthF(cellLightSize(rect).width() + thin);
Chris@923 272 pen.setCapStyle(Qt::RoundCap);
Chris@923 273 paint.setPen(pen);
Chris@923 274
Chris@923 275 for (int pan = -maxPan; pan <= maxPan; ++pan) {
Chris@929 276 paint.drawLine(cellCentre(rect, 0, pan), cellCentre(rect, maxLevel, pan));
Chris@924 277 }
Chris@924 278
Chris@924 279 if (isEnabled()) {
Chris@924 280 pen.setColor(Qt::black);
Chris@924 281 } else {
Chris@924 282 pen.setColor(Qt::darkGray);
Chris@923 283 }
Chris@929 284
Chris@929 285 if (!asIfEditable && m_level == 0) {
Chris@929 286 pen.setWidthF(thin * 2);
Chris@929 287 pen.setCapStyle(Qt::RoundCap);
Chris@929 288 paint.setPen(pen);
Chris@930 289 paint.drawLine(cellCentre(rect, 0, -maxPan),
Chris@930 290 cellCentre(rect, maxLevel, maxPan));
Chris@930 291 paint.drawLine(cellCentre(rect, maxLevel, -maxPan),
Chris@930 292 cellCentre(rect, 0, maxPan));
Chris@929 293 return;
Chris@929 294 }
Chris@923 295
Chris@923 296 pen.setWidthF(thin);
Chris@923 297 pen.setCapStyle(Qt::FlatCap);
Chris@923 298 paint.setPen(pen);
Chris@923 299
Chris@924 300 for (int level = 0; level <= m_level; ++level) {
Chris@924 301 if (isEnabled()) {
Chris@924 302 paint.setBrush(mapper.map(level));
Chris@924 303 }
Chris@929 304 QRectF clr = cellLightRect(rect, level, m_pan);
Chris@924 305 if (m_level == 0) {
Chris@924 306 paint.drawLine(clr.topLeft(), clr.bottomRight());
Chris@924 307 paint.drawLine(clr.bottomLeft(), clr.topRight());
Chris@924 308 } else {
Chris@924 309 paint.drawEllipse(clr);
Chris@924 310 }
Chris@923 311 }
Chris@923 312 }
Chris@923 313
Chris@929 314 void
Chris@929 315 LevelPanWidget::paintEvent(QPaintEvent *)
Chris@929 316 {
Chris@929 317 renderTo(this, rect(), m_editable);
Chris@929 318 }
Chris@923 319
Chris@929 320
Chris@929 321