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@1611
|
23 #include <QPainterPath>
|
Chris@173
|
24 #include <iostream>
|
Chris@173
|
25
|
Chris@643
|
26 //#define DEBUG_OVERVIEW 1
|
Chris@642
|
27
|
Chris@682
|
28
|
Chris@173
|
29 Overview::Overview(QWidget *w) :
|
Chris@173
|
30 View(w, false),
|
Chris@854
|
31 m_clickedInRange(false),
|
Chris@854
|
32 m_dragCentreFrame(0)
|
Chris@173
|
33 {
|
Chris@173
|
34 setObjectName(tr("Overview"));
|
Chris@173
|
35 m_followPan = false;
|
Chris@173
|
36 m_followZoom = false;
|
Chris@211
|
37 setPlaybackFollow(PlaybackIgnore);
|
Chris@1554
|
38 m_modelTestTimer.start();
|
Chris@965
|
39
|
Chris@965
|
40 bool light = hasLightBackground();
|
Chris@965
|
41 if (light) m_boxColour = Qt::darkGray;
|
Chris@965
|
42 else m_boxColour = Qt::lightGray;
|
Chris@173
|
43 }
|
Chris@173
|
44
|
Chris@173
|
45 void
|
Chris@1481
|
46 Overview::modelChangedWithin(ModelId modelId, sv_frame_t startFrame, sv_frame_t endFrame)
|
Chris@173
|
47 {
|
Chris@1325
|
48 using namespace std::rel_ops;
|
Chris@1325
|
49
|
Chris@253
|
50 bool zoomChanged = false;
|
Chris@253
|
51
|
Chris@908
|
52 sv_frame_t frameCount = getModelsEndFrame() - getModelsStartFrame();
|
Chris@1183
|
53 ZoomLevel zoomLevel { ZoomLevel::FramesPerPixel, int(frameCount / width()) };
|
Chris@1183
|
54 if (zoomLevel.level < 1) zoomLevel.level = 1;
|
Chris@1325
|
55 zoomLevel = getZoomConstraintLevel(zoomLevel, ZoomConstraint::RoundUp);
|
Chris@253
|
56 if (zoomLevel != m_zoomLevel) {
|
Chris@253
|
57 zoomChanged = true;
|
Chris@253
|
58 }
|
Chris@253
|
59
|
Chris@253
|
60 if (!zoomChanged) {
|
Chris@1554
|
61 if (m_modelTestTimer.elapsed() < 1000) {
|
Chris@835
|
62 for (LayerList::const_iterator i = m_layerStack.begin();
|
Chris@835
|
63 i != m_layerStack.end(); ++i) {
|
Chris@1475
|
64 auto model = ModelById::get((*i)->getModel());
|
Chris@1475
|
65 if (model && (!model->isOK() || !model->isReady())) {
|
Chris@274
|
66 return;
|
Chris@274
|
67 }
|
Chris@253
|
68 }
|
Chris@274
|
69 } else {
|
Chris@1554
|
70 m_modelTestTimer.restart();
|
Chris@253
|
71 }
|
Chris@253
|
72 }
|
Chris@253
|
73
|
Chris@1481
|
74 View::modelChangedWithin(modelId, 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
|