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