annotate view/Overview.cpp @ 1447:8afea53332f3 single-point

Add option to make pane sizes auto-resize-only (i.e. remove user control via a splitter); also place alignment views above panes instead of below, meaning the extra bit of space that we currently have for the pane without one at least goes to the primary pane
author Chris Cannam
date Tue, 30 Apr 2019 15:53:21 +0100
parents b39cd8fb309f
children 84c4ddb38415
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@908 45 Overview::modelChangedWithin(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@274 63 if ((*i)->getModel() &&
Chris@389 64 (!(*i)->getModel()->isOK() ||
Chris@389 65 !(*i)->getModel()->isReady())) {
Chris@274 66 return;
Chris@274 67 }
Chris@253 68 }
Chris@274 69 } else {
Chris@274 70 m_modelTestTime.restart();
Chris@253 71 }
Chris@253 72 }
Chris@253 73
Chris@806 74 View::modelChangedWithin(startFrame, endFrame);
Chris@173 75 }
Chris@173 76
Chris@173 77 void
Chris@173 78 Overview::modelReplaced()
Chris@173 79 {
Chris@339 80 m_playPointerFrame = getAlignedPlaybackFrame();
Chris@173 81 View::modelReplaced();
Chris@173 82 }
Chris@173 83
Chris@173 84 void
Chris@211 85 Overview::registerView(View *view)
Chris@173 86 {
Chris@211 87 m_views.insert(view);
Chris@173 88 update();
Chris@173 89 }
Chris@173 90
Chris@173 91 void
Chris@211 92 Overview::unregisterView(View *view)
Chris@173 93 {
Chris@211 94 m_views.erase(view);
Chris@173 95 update();
Chris@173 96 }
Chris@173 97
Chris@173 98 void
Chris@908 99 Overview::globalCentreFrameChanged(sv_frame_t
Chris@806 100 #ifdef DEBUG_OVERVIEW
Chris@806 101 f
Chris@806 102 #endif
Chris@806 103 )
Chris@173 104 {
Chris@642 105 #ifdef DEBUG_OVERVIEW
Chris@682 106 cerr << "Overview::globalCentreFrameChanged: " << f << endl;
Chris@642 107 #endif
Chris@211 108 update();
Chris@211 109 }
Chris@173 110
Chris@211 111 void
Chris@908 112 Overview::viewCentreFrameChanged(View *v, sv_frame_t
Chris@806 113 #ifdef DEBUG_OVERVIEW
Chris@806 114 f
Chris@806 115 #endif
Chris@806 116 )
Chris@211 117 {
Chris@642 118 #ifdef DEBUG_OVERVIEW
Chris@682 119 cerr << "Overview[" << this << "]::viewCentreFrameChanged(" << v << "): " << f << endl;
Chris@642 120 #endif
Chris@211 121 if (m_views.find(v) != m_views.end()) {
Chris@1266 122 update();
Chris@173 123 }
Chris@211 124 }
Chris@173 125
Chris@173 126 void
Chris@1326 127 Overview::viewZoomLevelChanged(View *v, ZoomLevel, bool)
Chris@173 128 {
Chris@222 129 if (v == this) return;
Chris@211 130 if (m_views.find(v) != m_views.end()) {
Chris@1266 131 update();
Chris@173 132 }
Chris@173 133 }
Chris@173 134
Chris@173 135 void
Chris@908 136 Overview::viewManagerPlaybackFrameChanged(sv_frame_t f)
Chris@173 137 {
Chris@642 138 #ifdef DEBUG_OVERVIEW
Chris@682 139 cerr << "Overview[" << this << "]::viewManagerPlaybackFrameChanged(" << f << "): " << f << endl;
Chris@642 140 #endif
Chris@642 141
Chris@173 142 bool changed = false;
Chris@173 143
Chris@339 144 f = getAlignedPlaybackFrame();
Chris@339 145
Chris@173 146 if (getXForFrame(m_playPointerFrame) != getXForFrame(f)) changed = true;
Chris@173 147 m_playPointerFrame = f;
Chris@173 148
Chris@173 149 if (changed) update();
Chris@173 150 }
Chris@173 151
Chris@871 152 QColor
Chris@871 153 Overview::getFillWithin() const
Chris@871 154 {
Chris@871 155 return Qt::transparent;
Chris@871 156 }
Chris@871 157
Chris@871 158 QColor
Chris@871 159 Overview::getFillWithout() const
Chris@871 160 {
Chris@871 161 QColor c = palette().window().color();
Chris@871 162 c.setAlpha(100);
Chris@871 163 return c;
Chris@871 164 }
Chris@871 165
Chris@173 166 void
Chris@965 167 Overview::setBoxColour(QColor c)
Chris@965 168 {
Chris@965 169 m_boxColour = c;
Chris@965 170 }
Chris@965 171
Chris@965 172 void
Chris@173 173 Overview::paintEvent(QPaintEvent *e)
Chris@173 174 {
Chris@1325 175 using namespace std::rel_ops;
Chris@1325 176
Chris@173 177 // Recalculate zoom in case the size of the widget has changed.
Chris@173 178
Chris@642 179 #ifdef DEBUG_OVERVIEW
Chris@682 180 cerr << "Overview::paintEvent: width is " << width() << ", centre frame " << m_centreFrame << endl;
Chris@642 181 #endif
Chris@214 182
Chris@908 183 sv_frame_t startFrame = getModelsStartFrame();
Chris@908 184 sv_frame_t frameCount = getModelsEndFrame() - getModelsStartFrame();
Chris@1325 185 ZoomLevel zoomLevel { ZoomLevel::FramesPerPixel, int(frameCount / width()) };
Chris@1325 186 if (zoomLevel.level < 1) zoomLevel.level = 1;
Chris@1325 187 zoomLevel = getZoomConstraintLevel(zoomLevel, ZoomConstraint::RoundUp);
Chris@173 188 if (zoomLevel != m_zoomLevel) {
Chris@1266 189 m_zoomLevel = zoomLevel;
Chris@1266 190 emit zoomLevelChanged(m_zoomLevel, m_followZoom);
Chris@173 191 }
Chris@253 192
Chris@1326 193 sv_frame_t centreFrame = startFrame +
Chris@1326 194 sv_frame_t(round(m_zoomLevel.pixelsToFrames(width()/2)));
Chris@1326 195
Chris@173 196 if (centreFrame > (startFrame + getModelsEndFrame())/2) {
Chris@1266 197 centreFrame = (startFrame + getModelsEndFrame())/2;
Chris@173 198 }
Chris@173 199 if (centreFrame != m_centreFrame) {
Chris@642 200 #ifdef DEBUG_OVERVIEW
Chris@682 201 cerr << "Overview::paintEvent: Centre frame changed from "
Chris@642 202 << m_centreFrame << " to " << centreFrame << " and thus start frame from " << getStartFrame();
Chris@642 203 #endif
Chris@1266 204 m_centreFrame = centreFrame;
Chris@642 205 #ifdef DEBUG_OVERVIEW
Chris@682 206 cerr << " to " << getStartFrame() << endl;
Chris@642 207 #endif
Chris@1266 208 emit centreFrameChanged(m_centreFrame, false, PlaybackIgnore);
Chris@173 209 }
Chris@173 210
Chris@173 211 View::paintEvent(e);
Chris@173 212
Chris@173 213 QPainter paint;
Chris@173 214 paint.begin(this);
Chris@871 215 paint.setClipRegion(e->region());
Chris@871 216 paint.setRenderHints(QPainter::Antialiasing);
Chris@871 217
Chris@871 218 // We paint a rounded rect for each distinct set of view extents,
Chris@871 219 // and we colour in the inside and outside of the rect that
Chris@871 220 // corresponds to the current view. (One small caveat -- we don't
Chris@871 221 // know which rect that is yet. We'll have to figure it out
Chris@871 222 // somehow...)
Chris@173 223
Chris@871 224 std::set<std::pair<int, int> > extents;
Chris@871 225 std::vector<QRect> rects;
Chris@871 226 QRect primary;
Chris@173 227
Chris@173 228 int y = 0;
Chris@173 229
Chris@211 230 for (ViewSet::iterator i = m_views.begin(); i != m_views.end(); ++i) {
Chris@1266 231 if (!*i) continue;
Chris@173 232
Chris@1266 233 View *w = (View *)*i;
Chris@173 234
Chris@1266 235 sv_frame_t f0 = w->getFrameForX(0);
Chris@1266 236 sv_frame_t f1 = w->getFrameForX(w->width());
Chris@173 237
Chris@339 238 if (f0 >= 0) {
Chris@908 239 sv_frame_t rf0 = w->alignToReference(f0);
Chris@339 240 f0 = alignFromReference(rf0);
Chris@339 241 }
Chris@339 242 if (f1 >= 0) {
Chris@908 243 sv_frame_t rf1 = w->alignToReference(f1);
Chris@339 244 f1 = alignFromReference(rf1);
Chris@339 245 }
Chris@339 246
Chris@1266 247 int x0 = getXForFrame(f0);
Chris@1266 248 int x1 = getXForFrame(f1);
Chris@173 249
Chris@1266 250 if (x1 <= x0) x1 = x0 + 1;
Chris@871 251
Chris@871 252 std::pair<int, int> extent(x0, x1);
Chris@871 253
Chris@871 254 if (extents.find(extent) == extents.end()) {
Chris@871 255
Chris@1266 256 y += height() / 10 + 1;
Chris@871 257 extents.insert(extent);
Chris@173 258
Chris@871 259 QRect vr(x0, y, x1 - x0, height() - 2 * y);
Chris@871 260 rects.push_back(vr);
Chris@871 261 primary = vr; //!!! for now
Chris@871 262 }
Chris@871 263 }
Chris@871 264
Chris@871 265 QPainterPath without;
matthiasm@935 266 without.addRoundedRect(primary, 4, 4);
Chris@871 267 without.addRect(rect());
Chris@871 268 paint.setPen(Qt::NoPen);
Chris@871 269 paint.setBrush(getFillWithout());
Chris@871 270 paint.drawPath(without);
Chris@871 271
Chris@871 272 paint.setBrush(getFillWithin());
matthiasm@935 273 paint.drawRoundedRect(primary, 4, 4);
Chris@871 274
Chris@871 275 foreach (QRect vr, rects) {
Chris@871 276 paint.setBrush(Qt::NoBrush);
Chris@965 277 paint.setPen(QPen(m_boxColour, 2));
matthiasm@935 278 paint.drawRoundedRect(vr, 4, 4);
Chris@173 279 }
Chris@173 280
Chris@173 281 paint.end();
Chris@173 282 }
Chris@173 283
Chris@173 284 void
Chris@173 285 Overview::mousePressEvent(QMouseEvent *e)
Chris@173 286 {
Chris@173 287 m_clickPos = e->pos();
Chris@908 288 sv_frame_t clickFrame = getFrameForX(m_clickPos.x());
Chris@339 289 if (clickFrame > 0) m_dragCentreFrame = clickFrame;
Chris@339 290 else m_dragCentreFrame = 0;
Chris@339 291 m_clickedInRange = true;
Chris@339 292
Chris@211 293 for (ViewSet::iterator i = m_views.begin(); i != m_views.end(); ++i) {
Chris@1266 294 if (*i && (*i)->getAligningModel() == getAligningModel()) {
Chris@339 295 m_dragCentreFrame = (*i)->getCentreFrame();
Chris@339 296 break;
Chris@339 297 }
Chris@173 298 }
Chris@173 299 }
Chris@173 300
Chris@173 301 void
Chris@173 302 Overview::mouseReleaseEvent(QMouseEvent *e)
Chris@173 303 {
Chris@173 304 if (m_clickedInRange) {
Chris@1266 305 mouseMoveEvent(e);
Chris@173 306 }
Chris@173 307 m_clickedInRange = false;
Chris@173 308 }
Chris@173 309
Chris@173 310 void
Chris@173 311 Overview::mouseMoveEvent(QMouseEvent *e)
Chris@173 312 {
Chris@173 313 if (!m_clickedInRange) return;
Chris@173 314
Chris@806 315 int xoff = int(e->x()) - int(m_clickPos.x());
Chris@1326 316 sv_frame_t frameOff = sv_frame_t(round(m_zoomLevel.pixelsToFrames(xoff)));
Chris@173 317
Chris@908 318 sv_frame_t newCentreFrame = m_dragCentreFrame;
Chris@173 319 if (frameOff > 0) {
Chris@1266 320 newCentreFrame += frameOff;
Chris@908 321 } else if (newCentreFrame >= -frameOff) {
Chris@1266 322 newCentreFrame += frameOff;
Chris@173 323 } else {
Chris@1266 324 newCentreFrame = 0;
Chris@173 325 }
Chris@173 326
Chris@173 327 if (newCentreFrame >= getModelsEndFrame()) {
Chris@1266 328 newCentreFrame = getModelsEndFrame();
Chris@1266 329 if (newCentreFrame > 0) --newCentreFrame;
Chris@173 330 }
Chris@173 331
Chris@1328 332 sv_frame_t pixel = sv_frame_t(round(m_zoomLevel.pixelsToFrames(1)));
Chris@1328 333
Chris@173 334 if (std::max(m_centreFrame, newCentreFrame) -
Chris@1326 335 std::min(m_centreFrame, newCentreFrame) >
Chris@1328 336 pixel) {
Chris@908 337 sv_frame_t rf = alignToReference(newCentreFrame);
Chris@643 338 #ifdef DEBUG_OVERVIEW
Chris@682 339 cerr << "Overview::mouseMoveEvent: x " << e->x() << " and click x " << m_clickPos.x() << " -> frame " << newCentreFrame << " -> rf " << rf << endl;
Chris@643 340 #endif
Chris@817 341 if (m_followPlay == PlaybackScrollContinuous ||
Chris@817 342 m_followPlay == PlaybackScrollPageWithCentre) {
Chris@817 343 emit centreFrameChanged(rf, true, PlaybackScrollContinuous);
Chris@817 344 } else {
Chris@817 345 emit centreFrameChanged(rf, true, PlaybackIgnore);
Chris@817 346 }
Chris@173 347 }
Chris@173 348 }
Chris@173 349
Chris@189 350 void
Chris@189 351 Overview::mouseDoubleClickEvent(QMouseEvent *e)
Chris@189 352 {
Chris@908 353 sv_frame_t frame = getFrameForX(e->x());
Chris@908 354 sv_frame_t rf = 0;
Chris@339 355 if (frame > 0) rf = alignToReference(frame);
Chris@643 356 #ifdef DEBUG_OVERVIEW
Chris@682 357 cerr << "Overview::mouseDoubleClickEvent: frame " << frame << " -> rf " << rf << endl;
Chris@643 358 #endif
Chris@643 359 m_clickedInRange = false; // we're not starting a drag with the second click
Chris@339 360 emit centreFrameChanged(rf, true, PlaybackScrollContinuous);
Chris@189 361 }
Chris@173 362
Chris@189 363 void
Chris@189 364 Overview::enterEvent(QEvent *)
Chris@189 365 {
Chris@189 366 emit contextHelpChanged(tr("Click and drag to navigate; double-click to jump"));
Chris@189 367 }
Chris@189 368
Chris@189 369 void
Chris@189 370 Overview::leaveEvent(QEvent *)
Chris@189 371 {
Chris@189 372 emit contextHelpChanged("");
Chris@189 373 }
Chris@189 374
Chris@189 375