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