| 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@1176 | 24 #include "WidgetScale.h" | 
| Chris@1176 | 25 | 
| Chris@923 | 26 #include <iostream> | 
| Chris@926 | 27 #include <cmath> | 
| Chris@940 | 28 #include <cassert> | 
| Chris@923 | 29 | 
| Chris@923 | 30 using std::cerr; | 
| Chris@923 | 31 using std::endl; | 
| Chris@923 | 32 | 
| Chris@1301 | 33 /** | 
| Chris@1301 | 34  * Gain and pan scales: | 
| Chris@1301 | 35  * | 
| Chris@1301 | 36  * Gain: we have 5 circles vertically in the display, each of which | 
| Chris@1301 | 37  * has half-circle and full-circle versions, and we also have "no | 
| Chris@1301 | 38  * circles", so there are in total 11 distinct levels, which we refer | 
| Chris@1301 | 39  * to as "notches" and number 0-10. (We use "notch" because "level" is | 
| Chris@1301 | 40  * used by the external API to refer to audio gain.) | 
| Chris@1301 | 41  * | 
| Chris@1301 | 42  * i.e. the levels are represented by these (schematic, rotated to | 
| Chris@1301 | 43  * horizontal) displays: | 
| Chris@1301 | 44  * | 
| Chris@1301 | 45  *  0  X | 
| Chris@1301 | 46  *  1  [ | 
| Chris@1301 | 47  *  2  [] | 
| Chris@1301 | 48  *  3  [][ | 
| Chris@1301 | 49  *  ... | 
| Chris@1301 | 50  *  9  [][][][][ | 
| Chris@1301 | 51  *  10 [][][][][] | 
| Chris@1301 | 52  * | 
| Chris@1301 | 53  * If we have mute enabled, then we map the range 0-10 to gain using | 
| Chris@1301 | 54  * AudioLevel::fader_to_* with the ShortFader type, which treats fader | 
| Chris@1301 | 55  * 0 as muted. If mute is disabled, then we map the range 1-10. | 
| Chris@1301 | 56  * | 
| Chris@1301 | 57  * We can also disable half-circles, which leaves the range unchanged | 
| Chris@1301 | 58  * but limits the notches to even values. | 
| Chris@1301 | 59  * | 
| Chris@1301 | 60  * Pan: we have 5 columns with no finer resolution, so we only have 2 | 
| Chris@1301 | 61  * possible pan values on each side of centre. | 
| Chris@1301 | 62  */ | 
| Chris@1301 | 63 | 
| Chris@923 | 64 static const int maxPan = 2; // range is -maxPan to maxPan | 
| Chris@923 | 65 | 
| Chris@923 | 66 LevelPanWidget::LevelPanWidget(QWidget *parent) : | 
| Chris@923 | 67     QWidget(parent), | 
| Chris@1301 | 68     m_minNotch(0), | 
| Chris@1301 | 69     m_maxNotch(10), | 
| Chris@1301 | 70     m_notch(m_maxNotch), | 
| Chris@923 | 71     m_pan(0), | 
| Chris@1177 | 72     m_monitorLeft(-1), | 
| Chris@1177 | 73     m_monitorRight(-1), | 
| Chris@940 | 74     m_editable(true), | 
| Chris@1249 | 75     m_editing(false), | 
| Chris@1301 | 76     m_includeMute(true), | 
| Chris@1302 | 77     m_includeHalfSteps(true), | 
| Chris@1302 | 78     m_pendingWheelAngle(0) | 
| Chris@923 | 79 { | 
| Chris@1191 | 80     setToolTip(tr("Drag vertically to adjust level, horizontally to adjust pan")); | 
| Chris@1201 | 81     setLevel(1.0); | 
| Chris@1201 | 82     setPan(0.0); | 
| Chris@923 | 83 } | 
| Chris@923 | 84 | 
| Chris@923 | 85 LevelPanWidget::~LevelPanWidget() | 
| Chris@923 | 86 { | 
| Chris@923 | 87 } | 
| Chris@923 | 88 | 
| Chris@1249 | 89 void | 
| Chris@1249 | 90 LevelPanWidget::setToDefault() | 
| Chris@1249 | 91 { | 
| Chris@1249 | 92     setLevel(1.0); | 
| Chris@1249 | 93     setPan(0.0); | 
| Chris@1250 | 94     emitLevelChanged(); | 
| Chris@1250 | 95     emitPanChanged(); | 
| Chris@1249 | 96 } | 
| Chris@1249 | 97 | 
| Chris@929 | 98 QSize | 
| Chris@929 | 99 LevelPanWidget::sizeHint() const | 
| Chris@929 | 100 { | 
| Chris@1176 | 101     return WidgetScale::scaleQSize(QSize(40, 40)); | 
| Chris@929 | 102 } | 
| Chris@929 | 103 | 
| Chris@1301 | 104 int | 
| Chris@1301 | 105 LevelPanWidget::clampNotch(int notch) const | 
| Chris@940 | 106 { | 
| Chris@1301 | 107     if (notch < m_minNotch) notch = m_minNotch; | 
| Chris@1301 | 108     if (notch > m_maxNotch) notch = m_maxNotch; | 
| Chris@1301 | 109     if (!m_includeHalfSteps) { | 
| Chris@1301 | 110         notch = (notch / 2) * 2; | 
| Chris@1301 | 111     } | 
| Chris@1301 | 112     return notch; | 
| Chris@940 | 113 } | 
| Chris@940 | 114 | 
| Chris@1177 | 115 int | 
| Chris@1302 | 116 LevelPanWidget::clampPan(int pan) const | 
| Chris@1302 | 117 { | 
| Chris@1302 | 118     if (pan < -maxPan) pan = -maxPan; | 
| Chris@1302 | 119     if (pan > maxPan) pan = maxPan; | 
| Chris@1302 | 120     return pan; | 
| Chris@1302 | 121 } | 
| Chris@1302 | 122 | 
| Chris@1302 | 123 int | 
| Chris@1301 | 124 LevelPanWidget::audioLevelToNotch(float audioLevel) const | 
| Chris@1177 | 125 { | 
| Chris@1301 | 126     int notch = AudioLevel::multiplier_to_fader | 
| Chris@1301 | 127         (audioLevel, m_maxNotch, AudioLevel::ShortFader); | 
| Chris@1301 | 128     return clampNotch(notch); | 
| Chris@1177 | 129 } | 
| Chris@1177 | 130 | 
| Chris@1177 | 131 float | 
| Chris@1301 | 132 LevelPanWidget::notchToAudioLevel(int notch) const | 
| Chris@1177 | 133 { | 
| Chris@1301 | 134     return float(AudioLevel::fader_to_multiplier | 
| Chris@1301 | 135                  (notch, m_maxNotch, AudioLevel::ShortFader)); | 
| Chris@1177 | 136 } | 
| Chris@1177 | 137 | 
| Chris@923 | 138 void | 
| Chris@1301 | 139 LevelPanWidget::setLevel(float level) | 
| Chris@923 | 140 { | 
| Chris@1301 | 141     int notch = audioLevelToNotch(level); | 
| Chris@1301 | 142     if (notch != m_notch) { | 
| Chris@1301 | 143         m_notch = notch; | 
| Chris@1266 | 144         float convertsTo = getLevel(); | 
| Chris@1301 | 145         if (fabsf(convertsTo - level) > 1e-5) { | 
| Chris@1266 | 146             emitLevelChanged(); | 
| Chris@1266 | 147         } | 
| Chris@1266 | 148         update(); | 
| Chris@925 | 149     } | 
| Chris@1301 | 150     SVCERR << "setLevel: level " << level << " -> notch " << m_notch << " (which converts back to level " << getLevel() << ")" << endl; | 
| Chris@923 | 151 } | 
| Chris@923 | 152 | 
| Chris@940 | 153 float | 
| Chris@940 | 154 LevelPanWidget::getLevel() const | 
| Chris@940 | 155 { | 
| Chris@1301 | 156     return notchToAudioLevel(m_notch); | 
| Chris@1177 | 157 } | 
| Chris@1177 | 158 | 
| Chris@1177 | 159 int | 
| Chris@1301 | 160 LevelPanWidget::audioPanToPan(float audioPan) const | 
| Chris@1177 | 161 { | 
| Chris@1177 | 162     int pan = int(round(audioPan * maxPan)); | 
| Chris@1302 | 163     pan = clampPan(pan); | 
| Chris@1177 | 164     return pan; | 
| Chris@1177 | 165 } | 
| Chris@1177 | 166 | 
| Chris@1177 | 167 float | 
| Chris@1301 | 168 LevelPanWidget::panToAudioPan(int pan) const | 
| Chris@1177 | 169 { | 
| Chris@1177 | 170     return float(pan) / float(maxPan); | 
| Chris@1177 | 171 } | 
| Chris@1177 | 172 | 
| Chris@1177 | 173 void | 
| Chris@1177 | 174 LevelPanWidget::setPan(float fpan) | 
| Chris@1177 | 175 { | 
| Chris@1177 | 176     int pan = audioPanToPan(fpan); | 
| Chris@1177 | 177     if (pan != m_pan) { | 
| Chris@1177 | 178         m_pan = pan; | 
| Chris@1177 | 179         update(); | 
| Chris@940 | 180     } | 
| Chris@940 | 181 } | 
| Chris@940 | 182 | 
| Chris@1177 | 183 float | 
| Chris@1177 | 184 LevelPanWidget::getPan() const | 
| Chris@1177 | 185 { | 
| Chris@1177 | 186     return panToAudioPan(m_pan); | 
| Chris@1177 | 187 } | 
| Chris@1177 | 188 | 
| Chris@923 | 189 void | 
| Chris@1177 | 190 LevelPanWidget::setMonitoringLevels(float left, float right) | 
| Chris@923 | 191 { | 
| Chris@1177 | 192     m_monitorLeft = left; | 
| Chris@1177 | 193     m_monitorRight = right; | 
| Chris@923 | 194     update(); | 
| Chris@923 | 195 } | 
| Chris@923 | 196 | 
| Chris@940 | 197 bool | 
| Chris@940 | 198 LevelPanWidget::isEditable() const | 
| Chris@940 | 199 { | 
| Chris@940 | 200     return m_editable; | 
| Chris@940 | 201 } | 
| Chris@940 | 202 | 
| Chris@940 | 203 bool | 
| Chris@940 | 204 LevelPanWidget::includesMute() const | 
| Chris@940 | 205 { | 
| Chris@940 | 206     return m_includeMute; | 
| Chris@940 | 207 } | 
| Chris@940 | 208 | 
| Chris@923 | 209 void | 
| Chris@923 | 210 LevelPanWidget::setEditable(bool editable) | 
| Chris@923 | 211 { | 
| Chris@923 | 212     m_editable = editable; | 
| Chris@923 | 213     update(); | 
| Chris@923 | 214 } | 
| Chris@923 | 215 | 
| Chris@940 | 216 void | 
| Chris@940 | 217 LevelPanWidget::setIncludeMute(bool include) | 
| Chris@923 | 218 { | 
| Chris@940 | 219     m_includeMute = include; | 
| Chris@1301 | 220     if (m_includeMute) { | 
| Chris@1301 | 221         m_minNotch = 0; | 
| Chris@1301 | 222     } else { | 
| Chris@1301 | 223         m_minNotch = 1; | 
| Chris@1301 | 224     } | 
| Chris@940 | 225     emitLevelChanged(); | 
| Chris@940 | 226     update(); | 
| Chris@923 | 227 } | 
| Chris@923 | 228 | 
| Chris@923 | 229 void | 
| Chris@923 | 230 LevelPanWidget::emitLevelChanged() | 
| Chris@923 | 231 { | 
| Chris@923 | 232     emit levelChanged(getLevel()); | 
| Chris@923 | 233 } | 
| Chris@923 | 234 | 
| Chris@923 | 235 void | 
| Chris@923 | 236 LevelPanWidget::emitPanChanged() | 
| Chris@923 | 237 { | 
| Chris@923 | 238     emit panChanged(getPan()); | 
| Chris@923 | 239 } | 
| Chris@923 | 240 | 
| Chris@923 | 241 void | 
| Chris@923 | 242 LevelPanWidget::mousePressEvent(QMouseEvent *e) | 
| Chris@923 | 243 { | 
| Chris@1249 | 244     if (e->button() == Qt::MidButton || | 
| Chris@1249 | 245         ((e->button() == Qt::LeftButton) && | 
| Chris@1249 | 246          (e->modifiers() & Qt::ControlModifier))) { | 
| Chris@1249 | 247         setToDefault(); | 
| Chris@1249 | 248     } else if (e->button() == Qt::LeftButton) { | 
| Chris@1249 | 249         m_editing = true; | 
| Chris@1249 | 250         mouseMoveEvent(e); | 
| Chris@1249 | 251     } | 
| Chris@1249 | 252 } | 
| Chris@1249 | 253 | 
| Chris@1249 | 254 void | 
| Chris@1249 | 255 LevelPanWidget::mouseReleaseEvent(QMouseEvent *e) | 
| Chris@1249 | 256 { | 
| Chris@923 | 257     mouseMoveEvent(e); | 
| Chris@1249 | 258     m_editing = false; | 
| Chris@923 | 259 } | 
| Chris@923 | 260 | 
| Chris@923 | 261 void | 
| Chris@923 | 262 LevelPanWidget::mouseMoveEvent(QMouseEvent *e) | 
| Chris@923 | 263 { | 
| Chris@923 | 264     if (!m_editable) return; | 
| Chris@1249 | 265     if (!m_editing) return; | 
| Chris@923 | 266 | 
| Chris@1301 | 267     int notch = coordsToNotch(rect(), e->pos()); | 
| Chris@1301 | 268     int pan = coordsToPan(rect(), e->pos()); | 
| Chris@1301 | 269 | 
| Chris@1301 | 270     if (notch == m_notch && pan == m_pan) { | 
| Chris@1266 | 271         return; | 
| Chris@923 | 272     } | 
| Chris@1301 | 273     if (notch != m_notch) { | 
| Chris@1301 | 274         m_notch = notch; | 
| Chris@1266 | 275         emitLevelChanged(); | 
| Chris@923 | 276     } | 
| Chris@923 | 277     if (pan != m_pan) { | 
| Chris@1266 | 278         m_pan = pan; | 
| Chris@1266 | 279         emitPanChanged(); | 
| Chris@923 | 280     } | 
| Chris@923 | 281     update(); | 
| Chris@923 | 282 } | 
| Chris@923 | 283 | 
| Chris@923 | 284 void | 
| Chris@923 | 285 LevelPanWidget::wheelEvent(QWheelEvent *e) | 
| Chris@923 | 286 { | 
| Chris@1302 | 287     e->accept(); | 
| Chris@1302 | 288 | 
| Chris@1302 | 289     int dy = e->angleDelta().y(); | 
| Chris@1302 | 290     if (dy == 0) { | 
| Chris@1302 | 291         return; | 
| Chris@1302 | 292     } | 
| Chris@1302 | 293 | 
| Chris@1302 | 294     if (e->phase() == Qt::ScrollBegin || | 
| Chris@1302 | 295         std::abs(dy) >= 120 || | 
| Chris@1302 | 296         (dy > 0 && m_pendingWheelAngle < 0) || | 
| Chris@1302 | 297         (dy < 0 && m_pendingWheelAngle > 0)) { | 
| Chris@1302 | 298         m_pendingWheelAngle = dy; | 
| Chris@1302 | 299     } else { | 
| Chris@1302 | 300         m_pendingWheelAngle += dy; | 
| Chris@1302 | 301     } | 
| Chris@1302 | 302 | 
| Chris@1302 | 303     if (abs(m_pendingWheelAngle) >= 600) { | 
| Chris@1302 | 304         // discard absurd results | 
| Chris@1302 | 305         m_pendingWheelAngle = 0; | 
| Chris@1302 | 306         return; | 
| Chris@1302 | 307     } | 
| Chris@1302 | 308 | 
| Chris@1302 | 309     while (abs(m_pendingWheelAngle) >= 120) { | 
| Chris@1302 | 310 | 
| Chris@1302 | 311         int sign = (m_pendingWheelAngle < 0 ? -1 : 1); | 
| Chris@1302 | 312 | 
| Chris@1302 | 313         if (e->modifiers() & Qt::ControlModifier) { | 
| Chris@1302 | 314             m_pan += sign; | 
| Chris@1302 | 315             m_pan = clampPan(m_pan); | 
| Chris@1302 | 316             emitPanChanged(); | 
| Chris@1302 | 317             update(); | 
| Chris@1266 | 318         } else { | 
| Chris@1302 | 319             m_notch += sign; | 
| Chris@1302 | 320             m_notch = clampNotch(m_notch); | 
| Chris@1302 | 321             emitLevelChanged(); | 
| Chris@1302 | 322             update(); | 
| Chris@1266 | 323         } | 
| Chris@1302 | 324 | 
| Chris@1302 | 325         m_pendingWheelAngle -= sign * 120; | 
| Chris@923 | 326     } | 
| Chris@923 | 327 } | 
| Chris@923 | 328 | 
| Chris@1301 | 329 int | 
| Chris@1301 | 330 LevelPanWidget::coordsToNotch(QRectF rect, QPointF loc) const | 
| Chris@923 | 331 { | 
| Chris@1301 | 332     double h = rect.height(); | 
| Chris@1301 | 333 | 
| Chris@1301 | 334     int nnotch = m_maxNotch + 1; | 
| Chris@1301 | 335     double cell = h / nnotch; | 
| Chris@1301 | 336 | 
| Chris@1301 | 337     int notch = int((h - (loc.y() - rect.y())) / cell); | 
| Chris@1301 | 338     notch = clampNotch(notch); | 
| Chris@1301 | 339 | 
| Chris@1301 | 340     return notch; | 
| Chris@1301 | 341 } | 
| Chris@1301 | 342 | 
| Chris@1301 | 343 int | 
| Chris@1301 | 344 LevelPanWidget::coordsToPan(QRectF rect, QPointF loc) const | 
| Chris@1301 | 345 { | 
| Chris@1301 | 346     double w = rect.width(); | 
| Chris@929 | 347 | 
| Chris@923 | 348     int npan = maxPan * 2 + 1; | 
| Chris@1301 | 349     double cell = w / npan; | 
| Chris@929 | 350 | 
| Chris@1301 | 351     int pan = int((loc.x() - rect.x()) / cell) - maxPan; | 
| Chris@1302 | 352     pan = clampPan(pan); | 
| Chris@1301 | 353 | 
| Chris@1301 | 354     return pan; | 
| Chris@923 | 355 } | 
| Chris@923 | 356 | 
| Chris@923 | 357 QSizeF | 
| Chris@929 | 358 LevelPanWidget::cellSize(QRectF rect) const | 
| Chris@923 | 359 { | 
| Chris@929 | 360     double w = rect.width(), h = rect.height(); | 
| Chris@1301 | 361     int ncol = maxPan * 2 + 1; | 
| Chris@1301 | 362     int nrow = m_maxNotch/2; | 
| Chris@1301 | 363     double wcell = w / ncol, hcell = h / nrow; | 
| Chris@923 | 364     return QSizeF(wcell, hcell); | 
| Chris@923 | 365 } | 
| Chris@923 | 366 | 
| Chris@923 | 367 QPointF | 
| Chris@1301 | 368 LevelPanWidget::cellCentre(QRectF rect, int row, int col) const | 
| Chris@923 | 369 { | 
| Chris@929 | 370     QSizeF cs = cellSize(rect); | 
| Chris@1301 | 371     return QPointF(rect.x() + | 
| Chris@1301 | 372                    cs.width() * (col + maxPan) + cs.width() / 2., | 
| Chris@1301 | 373                    rect.y() + rect.height() - | 
| Chris@1301 | 374                    cs.height() * (row + 1) + cs.height() / 2.); | 
| Chris@923 | 375 } | 
| Chris@923 | 376 | 
| Chris@923 | 377 QSizeF | 
| Chris@929 | 378 LevelPanWidget::cellLightSize(QRectF rect) const | 
| Chris@923 | 379 { | 
| Chris@923 | 380     double extent = 3. / 4.; | 
| Chris@929 | 381     QSizeF cs = cellSize(rect); | 
| Chris@923 | 382     double m = std::min(cs.width(), cs.height()); | 
| Chris@923 | 383     return QSizeF(m * extent, m * extent); | 
| Chris@923 | 384 } | 
| Chris@923 | 385 | 
| Chris@923 | 386 QRectF | 
| Chris@1301 | 387 LevelPanWidget::cellLightRect(QRectF rect, int row, int col) const | 
| Chris@923 | 388 { | 
| Chris@929 | 389     QSizeF cls = cellLightSize(rect); | 
| Chris@1301 | 390     QPointF cc = cellCentre(rect, row, col); | 
| Chris@923 | 391     return QRectF(cc.x() - cls.width() / 2., | 
| Chris@1266 | 392                   cc.y() - cls.height() / 2., | 
| Chris@1266 | 393                   cls.width(), | 
| Chris@1266 | 394                   cls.height()); | 
| Chris@923 | 395 } | 
| Chris@923 | 396 | 
| Chris@923 | 397 double | 
| Chris@929 | 398 LevelPanWidget::thinLineWidth(QRectF rect) const | 
| Chris@923 | 399 { | 
| Chris@929 | 400     double tw = ceil(rect.width() / (maxPan * 2. * 10.)); | 
| Chris@1301 | 401     double th = ceil(rect.height() / (m_maxNotch/2 * 10.)); | 
| Chris@923 | 402     return std::min(th, tw); | 
| Chris@923 | 403 } | 
| Chris@923 | 404 | 
| Chris@1301 | 405 QRectF | 
| Chris@1301 | 406 LevelPanWidget::cellOutlineRect(QRectF rect, int row, int col) const | 
| Chris@941 | 407 { | 
| Chris@1301 | 408     QRectF clr = cellLightRect(rect, row, col); | 
| Chris@1301 | 409     double adj = thinLineWidth(rect)/2; | 
| Chris@1301 | 410     return clr.adjusted(-adj, -adj, adj, adj); | 
| Chris@1301 | 411 } | 
| Chris@1301 | 412 | 
| Chris@1301 | 413 QColor | 
| Chris@1301 | 414 LevelPanWidget::notchToColour(int notch) const | 
| Chris@1301 | 415 { | 
| Chris@1301 | 416     if (notch < 3) return Qt::black; | 
| Chris@1301 | 417     if (notch < 5) return QColor(80, 0, 0); | 
| Chris@1301 | 418     if (notch < 7) return QColor(160, 0, 0); | 
| Chris@1301 | 419     if (notch < 9) return QColor(255, 0, 0); | 
| Chris@1301 | 420     return QColor(255, 255, 0); | 
| Chris@941 | 421 } | 
| Chris@941 | 422 | 
| Chris@923 | 423 void | 
| Chris@929 | 424 LevelPanWidget::renderTo(QPaintDevice *dev, QRectF rect, bool asIfEditable) const | 
| Chris@923 | 425 { | 
| Chris@929 | 426     QPainter paint(dev); | 
| Chris@923 | 427 | 
| Chris@923 | 428     paint.setRenderHint(QPainter::Antialiasing, true); | 
| Chris@923 | 429 | 
| Chris@923 | 430     QPen pen; | 
| Chris@923 | 431 | 
| Chris@929 | 432     double thin = thinLineWidth(rect); | 
| Chris@938 | 433 | 
| Chris@1301 | 434     QColor columnBackground = QColor(180, 180, 180); | 
| Chris@1301 | 435     pen.setColor(columnBackground); | 
| Chris@929 | 436     pen.setWidthF(cellLightSize(rect).width() + thin); | 
| Chris@923 | 437     pen.setCapStyle(Qt::RoundCap); | 
| Chris@923 | 438     paint.setPen(pen); | 
| Chris@1177 | 439     paint.setBrush(Qt::NoBrush); | 
| Chris@923 | 440 | 
| Chris@923 | 441     for (int pan = -maxPan; pan <= maxPan; ++pan) { | 
| Chris@1301 | 442         paint.drawLine(cellCentre(rect, 0, pan), | 
| Chris@1301 | 443                        cellCentre(rect, m_maxNotch/2 - 1, pan)); | 
| Chris@924 | 444     } | 
| Chris@924 | 445 | 
| Chris@1301 | 446     bool monitoring = (m_monitorLeft > 0.f || m_monitorRight > 0.f); | 
| Chris@1301 | 447 | 
| Chris@1301 | 448     if (isEnabled()) { | 
| Chris@1301 | 449         pen.setColor(Qt::black); | 
| Chris@1301 | 450     } else { | 
| Chris@1301 | 451         pen.setColor(Qt::darkGray); | 
| Chris@1301 | 452     } | 
| Chris@1301 | 453 | 
| Chris@1301 | 454     if (!asIfEditable && m_includeMute && m_notch == 0) { | 
| Chris@1301 | 455         // The X for mute takes up the whole display when we're not | 
| Chris@1301 | 456         // being rendered in editable style | 
| Chris@1301 | 457         pen.setWidthF(thin * 2); | 
| Chris@1301 | 458         pen.setCapStyle(Qt::RoundCap); | 
| Chris@1301 | 459         paint.setPen(pen); | 
| Chris@1301 | 460         paint.drawLine(cellCentre(rect, 0, -maxPan), | 
| Chris@1301 | 461                        cellCentre(rect, m_maxNotch/2 - 1, maxPan)); | 
| Chris@1301 | 462         paint.drawLine(cellCentre(rect, m_maxNotch/2 - 1, -maxPan), | 
| Chris@1301 | 463                        cellCentre(rect, 0, maxPan)); | 
| Chris@1301 | 464     } else { | 
| Chris@1301 | 465         // the normal case | 
| Chris@1301 | 466 | 
| Chris@1301 | 467         // pen a bit less thin than in theory, so that we can erase | 
| Chris@1301 | 468         // semi-circles later without leaving a faint edge | 
| Chris@1301 | 469         pen.setWidthF(thin * 0.8); | 
| Chris@1301 | 470         pen.setCapStyle(Qt::FlatCap); | 
| Chris@1301 | 471         paint.setPen(pen); | 
| Chris@1301 | 472 | 
| Chris@1301 | 473         if (m_includeMute && m_notch == 0) { | 
| Chris@1301 | 474             QRectF clr = cellLightRect(rect, 0, m_pan); | 
| Chris@1301 | 475             paint.drawLine(clr.topLeft(), clr.bottomRight()); | 
| Chris@1301 | 476             paint.drawLine(clr.bottomLeft(), clr.topRight()); | 
| Chris@1301 | 477         } else { | 
| Chris@1301 | 478             for (int notch = 1; notch <= m_notch; notch += 2) { | 
| Chris@1301 | 479                 if (isEnabled() && !monitoring) { | 
| Chris@1301 | 480                     paint.setBrush(notchToColour(notch)); | 
| Chris@1301 | 481                 } | 
| Chris@1301 | 482                 QRectF clr = cellLightRect(rect, notch/2, m_pan); | 
| Chris@1301 | 483                 paint.drawEllipse(clr); | 
| Chris@1301 | 484             } | 
| Chris@1301 | 485             if (m_notch % 2 != 0) { | 
| Chris@1301 | 486                 QRectF clr = cellOutlineRect(rect, (m_notch-1)/2, m_pan); | 
| Chris@1301 | 487                 paint.save(); | 
| Chris@1301 | 488                 paint.setPen(Qt::NoPen); | 
| Chris@1301 | 489                 paint.setBrush(columnBackground); | 
| Chris@1301 | 490                 paint.drawPie(clr, 0, 180 * 16); | 
| Chris@1301 | 491                 paint.restore(); | 
| Chris@1301 | 492             } | 
| Chris@1301 | 493         } | 
| Chris@1301 | 494     } | 
| Chris@1301 | 495 | 
| Chris@1301 | 496     if (monitoring) { | 
| Chris@1177 | 497         paint.setPen(Qt::NoPen); | 
| Chris@1177 | 498         for (int pan = -maxPan; pan <= maxPan; ++pan) { | 
| Chris@1177 | 499             float audioPan = panToAudioPan(pan); | 
| Chris@1177 | 500             float audioLevel; | 
| Chris@1177 | 501             if (audioPan < 0.f) { | 
| Chris@1177 | 502                 audioLevel = m_monitorLeft + m_monitorRight * (1.f + audioPan); | 
| Chris@1177 | 503             } else { | 
| Chris@1177 | 504                 audioLevel = m_monitorRight + m_monitorLeft * (1.f - audioPan); | 
| Chris@1177 | 505             } | 
| Chris@1301 | 506             int notchHere = audioLevelToNotch(audioLevel); | 
| Chris@1301 | 507             for (int notch = 1; notch <= notchHere; notch += 2) { | 
| Chris@1301 | 508                 paint.setBrush(notchToColour(notch)); | 
| Chris@1301 | 509                 QRectF clr = cellLightRect(rect, (notch-1)/2, pan); | 
| Chris@1301 | 510                 double adj = thinLineWidth(rect)/2; | 
| Chris@1301 | 511                 clr = clr.adjusted(adj, adj, -adj, -adj); | 
| Chris@1301 | 512                 if (notch + 2 > notchHere && notchHere % 2 != 0) { | 
| Chris@1301 | 513                     paint.drawPie(clr, 180 * 16, 180 * 16); | 
| Chris@1301 | 514                 } else { | 
| Chris@1301 | 515                     paint.drawEllipse(clr); | 
| Chris@1301 | 516                 } | 
| Chris@1177 | 517             } | 
| Chris@1177 | 518         } | 
| Chris@1177 | 519         paint.setPen(pen); | 
| Chris@1177 | 520         paint.setBrush(Qt::NoBrush); | 
| Chris@1177 | 521     } | 
| Chris@923 | 522 } | 
| Chris@923 | 523 | 
| Chris@929 | 524 void | 
| Chris@929 | 525 LevelPanWidget::paintEvent(QPaintEvent *) | 
| Chris@929 | 526 { | 
| Chris@929 | 527     renderTo(this, rect(), m_editable); | 
| Chris@929 | 528 } | 
| Chris@923 | 529 | 
| Chris@1180 | 530 void | 
| Chris@1180 | 531 LevelPanWidget::enterEvent(QEvent *e) | 
| Chris@1180 | 532 { | 
| Chris@1180 | 533     QWidget::enterEvent(e); | 
| Chris@1180 | 534     emit mouseEntered(); | 
| Chris@1180 | 535 } | 
| Chris@929 | 536 | 
| Chris@1180 | 537 void | 
| Chris@1180 | 538 LevelPanWidget::leaveEvent(QEvent *e) | 
| Chris@1180 | 539 { | 
| Chris@1180 | 540     QWidget::enterEvent(e); | 
| Chris@1180 | 541     emit mouseLeft(); | 
| Chris@1180 | 542 } | 
| Chris@929 | 543 |