annotate view/Overview.cpp @ 932:37bb4b416c52 tonioni

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