Chris@923: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ Chris@923: Chris@923: /* Chris@923: Sonic Visualiser Chris@923: An audio file viewer and annotation editor. Chris@923: Centre for Digital Music, Queen Mary, University of London. Chris@923: Chris@923: This program is free software; you can redistribute it and/or Chris@923: modify it under the terms of the GNU General Public License as Chris@923: published by the Free Software Foundation; either version 2 of the Chris@923: License, or (at your option) any later version. See the file Chris@923: COPYING included with this distribution for more information. Chris@923: */ Chris@923: Chris@923: #include "LevelPanWidget.h" Chris@923: Chris@923: #include Chris@923: #include Chris@923: #include Chris@923: Chris@923: #include "layer/ColourMapper.h" Chris@923: Chris@923: #include Chris@923: Chris@923: using std::cerr; Chris@923: using std::endl; Chris@923: Chris@923: static const int maxLevel = 5; Chris@923: static const int maxPan = 2; // range is -maxPan to maxPan Chris@923: Chris@923: LevelPanWidget::LevelPanWidget(QWidget *parent) : Chris@923: QWidget(parent), Chris@923: m_level(maxLevel), Chris@923: m_pan(0), Chris@923: m_editable(true) Chris@923: { Chris@923: } Chris@923: Chris@923: LevelPanWidget::~LevelPanWidget() Chris@923: { Chris@923: } Chris@923: Chris@923: void Chris@923: LevelPanWidget::setLevel(float level) Chris@923: { Chris@923: m_level = int(round(level * maxLevel)); Chris@923: if (m_level < 0) m_level = 0; Chris@923: if (m_level > maxLevel) m_level = maxLevel; Chris@923: update(); Chris@923: } Chris@923: Chris@923: void Chris@923: LevelPanWidget::setPan(float pan) Chris@923: { Chris@923: m_pan = int(round(pan * maxPan)); Chris@923: if (m_pan < -maxPan) m_pan = -maxPan; Chris@923: if (m_pan > maxPan) m_pan = maxPan; Chris@923: update(); Chris@923: } Chris@923: Chris@923: void Chris@923: LevelPanWidget::setEditable(bool editable) Chris@923: { Chris@923: m_editable = editable; Chris@923: update(); Chris@923: } Chris@923: Chris@923: float Chris@923: LevelPanWidget::getLevel() const Chris@923: { Chris@923: return float(m_level) / float(maxLevel); Chris@923: } Chris@923: Chris@923: float Chris@923: LevelPanWidget::getPan() const Chris@923: { Chris@923: return float(m_pan) / float(maxPan); Chris@923: } Chris@923: Chris@923: void Chris@923: LevelPanWidget::emitLevelChanged() Chris@923: { Chris@923: cerr << "emitting levelChanged(" << getLevel() << ")" << endl; Chris@923: emit levelChanged(getLevel()); Chris@923: } Chris@923: Chris@923: void Chris@923: LevelPanWidget::emitPanChanged() Chris@923: { Chris@923: cerr << "emitting panChanged(" << getPan() << ")" << endl; Chris@923: emit panChanged(getPan()); Chris@923: } Chris@923: Chris@923: void Chris@923: LevelPanWidget::mousePressEvent(QMouseEvent *e) Chris@923: { Chris@923: mouseMoveEvent(e); Chris@923: } Chris@923: Chris@923: void Chris@923: LevelPanWidget::mouseMoveEvent(QMouseEvent *e) Chris@923: { Chris@923: if (!m_editable) return; Chris@923: Chris@923: int level, pan; Chris@923: toCell(e->pos(), level, pan); Chris@923: if (level == m_level && pan == m_pan) { Chris@923: return; Chris@923: } Chris@923: if (level != m_level) { Chris@923: m_level = level; Chris@923: emitLevelChanged(); Chris@923: } Chris@923: if (pan != m_pan) { Chris@923: m_pan = pan; Chris@923: emitPanChanged(); Chris@923: } Chris@923: update(); Chris@923: } Chris@923: Chris@923: void Chris@923: LevelPanWidget::mouseReleaseEvent(QMouseEvent *e) Chris@923: { Chris@923: mouseMoveEvent(e); Chris@923: } Chris@923: Chris@923: void Chris@923: LevelPanWidget::wheelEvent(QWheelEvent *e) Chris@923: { Chris@923: if (e->modifiers() & Qt::ControlModifier) { Chris@923: if (e->delta() > 0) { Chris@923: if (m_pan < maxPan) { Chris@923: ++m_pan; Chris@923: emitPanChanged(); Chris@923: update(); Chris@923: } Chris@923: } else { Chris@923: if (m_pan > -maxPan) { Chris@923: --m_pan; Chris@923: emitPanChanged(); Chris@923: update(); Chris@923: } Chris@923: } Chris@923: } else { Chris@923: if (e->delta() > 0) { Chris@923: if (m_level < maxLevel) { Chris@923: ++m_level; Chris@923: emitLevelChanged(); Chris@923: update(); Chris@923: } Chris@923: } else { Chris@923: if (m_level > 0) { Chris@923: --m_level; Chris@923: emitLevelChanged(); Chris@923: update(); Chris@923: } Chris@923: } Chris@923: } Chris@923: } Chris@923: Chris@923: void Chris@923: LevelPanWidget::toCell(QPointF loc, int &level, int &pan) const Chris@923: { Chris@923: double w = width(), h = height(); Chris@923: int npan = maxPan * 2 + 1; Chris@923: double wcell = w / npan, hcell = h / maxLevel; Chris@923: level = int((h - loc.y()) / hcell) + 1; Chris@923: if (level < 1) level = 1; Chris@923: if (level > maxLevel) level = maxLevel; Chris@923: pan = int(loc.x() / wcell) - maxPan; Chris@923: if (pan < -maxPan) pan = -maxPan; Chris@923: if (pan > maxPan) pan = maxPan; Chris@923: } Chris@923: Chris@923: QSizeF Chris@923: LevelPanWidget::cellSize() const Chris@923: { Chris@923: double w = width(), h = height(); Chris@923: int npan = maxPan * 2 + 1; Chris@923: double wcell = w / npan, hcell = h / maxLevel; Chris@923: return QSizeF(wcell, hcell); Chris@923: } Chris@923: Chris@923: QPointF Chris@923: LevelPanWidget::cellCentre(int level, int pan) const Chris@923: { Chris@923: QSizeF cs = cellSize(); Chris@923: return QPointF(cs.width() * (pan + maxPan) + cs.width() / 2., Chris@923: height() - cs.height() * level + cs.height() / 2.); Chris@923: } Chris@923: Chris@923: QSizeF Chris@923: LevelPanWidget::cellLightSize() const Chris@923: { Chris@923: double extent = 3. / 4.; Chris@923: QSizeF cs = cellSize(); Chris@923: double m = std::min(cs.width(), cs.height()); Chris@923: return QSizeF(m * extent, m * extent); Chris@923: } Chris@923: Chris@923: QRectF Chris@923: LevelPanWidget::cellLightRect(int level, int pan) const Chris@923: { Chris@923: QSizeF cls = cellLightSize(); Chris@923: QPointF cc = cellCentre(level, pan); Chris@923: return QRectF(cc.x() - cls.width() / 2., Chris@923: cc.y() - cls.height() / 2., Chris@923: cls.width(), Chris@923: cls.height()); Chris@923: } Chris@923: Chris@923: double Chris@923: LevelPanWidget::thinLineWidth() const Chris@923: { Chris@923: double tw = ceil(width() / (maxPan * 2. * 10.)); Chris@923: double th = ceil(height() / (maxLevel * 10.)); Chris@923: return std::min(th, tw); Chris@923: } Chris@923: Chris@923: void Chris@923: LevelPanWidget::paintEvent(QPaintEvent *) Chris@923: { Chris@923: QPainter paint(this); Chris@923: ColourMapper mapper(ColourMapper::Sunset, 0, maxLevel); Chris@923: Chris@923: paint.setRenderHint(QPainter::Antialiasing, true); Chris@923: Chris@923: QPen pen; Chris@923: Chris@923: double thin = thinLineWidth(); Chris@923: Chris@923: pen.setColor(QColor(127, 127, 127, 127)); Chris@923: pen.setWidthF(cellLightSize().width() + thin); Chris@923: pen.setCapStyle(Qt::RoundCap); Chris@923: paint.setPen(pen); Chris@923: Chris@923: for (int pan = -maxPan; pan <= maxPan; ++pan) { Chris@923: paint.drawLine(cellCentre(1, pan), cellCentre(maxLevel, pan)); Chris@923: } Chris@923: Chris@923: pen.setColor(Qt::black); Chris@923: pen.setWidthF(thin); Chris@923: pen.setCapStyle(Qt::FlatCap); Chris@923: paint.setPen(pen); Chris@923: Chris@923: for (int level = 1; level <= m_level; ++level) { Chris@923: // level starts at 1 because we handle mute separately Chris@923: paint.setBrush(mapper.map(level)); Chris@923: paint.drawEllipse(cellLightRect(level, m_pan)); Chris@923: } Chris@923: } Chris@923: Chris@923: