annotate view/Overview.cpp @ 1523:1ccb64bfb22b

Make the waveform layer do the expected thing when MergeChannels mode (i.e. butterfly mode) is enabled even if there is only 1 channel. This combination isn't actually available in the UI for SV, but it's useful for Sonic Lineup.
author Chris Cannam
date Wed, 25 Sep 2019 13:42:17 +0100
parents e540aa5d89cd
children a0b2f3b4dd2f
rev   line source
Chris@173 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@173 2
Chris@173 3 /*
Chris@173 4 Sonic Visualiser
Chris@173 5 An audio file viewer and annotation editor.
Chris@173 6 Centre for Digital Music, Queen Mary, University of London.
Chris@182 7 This file copyright 2006 Chris Cannam and QMUL.
Chris@173 8
Chris@173 9 This program is free software; you can redistribute it and/or
Chris@173 10 modify it under the terms of the GNU General Public License as
Chris@173 11 published by the Free Software Foundation; either version 2 of the
Chris@173 12 License, or (at your option) any later version. See the file
Chris@173 13 COPYING included with this distribution for more information.
Chris@173 14 */
Chris@173 15
Chris@173 16 #include "Overview.h"
Chris@173 17 #include "layer/Layer.h"
Chris@173 18 #include "data/model/Model.h"
Chris@173 19 #include "base/ZoomConstraint.h"
Chris@173 20
Chris@173 21 #include <QPaintEvent>
Chris@173 22 #include <QPainter>
Chris@173 23 #include <iostream>
Chris@173 24
Chris@643 25 //#define DEBUG_OVERVIEW 1
Chris@642 26
Chris@682 27
Chris@173 28 Overview::Overview(QWidget *w) :
Chris@173 29 View(w, false),
Chris@854 30 m_clickedInRange(false),
Chris@854 31 m_dragCentreFrame(0)
Chris@173 32 {
Chris@173 33 setObjectName(tr("Overview"));
Chris@173 34 m_followPan = false;
Chris@173 35 m_followZoom = false;
Chris@211 36 setPlaybackFollow(PlaybackIgnore);
Chris@274 37 m_modelTestTime.start();
Chris@965 38
Chris@965 39 bool light = hasLightBackground();
Chris@965 40 if (light) m_boxColour = Qt::darkGray;
Chris@965 41 else m_boxColour = Qt::lightGray;
Chris@173 42 }
Chris@173 43
Chris@173 44 void
Chris@1481 45 Overview::modelChangedWithin(ModelId modelId, sv_frame_t startFrame, sv_frame_t endFrame)
Chris@173 46 {
Chris@1325 47 using namespace std::rel_ops;
Chris@1325 48
Chris@253 49 bool zoomChanged = false;
Chris@253 50
Chris@908 51 sv_frame_t frameCount = getModelsEndFrame() - getModelsStartFrame();
Chris@1183 52 ZoomLevel zoomLevel { ZoomLevel::FramesPerPixel, int(frameCount / width()) };
Chris@1183 53 if (zoomLevel.level < 1) zoomLevel.level = 1;
Chris@1325 54 zoomLevel = getZoomConstraintLevel(zoomLevel, ZoomConstraint::RoundUp);
Chris@253 55 if (zoomLevel != m_zoomLevel) {
Chris@253 56 zoomChanged = true;
Chris@253 57 }
Chris@253 58
Chris@253 59 if (!zoomChanged) {
Chris@274 60 if (m_modelTestTime.elapsed() < 1000) {
Chris@835 61 for (LayerList::const_iterator i = m_layerStack.begin();
Chris@835 62 i != m_layerStack.end(); ++i) {
Chris@1475 63 auto model = ModelById::get((*i)->getModel());
Chris@1475 64 if (model && (!model->isOK() || !model->isReady())) {
Chris@274 65 return;
Chris@274 66 }
Chris@253 67 }
Chris@274 68 } else {
Chris@274 69 m_modelTestTime.restart();
Chris@253 70 }
Chris@253 71 }
Chris@253 72
Chris@1481 73 View::modelChangedWithin(modelId, startFrame, endFrame);
Chris@173 74 }
Chris@173 75
Chris@173 76 void
Chris@173 77 Overview::modelReplaced()
Chris@173 78 {
Chris@339 79 m_playPointerFrame = getAlignedPlaybackFrame();
Chris@173 80 View::modelReplaced();
Chris@173 81 }
Chris@173 82
Chris@173 83 void
Chris@211 84 Overview::registerView(View *view)
Chris@173 85 {
Chris@211 86 m_views.insert(view);
Chris@173 87 update();
Chris@173 88 }
Chris@173 89
Chris@173 90 void
Chris@211 91 Overview::unregisterView(View *view)
Chris@173 92 {
Chris@211 93 m_views.erase(view);
Chris@173 94 update();
Chris@173 95 }
Chris@173 96
Chris@173 97 void
Chris@908 98 Overview::globalCentreFrameChanged(sv_frame_t
Chris@806 99 #ifdef DEBUG_OVERVIEW
Chris@806 100 f
Chris@806 101 #endif
Chris@806 102 )
Chris@173 103 {
Chris@642 104 #ifdef DEBUG_OVERVIEW
Chris@682 105 cerr << "Overview::globalCentreFrameChanged: " << f << endl;
Chris@642 106 #endif
Chris@211 107 update();
Chris@211 108 }
Chris@173 109
Chris@211 110 void
Chris@908 111 Overview::viewCentreFrameChanged(View *v, sv_frame_t
Chris@806 112 #ifdef DEBUG_OVERVIEW
Chris@806 113 f
Chris@806 114 #endif
Chris@806 115 )
Chris@211 116 {
Chris@642 117 #ifdef DEBUG_OVERVIEW
Chris@682 118 cerr << "Overview[" << this << "]::viewCentreFrameChanged(" << v << "): " << f << endl;
Chris@642 119 #endif
Chris@211 120 if (m_views.find(v) != m_views.end()) {
Chris@1266 121 update();
Chris@173 122 }
Chris@211 123 }
Chris@173 124
Chris@173 125 void
Chris@1326 126 Overview::viewZoomLevelChanged(View *v, ZoomLevel, bool)
Chris@173 127 {
Chris@222 128 if (v == this) return;
Chris@211 129 if (m_views.find(v) != m_views.end()) {
Chris@1266 130 update();
Chris@173 131 }
Chris@173 132 }
Chris@173 133
Chris@173 134 void
Chris@908 135 Overview::viewManagerPlaybackFrameChanged(sv_frame_t f)
Chris@173 136 {
Chris@642 137 #ifdef DEBUG_OVERVIEW
Chris@682 138 cerr << "Overview[" << this << "]::viewManagerPlaybackFrameChanged(" << f << "): " << f << endl;
Chris@642 139 #endif
Chris@642 140
Chris@173 141 bool changed = false;
Chris@173 142
Chris@339 143 f = getAlignedPlaybackFrame();
Chris@339 144
Chris@173 145 if (getXForFrame(m_playPointerFrame) != getXForFrame(f)) changed = true;
Chris@173 146 m_playPointerFrame = f;
Chris@173 147
Chris@173 148 if (changed) update();
Chris@173 149 }
Chris@173 150
Chris@871 151 QColor
Chris@871 152 Overview::getFillWithin() const
Chris@871 153 {
Chris@871 154 return Qt::transparent;
Chris@871 155 }
Chris@871 156
Chris@871 157 QColor
Chris@871 158 Overview::getFillWithout() const
Chris@871 159 {
Chris@871 160 QColor c = palette().window().color();
Chris@871 161 c.setAlpha(100);
Chris@871 162 return c;
Chris@871 163 }
Chris@871 164
Chris@173 165 void
Chris@965 166 Overview::setBoxColour(QColor c)
Chris@965 167 {
Chris@965 168 m_boxColour = c;
Chris@965 169 }
Chris@965 170
Chris@965 171 void
Chris@173 172 Overview::paintEvent(QPaintEvent *e)
Chris@173 173 {
Chris@1325 174 using namespace std::rel_ops;
Chris@1325 175
Chris@173 176 // Recalculate zoom in case the size of the widget has changed.
Chris@173 177
Chris@642 178 #ifdef DEBUG_OVERVIEW
Chris@682 179 cerr << "Overview::paintEvent: width is " << width() << ", centre frame " << m_centreFrame << endl;
Chris@642 180 #endif
Chris@214 181
Chris@908 182 sv_frame_t startFrame = getModelsStartFrame();
Chris@908 183 sv_frame_t frameCount = getModelsEndFrame() - getModelsStartFrame();
Chris@1325 184 ZoomLevel zoomLevel { ZoomLevel::FramesPerPixel, int(frameCount / width()) };
Chris@1325 185 if (zoomLevel.level < 1) zoomLevel.level = 1;
Chris@1325 186 zoomLevel = getZoomConstraintLevel(zoomLevel, ZoomConstraint::RoundUp);
Chris@173 187 if (zoomLevel != m_zoomLevel) {
Chris@1266 188 m_zoomLevel = zoomLevel;
Chris@1266 189 emit zoomLevelChanged(m_zoomLevel, m_followZoom);
Chris@173 190 }
Chris@253 191
Chris@1326 192 sv_frame_t centreFrame = startFrame +
Chris@1326 193 sv_frame_t(round(m_zoomLevel.pixelsToFrames(width()/2)));
Chris@1326 194
Chris@173 195 if (centreFrame > (startFrame + getModelsEndFrame())/2) {
Chris@1266 196 centreFrame = (startFrame + getModelsEndFrame())/2;
Chris@173 197 }
Chris@173 198 if (centreFrame != m_centreFrame) {
Chris@642 199 #ifdef DEBUG_OVERVIEW
Chris@682 200 cerr << "Overview::paintEvent: Centre frame changed from "
Chris@642 201 << m_centreFrame << " to " << centreFrame << " and thus start frame from " << getStartFrame();
Chris@642 202 #endif
Chris@1266 203 m_centreFrame = centreFrame;
Chris@642 204 #ifdef DEBUG_OVERVIEW
Chris@682 205 cerr << " to " << getStartFrame() << endl;
Chris@642 206 #endif
Chris@1266 207 emit centreFrameChanged(m_centreFrame, false, PlaybackIgnore);
Chris@173 208 }
Chris@173 209
Chris@173 210 View::paintEvent(e);
Chris@173 211
Chris@173 212 QPainter paint;
Chris@173 213 paint.begin(this);
Chris@871 214 paint.setClipRegion(e->region());
Chris@871 215 paint.setRenderHints(QPainter::Antialiasing);
Chris@871 216
Chris@871 217 // We paint a rounded rect for each distinct set of view extents,
Chris@871 218 // and we colour in the inside and outside of the rect that
Chris@871 219 // corresponds to the current view. (One small caveat -- we don't
Chris@871 220 // know which rect that is yet. We'll have to figure it out
Chris@871 221 // somehow...)
Chris@173 222
Chris@871 223 std::set<std::pair<int, int> > extents;
Chris@871 224 std::vector<QRect> rects;
Chris@871 225 QRect primary;
Chris@173 226
Chris@173 227 int y = 0;
Chris@173 228
Chris@211 229 for (ViewSet::iterator i = m_views.begin(); i != m_views.end(); ++i) {
Chris@1266 230 if (!*i) continue;
Chris@173 231
Chris@1266 232 View *w = (View *)*i;
Chris@173 233
Chris@1266 234 sv_frame_t f0 = w->getFrameForX(0);
Chris@1266 235 sv_frame_t f1 = w->getFrameForX(w->width());
Chris@173 236
Chris@339 237 if (f0 >= 0) {
Chris@908 238 sv_frame_t rf0 = w->alignToReference(f0);
Chris@339 239 f0 = alignFromReference(rf0);
Chris@339 240 }
Chris@339 241 if (f1 >= 0) {
Chris@908 242 sv_frame_t rf1 = w->alignToReference(f1);
Chris@339 243 f1 = alignFromReference(rf1);
Chris@339 244 }
Chris@339 245
Chris@1266 246 int x0 = getXForFrame(f0);
Chris@1266 247 int x1 = getXForFrame(f1);
Chris@173 248
Chris@1266 249 if (x1 <= x0) x1 = x0 + 1;
Chris@871 250
Chris@871 251 std::pair<int, int> extent(x0, x1);
Chris@871 252
Chris@871 253 if (extents.find(extent) == extents.end()) {
Chris@871 254
Chris@1266 255 y += height() / 10 + 1;
Chris@871 256 extents.insert(extent);
Chris@173 257
Chris@871 258 QRect vr(x0, y, x1 - x0, height() - 2 * y);
Chris@871 259 rects.push_back(vr);
Chris@871 260 primary = vr; //!!! for now
Chris@871 261 }
Chris@871 262 }
Chris@871 263
Chris@871 264 QPainterPath without;
matthiasm@935 265 without.addRoundedRect(primary, 4, 4);
Chris@871 266 without.addRect(rect());
Chris@871 267 paint.setPen(Qt::NoPen);
Chris@871 268 paint.setBrush(getFillWithout());
Chris@871 269 paint.drawPath(without);
Chris@871 270
Chris@871 271 paint.setBrush(getFillWithin());
matthiasm@935 272 paint.drawRoundedRect(primary, 4, 4);
Chris@871 273
Chris@871 274 foreach (QRect vr, rects) {
Chris@871 275 paint.setBrush(Qt::NoBrush);
Chris@965 276 paint.setPen(QPen(m_boxColour, 2));
matthiasm@935 277 paint.drawRoundedRect(vr, 4, 4);
Chris@173 278 }
Chris@173 279
Chris@173 280 paint.end();
Chris@173 281 }
Chris@173 282
Chris@173 283 void
Chris@173 284 Overview::mousePressEvent(QMouseEvent *e)
Chris@173 285 {
Chris@173 286 m_clickPos = e->pos();
Chris@908 287 sv_frame_t clickFrame = getFrameForX(m_clickPos.x());
Chris@339 288 if (clickFrame > 0) m_dragCentreFrame = clickFrame;
Chris@339 289 else m_dragCentreFrame = 0;
Chris@339 290 m_clickedInRange = true;
Chris@339 291
Chris@211 292 for (ViewSet::iterator i = m_views.begin(); i != m_views.end(); ++i) {
Chris@1266 293 if (*i && (*i)->getAligningModel() == getAligningModel()) {
Chris@339 294 m_dragCentreFrame = (*i)->getCentreFrame();
Chris@339 295 break;
Chris@339 296 }
Chris@173 297 }
Chris@173 298 }
Chris@173 299
Chris@173 300 void
Chris@173 301 Overview::mouseReleaseEvent(QMouseEvent *e)
Chris@173 302 {
Chris@173 303 if (m_clickedInRange) {
Chris@1266 304 mouseMoveEvent(e);
Chris@173 305 }
Chris@173 306 m_clickedInRange = false;
Chris@173 307 }
Chris@173 308
Chris@173 309 void
Chris@173 310 Overview::mouseMoveEvent(QMouseEvent *e)
Chris@173 311 {
Chris@173 312 if (!m_clickedInRange) return;
Chris@173 313
Chris@806 314 int xoff = int(e->x()) - int(m_clickPos.x());
Chris@1326 315 sv_frame_t frameOff = sv_frame_t(round(m_zoomLevel.pixelsToFrames(xoff)));
Chris@173 316
Chris@908 317 sv_frame_t newCentreFrame = m_dragCentreFrame;
Chris@173 318 if (frameOff > 0) {
Chris@1266 319 newCentreFrame += frameOff;
Chris@908 320 } else if (newCentreFrame >= -frameOff) {
Chris@1266 321 newCentreFrame += frameOff;
Chris@173 322 } else {
Chris@1266 323 newCentreFrame = 0;
Chris@173 324 }
Chris@173 325
Chris@173 326 if (newCentreFrame >= getModelsEndFrame()) {
Chris@1266 327 newCentreFrame = getModelsEndFrame();
Chris@1266 328 if (newCentreFrame > 0) --newCentreFrame;
Chris@173 329 }
Chris@173 330
Chris@1328 331 sv_frame_t pixel = sv_frame_t(round(m_zoomLevel.pixelsToFrames(1)));
Chris@1328 332
Chris@173 333 if (std::max(m_centreFrame, newCentreFrame) -
Chris@1326 334 std::min(m_centreFrame, newCentreFrame) >
Chris@1328 335 pixel) {
Chris@908 336 sv_frame_t rf = alignToReference(newCentreFrame);
Chris@643 337 #ifdef DEBUG_OVERVIEW
Chris@682 338 cerr << "Overview::mouseMoveEvent: x " << e->x() << " and click x " << m_clickPos.x() << " -> frame " << newCentreFrame << " -> rf " << rf << endl;
Chris@643 339 #endif
Chris@817 340 if (m_followPlay == PlaybackScrollContinuous ||
Chris@817 341 m_followPlay == PlaybackScrollPageWithCentre) {
Chris@817 342 emit centreFrameChanged(rf, true, PlaybackScrollContinuous);
Chris@817 343 } else {
Chris@817 344 emit centreFrameChanged(rf, true, PlaybackIgnore);
Chris@817 345 }
Chris@173 346 }
Chris@173 347 }
Chris@173 348
Chris@189 349 void
Chris@189 350 Overview::mouseDoubleClickEvent(QMouseEvent *e)
Chris@189 351 {
Chris@908 352 sv_frame_t frame = getFrameForX(e->x());
Chris@908 353 sv_frame_t rf = 0;
Chris@339 354 if (frame > 0) rf = alignToReference(frame);
Chris@643 355 #ifdef DEBUG_OVERVIEW
Chris@682 356 cerr << "Overview::mouseDoubleClickEvent: frame " << frame << " -> rf " << rf << endl;
Chris@643 357 #endif
Chris@643 358 m_clickedInRange = false; // we're not starting a drag with the second click
Chris@339 359 emit centreFrameChanged(rf, true, PlaybackScrollContinuous);
Chris@189 360 }
Chris@173 361
Chris@189 362 void
Chris@189 363 Overview::enterEvent(QEvent *)
Chris@189 364 {
Chris@189 365 emit contextHelpChanged(tr("Click and drag to navigate; double-click to jump"));
Chris@189 366 }
Chris@189 367
Chris@189 368 void
Chris@189 369 Overview::leaveEvent(QEvent *)
Chris@189 370 {
Chris@189 371 emit contextHelpChanged("");
Chris@189 372 }
Chris@189 373
Chris@189 374