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@806
|
41 Overview::modelChangedWithin(int startFrame, int endFrame)
|
Chris@173
|
42 {
|
Chris@253
|
43 bool zoomChanged = false;
|
Chris@253
|
44
|
Chris@806
|
45 int frameCount = getModelsEndFrame() - getModelsStartFrame();
|
Chris@253
|
46 int zoomLevel = 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@806
|
94 Overview::globalCentreFrameChanged(int
|
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@806
|
107 Overview::viewCentreFrameChanged(View *v, int
|
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@806
|
131 Overview::viewManagerPlaybackFrameChanged(int 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@806
|
170 int startFrame = getModelsStartFrame();
|
Chris@806
|
171 int frameCount = getModelsEndFrame() - getModelsStartFrame();
|
Chris@173
|
172 int zoomLevel = 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@806
|
181 int 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@871
|
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@806
|
223 int f0 = w->getFrameForX(0);
|
Chris@806
|
224 int f1 = w->getFrameForX(w->width());
|
Chris@173
|
225
|
Chris@339
|
226 if (f0 >= 0) {
|
Chris@806
|
227 int rf0 = w->alignToReference(f0);
|
Chris@339
|
228 f0 = alignFromReference(rf0);
|
Chris@339
|
229 }
|
Chris@339
|
230 if (f1 >= 0) {
|
Chris@806
|
231 int 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@806
|
276 int 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@806
|
304 int frameOff = xoff * m_zoomLevel;
|
Chris@173
|
305
|
Chris@806
|
306 int newCentreFrame = m_dragCentreFrame;
|
Chris@173
|
307 if (frameOff > 0) {
|
Chris@173
|
308 newCentreFrame += frameOff;
|
Chris@806
|
309 } else if (newCentreFrame >= int(-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@806
|
321 std::min(m_centreFrame, newCentreFrame) > int(m_zoomLevel)) {
|
Chris@806
|
322 int 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@806
|
338 int frame = getFrameForX(e->x());
|
Chris@806
|
339 int 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
|