Chris@173: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ Chris@173: Chris@173: /* Chris@173: Sonic Visualiser Chris@173: An audio file viewer and annotation editor. Chris@173: Centre for Digital Music, Queen Mary, University of London. Chris@182: This file copyright 2006 Chris Cannam and QMUL. Chris@173: Chris@173: This program is free software; you can redistribute it and/or Chris@173: modify it under the terms of the GNU General Public License as Chris@173: published by the Free Software Foundation; either version 2 of the Chris@173: License, or (at your option) any later version. See the file Chris@173: COPYING included with this distribution for more information. Chris@173: */ Chris@173: Chris@173: #include "Overview.h" Chris@173: #include "layer/Layer.h" Chris@173: #include "data/model/Model.h" Chris@173: #include "base/ZoomConstraint.h" Chris@173: Chris@173: #include Chris@173: #include Chris@1611: #include Chris@173: #include Chris@173: Chris@643: //#define DEBUG_OVERVIEW 1 Chris@642: Chris@682: Chris@173: Overview::Overview(QWidget *w) : Chris@173: View(w, false), Chris@854: m_clickedInRange(false), Chris@854: m_dragCentreFrame(0) Chris@173: { Chris@173: setObjectName(tr("Overview")); Chris@173: m_followPan = false; Chris@173: m_followZoom = false; Chris@211: setPlaybackFollow(PlaybackIgnore); Chris@1554: m_modelTestTimer.start(); Chris@965: Chris@965: bool light = hasLightBackground(); Chris@965: if (light) m_boxColour = Qt::darkGray; Chris@965: else m_boxColour = Qt::lightGray; Chris@173: } Chris@173: Chris@173: void Chris@1481: Overview::modelChangedWithin(ModelId modelId, sv_frame_t startFrame, sv_frame_t endFrame) Chris@173: { Chris@1325: using namespace std::rel_ops; Chris@1325: Chris@253: bool zoomChanged = false; Chris@253: Chris@908: sv_frame_t frameCount = getModelsEndFrame() - getModelsStartFrame(); Chris@1183: ZoomLevel zoomLevel { ZoomLevel::FramesPerPixel, int(frameCount / width()) }; Chris@1183: if (zoomLevel.level < 1) zoomLevel.level = 1; Chris@1325: zoomLevel = getZoomConstraintLevel(zoomLevel, ZoomConstraint::RoundUp); Chris@253: if (zoomLevel != m_zoomLevel) { Chris@253: zoomChanged = true; Chris@253: } Chris@253: Chris@253: if (!zoomChanged) { Chris@1554: if (m_modelTestTimer.elapsed() < 1000) { Chris@835: for (LayerList::const_iterator i = m_layerStack.begin(); Chris@835: i != m_layerStack.end(); ++i) { Chris@1475: auto model = ModelById::get((*i)->getModel()); Chris@1475: if (model && (!model->isOK() || !model->isReady())) { Chris@274: return; Chris@274: } Chris@253: } Chris@274: } else { Chris@1554: m_modelTestTimer.restart(); Chris@253: } Chris@253: } Chris@253: Chris@1481: View::modelChangedWithin(modelId, startFrame, endFrame); Chris@173: } Chris@173: Chris@173: void Chris@173: Overview::modelReplaced() Chris@173: { Chris@339: m_playPointerFrame = getAlignedPlaybackFrame(); Chris@173: View::modelReplaced(); Chris@173: } Chris@173: Chris@173: void Chris@211: Overview::registerView(View *view) Chris@173: { Chris@211: m_views.insert(view); Chris@173: update(); Chris@173: } Chris@173: Chris@173: void Chris@211: Overview::unregisterView(View *view) Chris@173: { Chris@211: m_views.erase(view); Chris@173: update(); Chris@173: } Chris@173: Chris@173: void Chris@908: Overview::globalCentreFrameChanged(sv_frame_t Chris@806: #ifdef DEBUG_OVERVIEW Chris@806: f Chris@806: #endif Chris@806: ) Chris@173: { Chris@642: #ifdef DEBUG_OVERVIEW Chris@682: cerr << "Overview::globalCentreFrameChanged: " << f << endl; Chris@642: #endif Chris@211: update(); Chris@211: } Chris@173: Chris@211: void Chris@908: Overview::viewCentreFrameChanged(View *v, sv_frame_t Chris@806: #ifdef DEBUG_OVERVIEW Chris@806: f Chris@806: #endif Chris@806: ) Chris@211: { Chris@642: #ifdef DEBUG_OVERVIEW Chris@682: cerr << "Overview[" << this << "]::viewCentreFrameChanged(" << v << "): " << f << endl; Chris@642: #endif Chris@211: if (m_views.find(v) != m_views.end()) { Chris@1266: update(); Chris@173: } Chris@211: } Chris@173: Chris@173: void Chris@1326: Overview::viewZoomLevelChanged(View *v, ZoomLevel, bool) Chris@173: { Chris@222: if (v == this) return; Chris@211: if (m_views.find(v) != m_views.end()) { Chris@1266: update(); Chris@173: } Chris@173: } Chris@173: Chris@173: void Chris@908: Overview::viewManagerPlaybackFrameChanged(sv_frame_t f) Chris@173: { Chris@642: #ifdef DEBUG_OVERVIEW Chris@682: cerr << "Overview[" << this << "]::viewManagerPlaybackFrameChanged(" << f << "): " << f << endl; Chris@642: #endif Chris@642: Chris@173: bool changed = false; Chris@173: Chris@339: f = getAlignedPlaybackFrame(); Chris@339: Chris@173: if (getXForFrame(m_playPointerFrame) != getXForFrame(f)) changed = true; Chris@173: m_playPointerFrame = f; Chris@173: Chris@173: if (changed) update(); Chris@173: } Chris@173: Chris@871: QColor Chris@871: Overview::getFillWithin() const Chris@871: { Chris@871: return Qt::transparent; Chris@871: } Chris@871: Chris@871: QColor Chris@871: Overview::getFillWithout() const Chris@871: { Chris@871: QColor c = palette().window().color(); Chris@871: c.setAlpha(100); Chris@871: return c; Chris@871: } Chris@871: Chris@173: void Chris@965: Overview::setBoxColour(QColor c) Chris@965: { Chris@965: m_boxColour = c; Chris@965: } Chris@965: Chris@965: void Chris@173: Overview::paintEvent(QPaintEvent *e) Chris@173: { Chris@1325: using namespace std::rel_ops; Chris@1325: Chris@173: // Recalculate zoom in case the size of the widget has changed. Chris@173: Chris@642: #ifdef DEBUG_OVERVIEW Chris@682: cerr << "Overview::paintEvent: width is " << width() << ", centre frame " << m_centreFrame << endl; Chris@642: #endif Chris@214: Chris@908: sv_frame_t startFrame = getModelsStartFrame(); Chris@908: sv_frame_t frameCount = getModelsEndFrame() - getModelsStartFrame(); Chris@1325: ZoomLevel zoomLevel { ZoomLevel::FramesPerPixel, int(frameCount / width()) }; Chris@1325: if (zoomLevel.level < 1) zoomLevel.level = 1; Chris@1325: zoomLevel = getZoomConstraintLevel(zoomLevel, ZoomConstraint::RoundUp); Chris@173: if (zoomLevel != m_zoomLevel) { Chris@1266: m_zoomLevel = zoomLevel; Chris@1266: emit zoomLevelChanged(m_zoomLevel, m_followZoom); Chris@173: } Chris@253: Chris@1326: sv_frame_t centreFrame = startFrame + Chris@1326: sv_frame_t(round(m_zoomLevel.pixelsToFrames(width()/2))); Chris@1326: Chris@173: if (centreFrame > (startFrame + getModelsEndFrame())/2) { Chris@1266: centreFrame = (startFrame + getModelsEndFrame())/2; Chris@173: } Chris@173: if (centreFrame != m_centreFrame) { Chris@642: #ifdef DEBUG_OVERVIEW Chris@682: cerr << "Overview::paintEvent: Centre frame changed from " Chris@642: << m_centreFrame << " to " << centreFrame << " and thus start frame from " << getStartFrame(); Chris@642: #endif Chris@1266: m_centreFrame = centreFrame; Chris@642: #ifdef DEBUG_OVERVIEW Chris@682: cerr << " to " << getStartFrame() << endl; Chris@642: #endif Chris@1266: emit centreFrameChanged(m_centreFrame, false, PlaybackIgnore); Chris@173: } Chris@173: Chris@173: View::paintEvent(e); Chris@173: Chris@173: QPainter paint; Chris@173: paint.begin(this); Chris@871: paint.setClipRegion(e->region()); Chris@871: paint.setRenderHints(QPainter::Antialiasing); Chris@871: Chris@871: // We paint a rounded rect for each distinct set of view extents, Chris@871: // and we colour in the inside and outside of the rect that Chris@871: // corresponds to the current view. (One small caveat -- we don't Chris@871: // know which rect that is yet. We'll have to figure it out Chris@871: // somehow...) Chris@173: Chris@871: std::set > extents; Chris@871: std::vector rects; Chris@871: QRect primary; Chris@173: Chris@173: int y = 0; Chris@173: Chris@211: for (ViewSet::iterator i = m_views.begin(); i != m_views.end(); ++i) { Chris@1266: if (!*i) continue; Chris@173: Chris@1266: View *w = (View *)*i; Chris@173: Chris@1266: sv_frame_t f0 = w->getFrameForX(0); Chris@1266: sv_frame_t f1 = w->getFrameForX(w->width()); Chris@173: Chris@339: if (f0 >= 0) { Chris@908: sv_frame_t rf0 = w->alignToReference(f0); Chris@339: f0 = alignFromReference(rf0); Chris@339: } Chris@339: if (f1 >= 0) { Chris@908: sv_frame_t rf1 = w->alignToReference(f1); Chris@339: f1 = alignFromReference(rf1); Chris@339: } Chris@339: Chris@1266: int x0 = getXForFrame(f0); Chris@1266: int x1 = getXForFrame(f1); Chris@173: Chris@1266: if (x1 <= x0) x1 = x0 + 1; Chris@871: Chris@871: std::pair extent(x0, x1); Chris@871: Chris@871: if (extents.find(extent) == extents.end()) { Chris@871: Chris@1266: y += height() / 10 + 1; Chris@871: extents.insert(extent); Chris@173: Chris@871: QRect vr(x0, y, x1 - x0, height() - 2 * y); Chris@871: rects.push_back(vr); Chris@871: primary = vr; //!!! for now Chris@871: } Chris@871: } Chris@871: Chris@871: QPainterPath without; matthiasm@935: without.addRoundedRect(primary, 4, 4); Chris@871: without.addRect(rect()); Chris@871: paint.setPen(Qt::NoPen); Chris@871: paint.setBrush(getFillWithout()); Chris@871: paint.drawPath(without); Chris@871: Chris@871: paint.setBrush(getFillWithin()); matthiasm@935: paint.drawRoundedRect(primary, 4, 4); Chris@871: Chris@871: foreach (QRect vr, rects) { Chris@871: paint.setBrush(Qt::NoBrush); Chris@965: paint.setPen(QPen(m_boxColour, 2)); matthiasm@935: paint.drawRoundedRect(vr, 4, 4); Chris@173: } Chris@173: Chris@173: paint.end(); Chris@173: } Chris@173: Chris@173: void Chris@173: Overview::mousePressEvent(QMouseEvent *e) Chris@173: { Chris@173: m_clickPos = e->pos(); Chris@908: sv_frame_t clickFrame = getFrameForX(m_clickPos.x()); Chris@339: if (clickFrame > 0) m_dragCentreFrame = clickFrame; Chris@339: else m_dragCentreFrame = 0; Chris@339: m_clickedInRange = true; Chris@339: Chris@211: for (ViewSet::iterator i = m_views.begin(); i != m_views.end(); ++i) { Chris@1266: if (*i && (*i)->getAligningModel() == getAligningModel()) { Chris@339: m_dragCentreFrame = (*i)->getCentreFrame(); Chris@339: break; Chris@339: } Chris@173: } Chris@173: } Chris@173: Chris@173: void Chris@173: Overview::mouseReleaseEvent(QMouseEvent *e) Chris@173: { Chris@173: if (m_clickedInRange) { Chris@1266: mouseMoveEvent(e); Chris@173: } Chris@173: m_clickedInRange = false; Chris@173: } Chris@173: Chris@173: void Chris@173: Overview::mouseMoveEvent(QMouseEvent *e) Chris@173: { Chris@173: if (!m_clickedInRange) return; Chris@173: Chris@806: int xoff = int(e->x()) - int(m_clickPos.x()); Chris@1326: sv_frame_t frameOff = sv_frame_t(round(m_zoomLevel.pixelsToFrames(xoff))); Chris@173: Chris@908: sv_frame_t newCentreFrame = m_dragCentreFrame; Chris@173: if (frameOff > 0) { Chris@1266: newCentreFrame += frameOff; Chris@908: } else if (newCentreFrame >= -frameOff) { Chris@1266: newCentreFrame += frameOff; Chris@173: } else { Chris@1266: newCentreFrame = 0; Chris@173: } Chris@173: Chris@173: if (newCentreFrame >= getModelsEndFrame()) { Chris@1266: newCentreFrame = getModelsEndFrame(); Chris@1266: if (newCentreFrame > 0) --newCentreFrame; Chris@173: } Chris@173: Chris@1328: sv_frame_t pixel = sv_frame_t(round(m_zoomLevel.pixelsToFrames(1))); Chris@1328: Chris@173: if (std::max(m_centreFrame, newCentreFrame) - Chris@1326: std::min(m_centreFrame, newCentreFrame) > Chris@1328: pixel) { Chris@908: sv_frame_t rf = alignToReference(newCentreFrame); Chris@643: #ifdef DEBUG_OVERVIEW Chris@682: cerr << "Overview::mouseMoveEvent: x " << e->x() << " and click x " << m_clickPos.x() << " -> frame " << newCentreFrame << " -> rf " << rf << endl; Chris@643: #endif Chris@817: if (m_followPlay == PlaybackScrollContinuous || Chris@817: m_followPlay == PlaybackScrollPageWithCentre) { Chris@817: emit centreFrameChanged(rf, true, PlaybackScrollContinuous); Chris@817: } else { Chris@817: emit centreFrameChanged(rf, true, PlaybackIgnore); Chris@817: } Chris@173: } Chris@173: } Chris@173: Chris@189: void Chris@189: Overview::mouseDoubleClickEvent(QMouseEvent *e) Chris@189: { Chris@908: sv_frame_t frame = getFrameForX(e->x()); Chris@908: sv_frame_t rf = 0; Chris@339: if (frame > 0) rf = alignToReference(frame); Chris@643: #ifdef DEBUG_OVERVIEW Chris@682: cerr << "Overview::mouseDoubleClickEvent: frame " << frame << " -> rf " << rf << endl; Chris@643: #endif Chris@643: m_clickedInRange = false; // we're not starting a drag with the second click Chris@339: emit centreFrameChanged(rf, true, PlaybackScrollContinuous); Chris@189: } Chris@173: Chris@189: void Chris@189: Overview::enterEvent(QEvent *) Chris@189: { Chris@189: emit contextHelpChanged(tr("Click and drag to navigate; double-click to jump")); Chris@189: } Chris@189: Chris@189: void Chris@189: Overview::leaveEvent(QEvent *) Chris@189: { Chris@189: emit contextHelpChanged(""); Chris@189: } Chris@189: Chris@189: