Mercurial > hg > easaier-soundaccess
comparison view/View.cpp @ 0:fc9323a41f5a
start base : Sonic Visualiser sv1-1.0rc1
author | lbajardsilogic |
---|---|
date | Fri, 11 May 2007 09:08:14 +0000 |
parents | |
children | 7b19f2719f91 |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:fc9323a41f5a |
---|---|
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. | |
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 "View.h" | |
17 #include "layer/Layer.h" | |
18 #include "data/model/Model.h" | |
19 #include "base/ZoomConstraint.h" | |
20 #include "base/Profiler.h" | |
21 | |
22 #include "layer/TimeRulerLayer.h" //!!! damn, shouldn't be including that here | |
23 #include "data/model/PowerOfSqrtTwoZoomConstraint.h" //!!! likewise | |
24 | |
25 #include <QPainter> | |
26 #include <QPaintEvent> | |
27 #include <QRect> | |
28 #include <QApplication> | |
29 #include <QProgressDialog> | |
30 | |
31 #include <iostream> | |
32 #include <cassert> | |
33 #include <math.h> | |
34 | |
35 //#define DEBUG_VIEW_WIDGET_PAINT 1 | |
36 | |
37 using std::cerr; | |
38 using std::endl; | |
39 | |
40 View::View(QWidget *w, bool showProgress) : | |
41 QFrame(w), | |
42 m_centreFrame(0), | |
43 m_zoomLevel(1024), | |
44 m_followPan(true), | |
45 m_followZoom(true), | |
46 m_followPlay(PlaybackScrollPage), | |
47 m_playPointerFrame(0), | |
48 m_showProgress(showProgress), | |
49 m_cache(0), | |
50 m_cacheCentreFrame(0), | |
51 m_cacheZoomLevel(1024), | |
52 m_selectionCached(false), | |
53 m_deleting(false), | |
54 m_haveSelectedLayer(false), | |
55 m_manager(0), | |
56 m_propertyContainer(new ViewPropertyContainer(this)) | |
57 { | |
58 } | |
59 | |
60 View::~View() | |
61 { | |
62 // std::cerr << "View::~View(" << this << ")" << std::endl; | |
63 | |
64 m_deleting = true; | |
65 delete m_propertyContainer; | |
66 } | |
67 | |
68 PropertyContainer::PropertyList | |
69 View::getProperties() const | |
70 { | |
71 PropertyContainer::PropertyList list; | |
72 list.push_back("Global Scroll"); | |
73 list.push_back("Global Zoom"); | |
74 list.push_back("Follow Playback"); | |
75 return list; | |
76 } | |
77 | |
78 QString | |
79 View::getPropertyLabel(const PropertyName &pn) const | |
80 { | |
81 if (pn == "Global Scroll") return tr("Global Scroll"); | |
82 if (pn == "Global Zoom") return tr("Global Zoom"); | |
83 if (pn == "Follow Playback") return tr("Follow Playback"); | |
84 return ""; | |
85 } | |
86 | |
87 PropertyContainer::PropertyType | |
88 View::getPropertyType(const PropertyContainer::PropertyName &name) const | |
89 { | |
90 if (name == "Global Scroll") return PropertyContainer::ToggleProperty; | |
91 if (name == "Global Zoom") return PropertyContainer::ToggleProperty; | |
92 if (name == "Follow Playback") return PropertyContainer::ValueProperty; | |
93 return PropertyContainer::InvalidProperty; | |
94 } | |
95 | |
96 int | |
97 View::getPropertyRangeAndValue(const PropertyContainer::PropertyName &name, | |
98 int *min, int *max, int *deflt) const | |
99 { | |
100 if (deflt) *deflt = 1; | |
101 if (name == "Global Scroll") return m_followPan; | |
102 if (name == "Global Zoom") return m_followZoom; | |
103 if (name == "Follow Playback") { | |
104 if (min) *min = 0; | |
105 if (max) *max = 2; | |
106 if (deflt) *deflt = int(PlaybackScrollPage); | |
107 return int(m_followPlay); | |
108 } | |
109 if (min) *min = 0; | |
110 if (max) *max = 0; | |
111 if (deflt) *deflt = 0; | |
112 return 0; | |
113 } | |
114 | |
115 QString | |
116 View::getPropertyValueLabel(const PropertyContainer::PropertyName &name, | |
117 int value) const | |
118 { | |
119 if (name == "Follow Playback") { | |
120 switch (value) { | |
121 default: | |
122 case 0: return tr("Scroll"); | |
123 case 1: return tr("Page"); | |
124 case 2: return tr("Off"); | |
125 } | |
126 } | |
127 return tr("<unknown>"); | |
128 } | |
129 | |
130 void | |
131 View::setProperty(const PropertyContainer::PropertyName &name, int value) | |
132 { | |
133 if (name == "Global Scroll") { | |
134 setFollowGlobalPan(value != 0); | |
135 } else if (name == "Global Zoom") { | |
136 setFollowGlobalZoom(value != 0); | |
137 } else if (name == "Follow Playback") { | |
138 switch (value) { | |
139 default: | |
140 case 0: setPlaybackFollow(PlaybackScrollContinuous); break; | |
141 case 1: setPlaybackFollow(PlaybackScrollPage); break; | |
142 case 2: setPlaybackFollow(PlaybackIgnore); break; | |
143 } | |
144 } | |
145 } | |
146 | |
147 size_t | |
148 View::getPropertyContainerCount() const | |
149 { | |
150 return m_layers.size() + 1; // the 1 is for me | |
151 } | |
152 | |
153 const PropertyContainer * | |
154 View::getPropertyContainer(size_t i) const | |
155 { | |
156 return (const PropertyContainer *)(((View *)this)-> | |
157 getPropertyContainer(i)); | |
158 } | |
159 | |
160 PropertyContainer * | |
161 View::getPropertyContainer(size_t i) | |
162 { | |
163 if (i == 0) return m_propertyContainer; | |
164 return m_layers[i-1]; | |
165 } | |
166 | |
167 bool | |
168 View::getValueExtents(QString unit, float &min, float &max, bool &log) const | |
169 { | |
170 bool have = false; | |
171 | |
172 for (LayerList::const_iterator i = m_layers.begin(); | |
173 i != m_layers.end(); ++i) { | |
174 | |
175 QString layerUnit; | |
176 float layerMin = 0.0, layerMax = 0.0; | |
177 float displayMin = 0.0, displayMax = 0.0; | |
178 bool layerLog = false; | |
179 | |
180 if ((*i)->getValueExtents(layerMin, layerMax, layerLog, layerUnit) && | |
181 layerUnit.toLower() == unit.toLower()) { | |
182 | |
183 if ((*i)->getDisplayExtents(displayMin, displayMax)) { | |
184 | |
185 min = displayMin; | |
186 max = displayMax; | |
187 log = layerLog; | |
188 have = true; | |
189 break; | |
190 | |
191 } else { | |
192 | |
193 if (!have || layerMin < min) min = layerMin; | |
194 if (!have || layerMax > max) max = layerMax; | |
195 if (layerLog) log = true; | |
196 have = true; | |
197 } | |
198 } | |
199 } | |
200 | |
201 return have; | |
202 } | |
203 | |
204 int | |
205 View::getTextLabelHeight(const Layer *layer, QPainter &paint) const | |
206 { | |
207 std::map<int, Layer *> sortedLayers; | |
208 | |
209 for (LayerList::const_iterator i = m_layers.begin(); | |
210 i != m_layers.end(); ++i) { | |
211 if ((*i)->needsTextLabelHeight()) { | |
212 sortedLayers[getObjectExportId(*i)] = *i; | |
213 } | |
214 } | |
215 | |
216 int y = 15 + paint.fontMetrics().ascent(); | |
217 | |
218 for (std::map<int, Layer *>::const_iterator i = sortedLayers.begin(); | |
219 i != sortedLayers.end(); ++i) { | |
220 if (i->second == layer) return y; | |
221 y += paint.fontMetrics().height(); | |
222 } | |
223 | |
224 return y; | |
225 } | |
226 | |
227 void | |
228 View::propertyContainerSelected(View *client, PropertyContainer *pc) | |
229 { | |
230 if (client != this) return; | |
231 | |
232 if (pc == m_propertyContainer) { | |
233 if (m_haveSelectedLayer) { | |
234 m_haveSelectedLayer = false; | |
235 update(); | |
236 } | |
237 return; | |
238 } | |
239 | |
240 delete m_cache; | |
241 m_cache = 0; | |
242 | |
243 Layer *selectedLayer = 0; | |
244 | |
245 for (LayerList::iterator i = m_layers.begin(); i != m_layers.end(); ++i) { | |
246 if (*i == pc) { | |
247 selectedLayer = *i; | |
248 m_layers.erase(i); | |
249 break; | |
250 } | |
251 } | |
252 | |
253 if (selectedLayer) { | |
254 m_haveSelectedLayer = true; | |
255 m_layers.push_back(selectedLayer); | |
256 update(); | |
257 } else { | |
258 m_haveSelectedLayer = false; | |
259 } | |
260 } | |
261 | |
262 void | |
263 View::toolModeChanged() | |
264 { | |
265 // std::cerr << "View::toolModeChanged(" << m_manager->getToolMode() << ")" << std::endl; | |
266 } | |
267 | |
268 void | |
269 View::overlayModeChanged() | |
270 { | |
271 delete m_cache; | |
272 m_cache = 0; | |
273 update(); | |
274 } | |
275 | |
276 void | |
277 View::zoomWheelsEnabledChanged() | |
278 { | |
279 // subclass might override this | |
280 } | |
281 | |
282 long | |
283 View::getStartFrame() const | |
284 { | |
285 size_t w2 = (width() / 2) * m_zoomLevel; | |
286 size_t frame = m_centreFrame; | |
287 if (frame >= w2) { | |
288 frame -= w2; | |
289 return (frame / m_zoomLevel * m_zoomLevel); | |
290 } else { | |
291 frame = w2 - frame; | |
292 frame = frame / m_zoomLevel * m_zoomLevel; | |
293 return -(long)frame - m_zoomLevel; | |
294 } | |
295 } | |
296 | |
297 size_t | |
298 View::getEndFrame() const | |
299 { | |
300 return getFrameForX(width()) - 1; | |
301 } | |
302 | |
303 void | |
304 View::setStartFrame(long f) | |
305 { | |
306 setCentreFrame(f + m_zoomLevel * (width() / 2)); | |
307 } | |
308 | |
309 bool | |
310 View::setCentreFrame(size_t f, bool e) | |
311 { | |
312 bool changeVisible = false; | |
313 | |
314 if (m_centreFrame != f) { | |
315 | |
316 int formerPixel = m_centreFrame / m_zoomLevel; | |
317 | |
318 m_centreFrame = f; | |
319 | |
320 int newPixel = m_centreFrame / m_zoomLevel; | |
321 | |
322 if (newPixel != formerPixel) { | |
323 | |
324 #ifdef DEBUG_VIEW_WIDGET_PAINT | |
325 std::cout << "View(" << this << ")::setCentreFrame: newPixel " << newPixel << ", formerPixel " << formerPixel << std::endl; | |
326 #endif | |
327 update(); | |
328 | |
329 changeVisible = true; | |
330 } | |
331 | |
332 if (e) emit centreFrameChanged(f, m_followPan, m_followPlay); | |
333 } | |
334 | |
335 return changeVisible; | |
336 } | |
337 | |
338 int | |
339 View::getXForFrame(long frame) const | |
340 { | |
341 return (frame - getStartFrame()) / m_zoomLevel; | |
342 } | |
343 | |
344 long | |
345 View::getFrameForX(int x) const | |
346 { | |
347 return (long(x) * long(m_zoomLevel)) + getStartFrame(); | |
348 } | |
349 | |
350 float | |
351 View::getYForFrequency(float frequency, | |
352 float minf, | |
353 float maxf, | |
354 bool logarithmic) const | |
355 { | |
356 int h = height(); | |
357 | |
358 if (logarithmic) { | |
359 | |
360 static float lastminf = 0.0, lastmaxf = 0.0; | |
361 static float logminf = 0.0, logmaxf = 0.0; | |
362 | |
363 if (lastminf != minf) { | |
364 lastminf = (minf == 0.0 ? 1.0 : minf); | |
365 logminf = log10f(minf); | |
366 } | |
367 if (lastmaxf != maxf) { | |
368 lastmaxf = (maxf < lastminf ? lastminf : maxf); | |
369 logmaxf = log10f(maxf); | |
370 } | |
371 | |
372 if (logminf == logmaxf) return 0; | |
373 return h - (h * (log10f(frequency) - logminf)) / (logmaxf - logminf); | |
374 | |
375 } else { | |
376 | |
377 if (minf == maxf) return 0; | |
378 return h - (h * (frequency - minf)) / (maxf - minf); | |
379 } | |
380 } | |
381 | |
382 float | |
383 View::getFrequencyForY(int y, | |
384 float minf, | |
385 float maxf, | |
386 bool logarithmic) const | |
387 { | |
388 int h = height(); | |
389 | |
390 if (logarithmic) { | |
391 | |
392 static float lastminf = 0.0, lastmaxf = 0.0; | |
393 static float logminf = 0.0, logmaxf = 0.0; | |
394 | |
395 if (lastminf != minf) { | |
396 lastminf = (minf == 0.0 ? 1.0 : minf); | |
397 logminf = log10f(minf); | |
398 } | |
399 if (lastmaxf != maxf) { | |
400 lastmaxf = (maxf < lastminf ? lastminf : maxf); | |
401 logmaxf = log10f(maxf); | |
402 } | |
403 | |
404 if (logminf == logmaxf) return 0; | |
405 return pow(10.f, logminf + ((logmaxf - logminf) * (h - y)) / h); | |
406 | |
407 } else { | |
408 | |
409 if (minf == maxf) return 0; | |
410 return minf + ((h - y) * (maxf - minf)) / h; | |
411 } | |
412 } | |
413 | |
414 int | |
415 View::getZoomLevel() const | |
416 { | |
417 #ifdef DEBUG_VIEW_WIDGET_PAINT | |
418 // std::cout << "zoom level: " << m_zoomLevel << std::endl; | |
419 #endif | |
420 return m_zoomLevel; | |
421 } | |
422 | |
423 void | |
424 View::setZoomLevel(size_t z) | |
425 { | |
426 if (m_zoomLevel != int(z)) { | |
427 m_zoomLevel = z; | |
428 emit zoomLevelChanged(z, m_followZoom); | |
429 update(); | |
430 } | |
431 } | |
432 | |
433 bool | |
434 View::hasLightBackground() const | |
435 { | |
436 for (LayerList::const_iterator i = m_layers.begin(); | |
437 i != m_layers.end(); ++i) { | |
438 if (!(*i)->hasLightBackground()) return false; | |
439 } | |
440 return true; | |
441 } | |
442 | |
443 View::LayerProgressBar::LayerProgressBar(QWidget *parent) : | |
444 QProgressBar(parent) | |
445 { | |
446 QFont f(font()); | |
447 f.setPointSize(f.pointSize() * 8 / 10); | |
448 setFont(f); | |
449 } | |
450 | |
451 void | |
452 View::addLayer(Layer *layer) | |
453 { | |
454 delete m_cache; | |
455 m_cache = 0; | |
456 | |
457 m_layers.push_back(layer); | |
458 | |
459 m_progressBars[layer] = new LayerProgressBar(this); | |
460 m_progressBars[layer]->setMinimum(0); | |
461 m_progressBars[layer]->setMaximum(100); | |
462 m_progressBars[layer]->setMinimumWidth(80); | |
463 m_progressBars[layer]->hide(); | |
464 | |
465 connect(layer, SIGNAL(layerParametersChanged()), | |
466 this, SLOT(layerParametersChanged())); | |
467 connect(layer, SIGNAL(layerParameterRangesChanged()), | |
468 this, SLOT(layerParameterRangesChanged())); | |
469 connect(layer, SIGNAL(layerNameChanged()), | |
470 this, SLOT(layerNameChanged())); | |
471 connect(layer, SIGNAL(modelChanged()), | |
472 this, SLOT(modelChanged())); | |
473 connect(layer, SIGNAL(modelCompletionChanged()), | |
474 this, SLOT(modelCompletionChanged())); | |
475 connect(layer, SIGNAL(modelChanged(size_t, size_t)), | |
476 this, SLOT(modelChanged(size_t, size_t))); | |
477 connect(layer, SIGNAL(modelReplaced()), | |
478 this, SLOT(modelReplaced())); | |
479 | |
480 update(); | |
481 | |
482 emit propertyContainerAdded(layer); | |
483 } | |
484 | |
485 void | |
486 View::removeLayer(Layer *layer) | |
487 { | |
488 if (m_deleting) { | |
489 return; | |
490 } | |
491 | |
492 delete m_cache; | |
493 m_cache = 0; | |
494 | |
495 for (LayerList::iterator i = m_layers.begin(); i != m_layers.end(); ++i) { | |
496 if (*i == layer) { | |
497 m_layers.erase(i); | |
498 if (m_progressBars.find(layer) != m_progressBars.end()) { | |
499 delete m_progressBars[layer]; | |
500 m_progressBars.erase(layer); | |
501 } | |
502 break; | |
503 } | |
504 } | |
505 | |
506 disconnect(layer, SIGNAL(layerParametersChanged()), | |
507 this, SLOT(layerParametersChanged())); | |
508 disconnect(layer, SIGNAL(layerParameterRangesChanged()), | |
509 this, SLOT(layerParameterRangesChanged())); | |
510 disconnect(layer, SIGNAL(layerNameChanged()), | |
511 this, SLOT(layerNameChanged())); | |
512 disconnect(layer, SIGNAL(modelChanged()), | |
513 this, SLOT(modelChanged())); | |
514 disconnect(layer, SIGNAL(modelCompletionChanged()), | |
515 this, SLOT(modelCompletionChanged())); | |
516 disconnect(layer, SIGNAL(modelChanged(size_t, size_t)), | |
517 this, SLOT(modelChanged(size_t, size_t))); | |
518 disconnect(layer, SIGNAL(modelReplaced()), | |
519 this, SLOT(modelReplaced())); | |
520 | |
521 update(); | |
522 | |
523 emit propertyContainerRemoved(layer); | |
524 } | |
525 | |
526 Layer * | |
527 View::getSelectedLayer() | |
528 { | |
529 if (m_haveSelectedLayer && !m_layers.empty()) { | |
530 return getLayer(getLayerCount() - 1); | |
531 } else { | |
532 return 0; | |
533 } | |
534 } | |
535 | |
536 const Layer * | |
537 View::getSelectedLayer() const | |
538 { | |
539 return const_cast<const Layer *>(const_cast<View *>(this)->getSelectedLayer()); | |
540 } | |
541 | |
542 void | |
543 View::setViewManager(ViewManager *manager) | |
544 { | |
545 if (m_manager) { | |
546 m_manager->disconnect(this, SLOT(globalCentreFrameChanged(unsigned long))); | |
547 m_manager->disconnect(this, SLOT(viewCentreFrameChanged(View *, unsigned long))); | |
548 m_manager->disconnect(this, SLOT(viewManagerPlaybackFrameChanged(unsigned long))); | |
549 m_manager->disconnect(this, SLOT(viewZoomLevelChanged(View *, unsigned long, bool))); | |
550 m_manager->disconnect(this, SLOT(toolModeChanged())); | |
551 m_manager->disconnect(this, SLOT(selectionChanged())); | |
552 m_manager->disconnect(this, SLOT(overlayModeChanged())); | |
553 m_manager->disconnect(this, SLOT(zoomWheelsEnabledChanged())); | |
554 disconnect(m_manager, SLOT(viewCentreFrameChanged(unsigned long, bool, PlaybackFollowMode))); | |
555 disconnect(m_manager, SLOT(zoomLevelChanged(unsigned long, bool))); | |
556 } | |
557 | |
558 m_manager = manager; | |
559 | |
560 connect(m_manager, SIGNAL(globalCentreFrameChanged(unsigned long)), | |
561 this, SLOT(globalCentreFrameChanged(unsigned long))); | |
562 connect(m_manager, SIGNAL(viewCentreFrameChanged(View *, unsigned long)), | |
563 this, SLOT(viewCentreFrameChanged(View *, unsigned long))); | |
564 connect(m_manager, SIGNAL(playbackFrameChanged(unsigned long)), | |
565 this, SLOT(viewManagerPlaybackFrameChanged(unsigned long))); | |
566 | |
567 connect(m_manager, SIGNAL(viewZoomLevelChanged(View *, unsigned long, bool)), | |
568 this, SLOT(viewZoomLevelChanged(View *, unsigned long, bool))); | |
569 | |
570 connect(m_manager, SIGNAL(toolModeChanged()), | |
571 this, SLOT(toolModeChanged())); | |
572 connect(m_manager, SIGNAL(selectionChanged()), | |
573 this, SLOT(selectionChanged())); | |
574 connect(m_manager, SIGNAL(inProgressSelectionChanged()), | |
575 this, SLOT(selectionChanged())); | |
576 connect(m_manager, SIGNAL(overlayModeChanged()), | |
577 this, SLOT(overlayModeChanged())); | |
578 connect(m_manager, SIGNAL(zoomWheelsEnabledChanged()), | |
579 this, SLOT(zoomWheelsEnabledChanged())); | |
580 | |
581 connect(this, SIGNAL(centreFrameChanged(unsigned long, bool, | |
582 PlaybackFollowMode)), | |
583 m_manager, SLOT(viewCentreFrameChanged(unsigned long, bool, | |
584 PlaybackFollowMode))); | |
585 | |
586 connect(this, SIGNAL(zoomLevelChanged(unsigned long, bool)), | |
587 m_manager, SLOT(viewZoomLevelChanged(unsigned long, bool))); | |
588 | |
589 if (m_followPlay != PlaybackIgnore) { | |
590 // std::cerr << "View::setViewManager: setting centre frame to playback frame: " << m_manager->getPlaybackFrame() << std::endl; | |
591 setCentreFrame(m_manager->getPlaybackFrame(), false); | |
592 } else if (m_followPan) { | |
593 // std::cerr << "View::setViewManager: setting centre frame to global centre frame: " << m_manager->getGlobalCentreFrame() << std::endl; | |
594 setCentreFrame(m_manager->getGlobalCentreFrame(), false); | |
595 } | |
596 if (m_followZoom) setZoomLevel(m_manager->getGlobalZoom()); | |
597 | |
598 toolModeChanged(); | |
599 } | |
600 | |
601 void | |
602 View::setFollowGlobalPan(bool f) | |
603 { | |
604 m_followPan = f; | |
605 emit propertyContainerPropertyChanged(m_propertyContainer); | |
606 } | |
607 | |
608 void | |
609 View::setFollowGlobalZoom(bool f) | |
610 { | |
611 m_followZoom = f; | |
612 emit propertyContainerPropertyChanged(m_propertyContainer); | |
613 } | |
614 | |
615 void | |
616 View::drawVisibleText(QPainter &paint, int x, int y, QString text, TextStyle style) | |
617 { | |
618 if (style == OutlinedText) { | |
619 | |
620 QColor origPenColour = paint.pen().color(); | |
621 QColor penColour = origPenColour; | |
622 QColor surroundColour = Qt::white; //palette().background().color(); | |
623 | |
624 if (!hasLightBackground()) { | |
625 int h, s, v; | |
626 penColour.getHsv(&h, &s, &v); | |
627 penColour = QColor::fromHsv(h, s, 255 - v); | |
628 surroundColour = Qt::black; | |
629 } | |
630 | |
631 paint.setPen(surroundColour); | |
632 | |
633 for (int dx = -1; dx <= 1; ++dx) { | |
634 for (int dy = -1; dy <= 1; ++dy) { | |
635 if (!(dx || dy)) continue; | |
636 paint.drawText(x + dx, y + dy, text); | |
637 } | |
638 } | |
639 | |
640 paint.setPen(penColour); | |
641 | |
642 paint.drawText(x, y, text); | |
643 | |
644 paint.setPen(origPenColour); | |
645 | |
646 } else { | |
647 | |
648 std::cerr << "ERROR: View::drawVisibleText: Boxed style not yet implemented!" << std::endl; | |
649 } | |
650 } | |
651 | |
652 void | |
653 View::setPlaybackFollow(PlaybackFollowMode m) | |
654 { | |
655 m_followPlay = m; | |
656 emit propertyContainerPropertyChanged(m_propertyContainer); | |
657 } | |
658 | |
659 void | |
660 View::modelChanged() | |
661 { | |
662 QObject *obj = sender(); | |
663 | |
664 #ifdef DEBUG_VIEW_WIDGET_PAINT | |
665 std::cerr << "View(" << this << ")::modelChanged()" << std::endl; | |
666 #endif | |
667 | |
668 // If the model that has changed is not used by any of the cached | |
669 // layers, we won't need to recreate the cache | |
670 | |
671 bool recreate = false; | |
672 | |
673 bool discard; | |
674 LayerList scrollables = getScrollableBackLayers(false, discard); | |
675 for (LayerList::const_iterator i = scrollables.begin(); | |
676 i != scrollables.end(); ++i) { | |
677 if (*i == obj || (*i)->getModel() == obj) { | |
678 recreate = true; | |
679 break; | |
680 } | |
681 } | |
682 | |
683 if (recreate) { | |
684 delete m_cache; | |
685 m_cache = 0; | |
686 } | |
687 | |
688 checkProgress(obj); | |
689 | |
690 update(); | |
691 } | |
692 | |
693 void | |
694 View::modelChanged(size_t startFrame, size_t endFrame) | |
695 { | |
696 QObject *obj = sender(); | |
697 | |
698 long myStartFrame = getStartFrame(); | |
699 size_t myEndFrame = getEndFrame(); | |
700 | |
701 #ifdef DEBUG_VIEW_WIDGET_PAINT | |
702 std::cerr << "View(" << this << ")::modelChanged(" << startFrame << "," << endFrame << ") [me " << myStartFrame << "," << myEndFrame << "]" << std::endl; | |
703 #endif | |
704 | |
705 if (myStartFrame > 0 && endFrame < size_t(myStartFrame)) { | |
706 checkProgress(obj); | |
707 return; | |
708 } | |
709 if (startFrame > myEndFrame) { | |
710 checkProgress(obj); | |
711 return; | |
712 } | |
713 | |
714 // If the model that has changed is not used by any of the cached | |
715 // layers, we won't need to recreate the cache | |
716 | |
717 bool recreate = false; | |
718 | |
719 bool discard; | |
720 LayerList scrollables = getScrollableBackLayers(false, discard); | |
721 for (LayerList::const_iterator i = scrollables.begin(); | |
722 i != scrollables.end(); ++i) { | |
723 if (*i == obj || (*i)->getModel() == obj) { | |
724 recreate = true; | |
725 break; | |
726 } | |
727 } | |
728 | |
729 if (recreate) { | |
730 delete m_cache; | |
731 m_cache = 0; | |
732 } | |
733 | |
734 if (long(startFrame) < myStartFrame) startFrame = myStartFrame; | |
735 if (endFrame > myEndFrame) endFrame = myEndFrame; | |
736 | |
737 checkProgress(obj); | |
738 | |
739 update(); | |
740 } | |
741 | |
742 void | |
743 View::modelCompletionChanged() | |
744 { | |
745 QObject *obj = sender(); | |
746 checkProgress(obj); | |
747 } | |
748 | |
749 void | |
750 View::modelReplaced() | |
751 { | |
752 #ifdef DEBUG_VIEW_WIDGET_PAINT | |
753 std::cerr << "View(" << this << ")::modelReplaced()" << std::endl; | |
754 #endif | |
755 delete m_cache; | |
756 m_cache = 0; | |
757 | |
758 update(); | |
759 } | |
760 | |
761 void | |
762 View::layerParametersChanged() | |
763 { | |
764 Layer *layer = dynamic_cast<Layer *>(sender()); | |
765 | |
766 #ifdef DEBUG_VIEW_WIDGET_PAINT | |
767 std::cerr << "View::layerParametersChanged()" << std::endl; | |
768 #endif | |
769 | |
770 delete m_cache; | |
771 m_cache = 0; | |
772 update(); | |
773 | |
774 if (layer) { | |
775 emit propertyContainerPropertyChanged(layer); | |
776 } | |
777 } | |
778 | |
779 void | |
780 View::layerParameterRangesChanged() | |
781 { | |
782 Layer *layer = dynamic_cast<Layer *>(sender()); | |
783 if (layer) emit propertyContainerPropertyRangeChanged(layer); | |
784 } | |
785 | |
786 void | |
787 View::layerNameChanged() | |
788 { | |
789 Layer *layer = dynamic_cast<Layer *>(sender()); | |
790 if (layer) emit propertyContainerNameChanged(layer); | |
791 } | |
792 | |
793 void | |
794 View::globalCentreFrameChanged(unsigned long f) | |
795 { | |
796 if (m_followPan) { | |
797 setCentreFrame(f, false); | |
798 } | |
799 } | |
800 | |
801 void | |
802 View::viewCentreFrameChanged(View *, unsigned long ) | |
803 { | |
804 // We do nothing with this, but a subclass might | |
805 } | |
806 | |
807 void | |
808 View::viewManagerPlaybackFrameChanged(unsigned long f) | |
809 { | |
810 if (m_manager) { | |
811 if (sender() != m_manager) return; | |
812 } | |
813 | |
814 if (m_playPointerFrame == f) return; | |
815 bool visible = (getXForFrame(m_playPointerFrame) != getXForFrame(f)); | |
816 size_t oldPlayPointerFrame = m_playPointerFrame; | |
817 m_playPointerFrame = f; | |
818 if (!visible) return; | |
819 | |
820 switch (m_followPlay) { | |
821 | |
822 case PlaybackScrollContinuous: | |
823 if (QApplication::mouseButtons() == Qt::NoButton) { | |
824 setCentreFrame(f, false); | |
825 } | |
826 break; | |
827 | |
828 case PlaybackScrollPage: | |
829 { | |
830 int xold = getXForFrame(oldPlayPointerFrame); | |
831 update(xold - 1, 0, 3, height()); | |
832 | |
833 long w = getEndFrame() - getStartFrame(); | |
834 w -= w/5; | |
835 long sf = (f / w) * w - w/8; | |
836 | |
837 if (m_manager && | |
838 m_manager->isPlaying() && | |
839 m_manager->getPlaySelectionMode()) { | |
840 MultiSelection::SelectionList selections = m_manager->getSelections(); | |
841 if (!selections.empty()) { | |
842 size_t selectionStart = selections.begin()->getStartFrame(); | |
843 if (sf < long(selectionStart) - w / 10) { | |
844 sf = long(selectionStart) - w / 10; | |
845 } | |
846 } | |
847 } | |
848 | |
849 #ifdef DEBUG_VIEW_WIDGET_PAINT | |
850 std::cerr << "PlaybackScrollPage: f = " << f << ", sf = " << sf << ", start frame " | |
851 << getStartFrame() << std::endl; | |
852 #endif | |
853 | |
854 // We don't consider scrolling unless the pointer is outside | |
855 // the clearly visible range already | |
856 | |
857 int xnew = getXForFrame(m_playPointerFrame); | |
858 | |
859 #ifdef DEBUG_VIEW_WIDGET_PAINT | |
860 std::cerr << "xnew = " << xnew << ", width = " << width() << std::endl; | |
861 #endif | |
862 | |
863 if (xnew < width()/8 || xnew > (width()*7)/8) { | |
864 if (QApplication::mouseButtons() == Qt::NoButton) { | |
865 long offset = getFrameForX(width()/2) - getStartFrame(); | |
866 long newCentre = sf + offset; | |
867 bool changed = setCentreFrame(newCentre, false); | |
868 if (changed) { | |
869 xold = getXForFrame(oldPlayPointerFrame); | |
870 update(xold - 1, 0, 3, height()); | |
871 } | |
872 } | |
873 } | |
874 | |
875 update(xnew - 1, 0, 3, height()); | |
876 | |
877 break; | |
878 } | |
879 | |
880 case PlaybackIgnore: | |
881 if (long(f) >= getStartFrame() && f < getEndFrame()) { | |
882 update(); | |
883 } | |
884 break; | |
885 } | |
886 } | |
887 | |
888 void | |
889 View::viewZoomLevelChanged(View *p, unsigned long z, bool locked) | |
890 { | |
891 #ifdef DEBUG_VIEW_WIDGET_PAINT | |
892 std::cerr << "View[" << this << "]: viewZoomLevelChanged(" << p << ", " << z << ", " << locked << ")" << std::endl; | |
893 #endif | |
894 if (m_followZoom && p != this && locked) { | |
895 setZoomLevel(z); | |
896 } | |
897 } | |
898 | |
899 void | |
900 View::selectionChanged() | |
901 { | |
902 if (m_selectionCached) { | |
903 delete m_cache; | |
904 m_cache = 0; | |
905 m_selectionCached = false; | |
906 } | |
907 update(); | |
908 } | |
909 | |
910 size_t | |
911 View::getFirstVisibleFrame() const | |
912 { | |
913 long f0 = getStartFrame(); | |
914 size_t f = getModelsStartFrame(); | |
915 if (f0 < 0 || f0 < long(f)) return f; | |
916 return f0; | |
917 } | |
918 | |
919 size_t | |
920 View::getLastVisibleFrame() const | |
921 { | |
922 size_t f0 = getEndFrame(); | |
923 size_t f = getModelsEndFrame(); | |
924 if (f0 > f) return f; | |
925 return f0; | |
926 } | |
927 | |
928 size_t | |
929 View::getModelsStartFrame() const | |
930 { | |
931 bool first = true; | |
932 size_t startFrame = 0; | |
933 | |
934 for (LayerList::const_iterator i = m_layers.begin(); i != m_layers.end(); ++i) { | |
935 | |
936 if ((*i)->getModel() && (*i)->getModel()->isOK()) { | |
937 | |
938 size_t thisStartFrame = (*i)->getModel()->getStartFrame(); | |
939 | |
940 if (first || thisStartFrame < startFrame) { | |
941 startFrame = thisStartFrame; | |
942 } | |
943 first = false; | |
944 } | |
945 } | |
946 return startFrame; | |
947 } | |
948 | |
949 size_t | |
950 View::getModelsEndFrame() const | |
951 { | |
952 bool first = true; | |
953 size_t endFrame = 0; | |
954 | |
955 for (LayerList::const_iterator i = m_layers.begin(); i != m_layers.end(); ++i) { | |
956 | |
957 if ((*i)->getModel() && (*i)->getModel()->isOK()) { | |
958 | |
959 size_t thisEndFrame = (*i)->getModel()->getEndFrame(); | |
960 | |
961 if (first || thisEndFrame > endFrame) { | |
962 endFrame = thisEndFrame; | |
963 } | |
964 first = false; | |
965 } | |
966 } | |
967 | |
968 if (first) return getModelsStartFrame(); | |
969 return endFrame; | |
970 } | |
971 | |
972 int | |
973 View::getModelsSampleRate() const | |
974 { | |
975 //!!! Just go for the first, for now. If we were supporting | |
976 // multiple samplerates, we'd probably want to do frame/time | |
977 // conversion in the model | |
978 | |
979 //!!! nah, this wants to always return the sr of the main model! | |
980 | |
981 for (LayerList::const_iterator i = m_layers.begin(); i != m_layers.end(); ++i) { | |
982 if ((*i)->getModel() && (*i)->getModel()->isOK()) { | |
983 return (*i)->getModel()->getSampleRate(); | |
984 } | |
985 } | |
986 return 0; | |
987 } | |
988 | |
989 bool | |
990 View::areLayersScrollable() const | |
991 { | |
992 // True iff all views are scrollable | |
993 for (LayerList::const_iterator i = m_layers.begin(); i != m_layers.end(); ++i) { | |
994 if (!(*i)->isLayerScrollable(this)) return false; | |
995 } | |
996 return true; | |
997 } | |
998 | |
999 View::LayerList | |
1000 View::getScrollableBackLayers(bool testChanged, bool &changed) const | |
1001 { | |
1002 changed = false; | |
1003 | |
1004 // We want a list of all the scrollable layers that are behind the | |
1005 // backmost non-scrollable layer. | |
1006 | |
1007 LayerList scrollables; | |
1008 bool metUnscrollable = false; | |
1009 | |
1010 for (LayerList::const_iterator i = m_layers.begin(); i != m_layers.end(); ++i) { | |
1011 // std::cerr << "View::getScrollableBackLayers: calling isLayerDormant on layer " << *i << std::endl; | |
1012 // std::cerr << "(name is " << (*i)->objectName().toStdString() << ")" | |
1013 // << std::endl; | |
1014 // std::cerr << "View::getScrollableBackLayers: I am " << this << std::endl; | |
1015 if ((*i)->isLayerDormant(this)) continue; | |
1016 if ((*i)->isLayerOpaque()) { | |
1017 // You can't see anything behind an opaque layer! | |
1018 scrollables.clear(); | |
1019 if (metUnscrollable) break; | |
1020 } | |
1021 if (!metUnscrollable && (*i)->isLayerScrollable(this)) { | |
1022 scrollables.push_back(*i); | |
1023 } else { | |
1024 metUnscrollable = true; | |
1025 } | |
1026 } | |
1027 | |
1028 if (testChanged && scrollables != m_lastScrollableBackLayers) { | |
1029 m_lastScrollableBackLayers = scrollables; | |
1030 changed = true; | |
1031 } | |
1032 return scrollables; | |
1033 } | |
1034 | |
1035 View::LayerList | |
1036 View::getNonScrollableFrontLayers(bool testChanged, bool &changed) const | |
1037 { | |
1038 changed = false; | |
1039 LayerList nonScrollables; | |
1040 | |
1041 // Everything in front of the first non-scrollable from the back | |
1042 // should also be considered non-scrollable | |
1043 | |
1044 bool started = false; | |
1045 | |
1046 for (LayerList::const_iterator i = m_layers.begin(); i != m_layers.end(); ++i) { | |
1047 if ((*i)->isLayerDormant(this)) continue; | |
1048 if (!started && (*i)->isLayerScrollable(this)) { | |
1049 continue; | |
1050 } | |
1051 started = true; | |
1052 if ((*i)->isLayerOpaque()) { | |
1053 // You can't see anything behind an opaque layer! | |
1054 nonScrollables.clear(); | |
1055 } | |
1056 nonScrollables.push_back(*i); | |
1057 } | |
1058 | |
1059 if (testChanged && nonScrollables != m_lastNonScrollableBackLayers) { | |
1060 m_lastNonScrollableBackLayers = nonScrollables; | |
1061 changed = true; | |
1062 } | |
1063 | |
1064 return nonScrollables; | |
1065 } | |
1066 | |
1067 size_t | |
1068 View::getZoomConstraintBlockSize(size_t blockSize, | |
1069 ZoomConstraint::RoundingDirection dir) | |
1070 const | |
1071 { | |
1072 size_t candidate = blockSize; | |
1073 bool haveCandidate = false; | |
1074 | |
1075 PowerOfSqrtTwoZoomConstraint defaultZoomConstraint; | |
1076 | |
1077 for (LayerList::const_iterator i = m_layers.begin(); i != m_layers.end(); ++i) { | |
1078 | |
1079 const ZoomConstraint *zoomConstraint = (*i)->getZoomConstraint(); | |
1080 if (!zoomConstraint) zoomConstraint = &defaultZoomConstraint; | |
1081 | |
1082 size_t thisBlockSize = | |
1083 zoomConstraint->getNearestBlockSize(blockSize, dir); | |
1084 | |
1085 // Go for the block size that's furthest from the one | |
1086 // passed in. Most of the time, that's what we want. | |
1087 if (!haveCandidate || | |
1088 (thisBlockSize > blockSize && thisBlockSize > candidate) || | |
1089 (thisBlockSize < blockSize && thisBlockSize < candidate)) { | |
1090 candidate = thisBlockSize; | |
1091 haveCandidate = true; | |
1092 } | |
1093 } | |
1094 | |
1095 return candidate; | |
1096 } | |
1097 | |
1098 bool | |
1099 View::areLayerColoursSignificant() const | |
1100 { | |
1101 for (LayerList::const_iterator i = m_layers.begin(); i != m_layers.end(); ++i) { | |
1102 if ((*i)->isLayerColourSignificant()) return true; | |
1103 if ((*i)->isLayerOpaque()) break; | |
1104 } | |
1105 return false; | |
1106 } | |
1107 | |
1108 bool | |
1109 View::hasTopLayerTimeXAxis() const | |
1110 { | |
1111 LayerList::const_iterator i = m_layers.end(); | |
1112 if (i == m_layers.begin()) return false; | |
1113 --i; | |
1114 return (*i)->hasTimeXAxis(); | |
1115 } | |
1116 | |
1117 void | |
1118 View::zoom(bool in) | |
1119 { | |
1120 int newZoomLevel = m_zoomLevel; | |
1121 | |
1122 if (in) { | |
1123 newZoomLevel = getZoomConstraintBlockSize(newZoomLevel - 1, | |
1124 ZoomConstraint::RoundDown); | |
1125 } else { | |
1126 newZoomLevel = getZoomConstraintBlockSize(newZoomLevel + 1, | |
1127 ZoomConstraint::RoundUp); | |
1128 } | |
1129 | |
1130 if (newZoomLevel != m_zoomLevel) { | |
1131 setZoomLevel(newZoomLevel); | |
1132 } | |
1133 } | |
1134 | |
1135 void | |
1136 View::scroll(bool right, bool lots) | |
1137 { | |
1138 long delta; | |
1139 if (lots) { | |
1140 delta = (getEndFrame() - getStartFrame()) / 2; | |
1141 } else { | |
1142 delta = (getEndFrame() - getStartFrame()) / 20; | |
1143 } | |
1144 if (right) delta = -delta; | |
1145 | |
1146 if (int(m_centreFrame) < delta) { | |
1147 setCentreFrame(0); | |
1148 } else if (int(m_centreFrame) - delta >= int(getModelsEndFrame())) { | |
1149 setCentreFrame(getModelsEndFrame()); | |
1150 } else { | |
1151 setCentreFrame(m_centreFrame - delta); | |
1152 } | |
1153 } | |
1154 | |
1155 void | |
1156 View::checkProgress(void *object) | |
1157 { | |
1158 if (!m_showProgress) return; | |
1159 | |
1160 int ph = height(); | |
1161 | |
1162 for (ProgressMap::const_iterator i = m_progressBars.begin(); | |
1163 i != m_progressBars.end(); ++i) { | |
1164 | |
1165 if (i->first == object) { | |
1166 | |
1167 int completion = i->first->getCompletion(this); | |
1168 | |
1169 if (completion >= 100) { | |
1170 | |
1171 i->second->hide(); | |
1172 | |
1173 } else { | |
1174 | |
1175 i->second->setText(i->first->getPropertyContainerName()); | |
1176 | |
1177 i->second->setValue(completion); | |
1178 i->second->move(0, ph - i->second->height()); | |
1179 | |
1180 i->second->show(); | |
1181 i->second->update(); | |
1182 | |
1183 ph -= i->second->height(); | |
1184 } | |
1185 } else { | |
1186 if (i->second->isVisible()) { | |
1187 ph -= i->second->height(); | |
1188 } | |
1189 } | |
1190 } | |
1191 } | |
1192 | |
1193 void | |
1194 View::paintEvent(QPaintEvent *e) | |
1195 { | |
1196 // Profiler prof("View::paintEvent", false); | |
1197 // std::cerr << "View::paintEvent: centre frame is " << m_centreFrame << std::endl; | |
1198 | |
1199 if (m_layers.empty()) { | |
1200 QFrame::paintEvent(e); | |
1201 return; | |
1202 } | |
1203 | |
1204 // ensure our constraints are met | |
1205 | |
1206 /*!!! Should we do this only if we have layers that can't support other | |
1207 zoom levels? | |
1208 | |
1209 m_zoomLevel = getZoomConstraintBlockSize(m_zoomLevel, | |
1210 ZoomConstraint::RoundUp); | |
1211 */ | |
1212 | |
1213 QPainter paint; | |
1214 bool repaintCache = false; | |
1215 bool paintedCacheRect = false; | |
1216 | |
1217 QRect cacheRect(rect()); | |
1218 | |
1219 if (e) { | |
1220 cacheRect &= e->rect(); | |
1221 #ifdef DEBUG_VIEW_WIDGET_PAINT | |
1222 std::cerr << "paint rect " << cacheRect.width() << "x" << cacheRect.height() | |
1223 << ", my rect " << width() << "x" << height() << std::endl; | |
1224 #endif | |
1225 } | |
1226 | |
1227 QRect nonCacheRect(cacheRect); | |
1228 | |
1229 // If not all layers are scrollable, but some of the back layers | |
1230 // are, we should store only those in the cache. | |
1231 | |
1232 bool layersChanged = false; | |
1233 LayerList scrollables = getScrollableBackLayers(true, layersChanged); | |
1234 LayerList nonScrollables = getNonScrollableFrontLayers(true, layersChanged); | |
1235 bool selectionCacheable = nonScrollables.empty(); | |
1236 bool haveSelections = m_manager && !m_manager->getSelections().empty(); | |
1237 bool selectionDrawn = false; | |
1238 | |
1239 // If all the non-scrollable layers are non-opaque, then we draw | |
1240 // the selection rectangle behind them and cache it. If any are | |
1241 // opaque, however, we can't cache. | |
1242 // | |
1243 if (!selectionCacheable) { | |
1244 selectionCacheable = true; | |
1245 for (LayerList::const_iterator i = nonScrollables.begin(); | |
1246 i != nonScrollables.end(); ++i) { | |
1247 if ((*i)->isLayerOpaque()) { | |
1248 selectionCacheable = false; | |
1249 break; | |
1250 } | |
1251 } | |
1252 } | |
1253 | |
1254 if (selectionCacheable) { | |
1255 QPoint localPos; | |
1256 bool closeToLeft, closeToRight; | |
1257 if (shouldIlluminateLocalSelection(localPos, closeToLeft, closeToRight)) { | |
1258 selectionCacheable = false; | |
1259 } | |
1260 } | |
1261 | |
1262 #ifdef DEBUG_VIEW_WIDGET_PAINT | |
1263 std::cerr << "View(" << this << ")::paintEvent: have " << scrollables.size() | |
1264 << " scrollable back layers and " << nonScrollables.size() | |
1265 << " non-scrollable front layers" << std::endl; | |
1266 std::cerr << "haveSelections " << haveSelections << ", selectionCacheable " | |
1267 << selectionCacheable << ", m_selectionCached " << m_selectionCached << std::endl; | |
1268 #endif | |
1269 | |
1270 if (layersChanged || scrollables.empty() || | |
1271 (haveSelections && (selectionCacheable != m_selectionCached))) { | |
1272 delete m_cache; | |
1273 m_cache = 0; | |
1274 m_selectionCached = false; | |
1275 } | |
1276 | |
1277 if (!scrollables.empty()) { | |
1278 | |
1279 #ifdef DEBUG_VIEW_WIDGET_PAINT | |
1280 std::cerr << "View(" << this << "): cache " << m_cache << ", cache zoom " | |
1281 << m_cacheZoomLevel << ", zoom " << m_zoomLevel << std::endl; | |
1282 #endif | |
1283 | |
1284 if (!m_cache || | |
1285 m_cacheZoomLevel != m_zoomLevel || | |
1286 width() != m_cache->width() || | |
1287 height() != m_cache->height()) { | |
1288 | |
1289 // cache is not valid | |
1290 | |
1291 if (cacheRect.width() < width()/10) { | |
1292 delete m_cache; | |
1293 m_cache = 0; | |
1294 #ifdef DEBUG_VIEW_WIDGET_PAINT | |
1295 std::cerr << "View(" << this << ")::paintEvent: small repaint, not bothering to recreate cache" << std::endl; | |
1296 #endif | |
1297 } else { | |
1298 delete m_cache; | |
1299 m_cache = new QPixmap(width(), height()); | |
1300 #ifdef DEBUG_VIEW_WIDGET_PAINT | |
1301 std::cerr << "View(" << this << ")::paintEvent: recreated cache" << std::endl; | |
1302 #endif | |
1303 cacheRect = rect(); | |
1304 repaintCache = true; | |
1305 } | |
1306 | |
1307 } else if (m_cacheCentreFrame != m_centreFrame) { | |
1308 | |
1309 long dx = | |
1310 getXForFrame(m_cacheCentreFrame) - | |
1311 getXForFrame(m_centreFrame); | |
1312 | |
1313 if (dx > -width() && dx < width()) { | |
1314 #if defined(Q_WS_WIN32) || defined(Q_WS_MAC) | |
1315 // Copying a pixmap to itself doesn't work properly on Windows | |
1316 // or Mac (it only works when moving in one direction) | |
1317 static QPixmap *tmpPixmap = 0; | |
1318 if (!tmpPixmap || | |
1319 tmpPixmap->width() != width() || | |
1320 tmpPixmap->height() != height()) { | |
1321 delete tmpPixmap; | |
1322 tmpPixmap = new QPixmap(width(), height()); | |
1323 } | |
1324 paint.begin(tmpPixmap); | |
1325 paint.drawPixmap(0, 0, *m_cache); | |
1326 paint.end(); | |
1327 paint.begin(m_cache); | |
1328 paint.drawPixmap(dx, 0, *tmpPixmap); | |
1329 paint.end(); | |
1330 #else | |
1331 // But it seems to be fine on X11 | |
1332 paint.begin(m_cache); | |
1333 paint.drawPixmap(dx, 0, *m_cache); | |
1334 paint.end(); | |
1335 #endif | |
1336 | |
1337 if (dx < 0) { | |
1338 cacheRect = QRect(width() + dx, 0, -dx, height()); | |
1339 } else { | |
1340 cacheRect = QRect(0, 0, dx, height()); | |
1341 } | |
1342 #ifdef DEBUG_VIEW_WIDGET_PAINT | |
1343 std::cerr << "View(" << this << ")::paintEvent: scrolled cache by " << dx << std::endl; | |
1344 #endif | |
1345 } else { | |
1346 cacheRect = rect(); | |
1347 #ifdef DEBUG_VIEW_WIDGET_PAINT | |
1348 std::cerr << "View(" << this << ")::paintEvent: scrolling too far" << std::endl; | |
1349 #endif | |
1350 } | |
1351 repaintCache = true; | |
1352 | |
1353 } else { | |
1354 #ifdef DEBUG_VIEW_WIDGET_PAINT | |
1355 std::cerr << "View(" << this << ")::paintEvent: cache is good" << std::endl; | |
1356 #endif | |
1357 paint.begin(this); | |
1358 paint.drawPixmap(cacheRect, *m_cache, cacheRect); | |
1359 paint.end(); | |
1360 QFrame::paintEvent(e); | |
1361 paintedCacheRect = true; | |
1362 } | |
1363 | |
1364 m_cacheCentreFrame = m_centreFrame; | |
1365 m_cacheZoomLevel = m_zoomLevel; | |
1366 } | |
1367 | |
1368 #ifdef DEBUG_VIEW_WIDGET_PAINT | |
1369 // std::cerr << "View(" << this << ")::paintEvent: cacheRect " << cacheRect << ", nonCacheRect " << (nonCacheRect | cacheRect) << ", repaintCache " << repaintCache << ", paintedCacheRect " << paintedCacheRect << std::endl; | |
1370 #endif | |
1371 | |
1372 // Scrollable (cacheable) items first | |
1373 | |
1374 if (!paintedCacheRect) { | |
1375 | |
1376 if (repaintCache) paint.begin(m_cache); | |
1377 else paint.begin(this); | |
1378 | |
1379 paint.setClipRect(cacheRect); | |
1380 | |
1381 if (hasLightBackground()) { | |
1382 paint.setPen(Qt::white); | |
1383 paint.setBrush(Qt::white); | |
1384 } else { | |
1385 paint.setPen(Qt::black); | |
1386 paint.setBrush(Qt::black); | |
1387 } | |
1388 paint.drawRect(cacheRect); | |
1389 | |
1390 paint.setPen(Qt::black); | |
1391 paint.setBrush(Qt::NoBrush); | |
1392 | |
1393 for (LayerList::iterator i = scrollables.begin(); i != scrollables.end(); ++i) { | |
1394 paint.setRenderHint(QPainter::Antialiasing, false); | |
1395 paint.save(); | |
1396 (*i)->paint(this, paint, cacheRect); | |
1397 paint.restore(); | |
1398 } | |
1399 | |
1400 if (haveSelections && selectionCacheable) { | |
1401 drawSelections(paint); | |
1402 m_selectionCached = repaintCache; | |
1403 selectionDrawn = true; | |
1404 } | |
1405 | |
1406 paint.end(); | |
1407 | |
1408 if (repaintCache) { | |
1409 cacheRect |= (e ? e->rect() : rect()); | |
1410 paint.begin(this); | |
1411 paint.drawPixmap(cacheRect, *m_cache, cacheRect); | |
1412 paint.end(); | |
1413 } | |
1414 } | |
1415 | |
1416 // Now non-cacheable items. We always need to redraw the | |
1417 // non-cacheable items across at least the area we drew of the | |
1418 // cacheable items. | |
1419 | |
1420 nonCacheRect |= cacheRect; | |
1421 | |
1422 paint.begin(this); | |
1423 paint.setClipRect(nonCacheRect); | |
1424 | |
1425 if (scrollables.empty()) { | |
1426 if (hasLightBackground()) { | |
1427 paint.setPen(Qt::white); | |
1428 paint.setBrush(Qt::white); | |
1429 } else { | |
1430 paint.setPen(Qt::black); | |
1431 paint.setBrush(Qt::black); | |
1432 } | |
1433 paint.drawRect(nonCacheRect); | |
1434 } | |
1435 | |
1436 paint.setPen(Qt::black); | |
1437 paint.setBrush(Qt::NoBrush); | |
1438 | |
1439 for (LayerList::iterator i = nonScrollables.begin(); i != nonScrollables.end(); ++i) { | |
1440 // Profiler profiler2("View::paintEvent non-cacheable"); | |
1441 (*i)->paint(this, paint, nonCacheRect); | |
1442 } | |
1443 | |
1444 paint.end(); | |
1445 | |
1446 paint.begin(this); | |
1447 if (e) paint.setClipRect(e->rect()); | |
1448 if (!m_selectionCached) { | |
1449 drawSelections(paint); | |
1450 } | |
1451 paint.end(); | |
1452 | |
1453 bool showPlayPointer = true; | |
1454 if (m_followPlay == PlaybackScrollContinuous) { | |
1455 showPlayPointer = false; | |
1456 } else if (long(m_playPointerFrame) <= getStartFrame() || | |
1457 m_playPointerFrame >= getEndFrame()) { | |
1458 showPlayPointer = false; | |
1459 } else if (m_manager && !m_manager->isPlaying()) { | |
1460 if (m_playPointerFrame == getCentreFrame() && | |
1461 m_followPlay != PlaybackIgnore) { | |
1462 showPlayPointer = false; | |
1463 } | |
1464 } | |
1465 | |
1466 if (showPlayPointer) { | |
1467 | |
1468 paint.begin(this); | |
1469 | |
1470 int playx = getXForFrame(m_playPointerFrame); | |
1471 | |
1472 paint.setPen(Qt::black); | |
1473 paint.drawLine(playx - 1, 0, playx - 1, height() - 1); | |
1474 paint.drawLine(playx + 1, 0, playx + 1, height() - 1); | |
1475 paint.drawPoint(playx, 0); | |
1476 paint.drawPoint(playx, height() - 1); | |
1477 paint.setPen(Qt::white); | |
1478 paint.drawLine(playx, 1, playx, height() - 2); | |
1479 | |
1480 paint.end(); | |
1481 } | |
1482 | |
1483 QFrame::paintEvent(e); | |
1484 } | |
1485 | |
1486 void | |
1487 View::drawSelections(QPainter &paint) | |
1488 { | |
1489 if (!hasTopLayerTimeXAxis()) return; | |
1490 | |
1491 MultiSelection::SelectionList selections; | |
1492 | |
1493 if (m_manager) { | |
1494 selections = m_manager->getSelections(); | |
1495 if (m_manager->haveInProgressSelection()) { | |
1496 bool exclusive; | |
1497 Selection inProgressSelection = | |
1498 m_manager->getInProgressSelection(exclusive); | |
1499 if (exclusive) selections.clear(); | |
1500 selections.insert(inProgressSelection); | |
1501 } | |
1502 } | |
1503 | |
1504 paint.save(); | |
1505 | |
1506 bool translucent = !areLayerColoursSignificant(); | |
1507 | |
1508 if (translucent) { | |
1509 paint.setBrush(QColor(150, 150, 255, 80)); | |
1510 } else { | |
1511 paint.setBrush(Qt::NoBrush); | |
1512 } | |
1513 | |
1514 int sampleRate = getModelsSampleRate(); | |
1515 | |
1516 QPoint localPos; | |
1517 long illuminateFrame = -1; | |
1518 bool closeToLeft, closeToRight; | |
1519 | |
1520 if (shouldIlluminateLocalSelection(localPos, closeToLeft, closeToRight)) { | |
1521 illuminateFrame = getFrameForX(localPos.x()); | |
1522 } | |
1523 | |
1524 const QFontMetrics &metrics = paint.fontMetrics(); | |
1525 | |
1526 for (MultiSelection::SelectionList::iterator i = selections.begin(); | |
1527 i != selections.end(); ++i) { | |
1528 | |
1529 int p0 = getXForFrame(i->getStartFrame()); | |
1530 int p1 = getXForFrame(i->getEndFrame()); | |
1531 | |
1532 if (p1 < 0 || p0 > width()) continue; | |
1533 | |
1534 #ifdef DEBUG_VIEW_WIDGET_PAINT | |
1535 std::cerr << "View::drawSelections: " << p0 << ",-1 [" << (p1-p0) << "x" << (height()+1) << "]" << std::endl; | |
1536 #endif | |
1537 | |
1538 bool illuminateThis = | |
1539 (illuminateFrame >= 0 && i->contains(illuminateFrame)); | |
1540 | |
1541 paint.setPen(QColor(150, 150, 255)); | |
1542 | |
1543 if (translucent && shouldLabelSelections()) { | |
1544 paint.drawRect(p0, -1, p1 - p0, height() + 1); | |
1545 } else { | |
1546 // Make the top & bottom lines of the box visible if we | |
1547 // are lacking some of the other visual cues. There's no | |
1548 // particular logic to this, it's just a question of what | |
1549 // I happen to think looks nice. | |
1550 paint.drawRect(p0, 0, p1 - p0, height() - 1); | |
1551 } | |
1552 | |
1553 if (illuminateThis) { | |
1554 paint.save(); | |
1555 if (hasLightBackground()) { | |
1556 paint.setPen(QPen(Qt::black, 2)); | |
1557 } else { | |
1558 paint.setPen(QPen(Qt::white, 2)); | |
1559 } | |
1560 if (closeToLeft) { | |
1561 paint.drawLine(p0, 1, p1, 1); | |
1562 paint.drawLine(p0, 0, p0, height()); | |
1563 paint.drawLine(p0, height() - 1, p1, height() - 1); | |
1564 } else if (closeToRight) { | |
1565 paint.drawLine(p0, 1, p1, 1); | |
1566 paint.drawLine(p1, 0, p1, height()); | |
1567 paint.drawLine(p0, height() - 1, p1, height() - 1); | |
1568 } else { | |
1569 paint.setBrush(Qt::NoBrush); | |
1570 paint.drawRect(p0, 1, p1 - p0, height() - 2); | |
1571 } | |
1572 paint.restore(); | |
1573 } | |
1574 | |
1575 if (sampleRate && shouldLabelSelections() && m_manager && | |
1576 m_manager->shouldShowSelectionExtents()) { | |
1577 | |
1578 QString startText = QString("%1 / %2") | |
1579 .arg(QString::fromStdString | |
1580 (RealTime::frame2RealTime | |
1581 (i->getStartFrame(), sampleRate).toText(true))) | |
1582 .arg(i->getStartFrame()); | |
1583 | |
1584 QString endText = QString(" %1 / %2") | |
1585 .arg(QString::fromStdString | |
1586 (RealTime::frame2RealTime | |
1587 (i->getEndFrame(), sampleRate).toText(true))) | |
1588 .arg(i->getEndFrame()); | |
1589 | |
1590 QString durationText = QString("(%1 / %2) ") | |
1591 .arg(QString::fromStdString | |
1592 (RealTime::frame2RealTime | |
1593 (i->getEndFrame() - i->getStartFrame(), sampleRate) | |
1594 .toText(true))) | |
1595 .arg(i->getEndFrame() - i->getStartFrame()); | |
1596 | |
1597 int sw = metrics.width(startText), | |
1598 ew = metrics.width(endText), | |
1599 dw = metrics.width(durationText); | |
1600 | |
1601 int sy = metrics.ascent() + metrics.height() + 4; | |
1602 int ey = sy; | |
1603 int dy = sy + metrics.height(); | |
1604 | |
1605 int sx = p0 + 2; | |
1606 int ex = sx; | |
1607 int dx = sx; | |
1608 | |
1609 if (sw + ew > (p1 - p0)) { | |
1610 ey += metrics.height(); | |
1611 dy += metrics.height(); | |
1612 } | |
1613 | |
1614 if (ew < (p1 - p0)) { | |
1615 ex = p1 - 2 - ew; | |
1616 } | |
1617 | |
1618 if (dw < (p1 - p0)) { | |
1619 dx = p1 - 2 - dw; | |
1620 } | |
1621 | |
1622 paint.drawText(sx, sy, startText); | |
1623 paint.drawText(ex, ey, endText); | |
1624 paint.drawText(dx, dy, durationText); | |
1625 } | |
1626 } | |
1627 | |
1628 paint.restore(); | |
1629 } | |
1630 | |
1631 bool | |
1632 View::render(QPainter &paint, int xorigin, size_t f0, size_t f1) | |
1633 { | |
1634 size_t x0 = f0 / m_zoomLevel; | |
1635 size_t x1 = f1 / m_zoomLevel; | |
1636 | |
1637 size_t w = x1 - x0; | |
1638 | |
1639 size_t origCentreFrame = m_centreFrame; | |
1640 | |
1641 bool someLayersIncomplete = false; | |
1642 | |
1643 for (LayerList::iterator i = m_layers.begin(); | |
1644 i != m_layers.end(); ++i) { | |
1645 | |
1646 int c = (*i)->getCompletion(this); | |
1647 if (c < 100) { | |
1648 someLayersIncomplete = true; | |
1649 break; | |
1650 } | |
1651 } | |
1652 | |
1653 if (someLayersIncomplete) { | |
1654 | |
1655 QProgressDialog progress(tr("Waiting for layers to be ready..."), | |
1656 tr("Cancel"), 0, 100, this); | |
1657 | |
1658 int layerCompletion = 0; | |
1659 | |
1660 while (layerCompletion < 100) { | |
1661 | |
1662 for (LayerList::iterator i = m_layers.begin(); | |
1663 i != m_layers.end(); ++i) { | |
1664 | |
1665 int c = (*i)->getCompletion(this); | |
1666 if (i == m_layers.begin() || c < layerCompletion) { | |
1667 layerCompletion = c; | |
1668 } | |
1669 } | |
1670 | |
1671 if (layerCompletion >= 100) break; | |
1672 | |
1673 progress.setValue(layerCompletion); | |
1674 qApp->processEvents(); | |
1675 if (progress.wasCanceled()) { | |
1676 update(); | |
1677 return false; | |
1678 } | |
1679 | |
1680 usleep(50000); | |
1681 } | |
1682 } | |
1683 | |
1684 QProgressDialog progress(tr("Rendering image..."), | |
1685 tr("Cancel"), 0, w / width(), this); | |
1686 | |
1687 for (size_t x = 0; x < w; x += width()) { | |
1688 | |
1689 progress.setValue(x / width()); | |
1690 qApp->processEvents(); | |
1691 if (progress.wasCanceled()) { | |
1692 m_centreFrame = origCentreFrame; | |
1693 update(); | |
1694 return false; | |
1695 } | |
1696 | |
1697 m_centreFrame = f0 + (x + width()/2) * m_zoomLevel; | |
1698 | |
1699 QRect chunk(0, 0, width(), height()); | |
1700 | |
1701 if (hasLightBackground()) { | |
1702 paint.setPen(Qt::white); | |
1703 paint.setBrush(Qt::white); | |
1704 } else { | |
1705 paint.setPen(Qt::black); | |
1706 paint.setBrush(Qt::black); | |
1707 } | |
1708 | |
1709 paint.drawRect(QRect(xorigin + x, 0, width(), height())); | |
1710 | |
1711 paint.setPen(Qt::black); | |
1712 paint.setBrush(Qt::NoBrush); | |
1713 | |
1714 for (LayerList::iterator i = m_layers.begin(); | |
1715 i != m_layers.end(); ++i) { | |
1716 | |
1717 paint.setRenderHint(QPainter::Antialiasing, false); | |
1718 | |
1719 paint.save(); | |
1720 paint.translate(xorigin + x, 0); | |
1721 | |
1722 std::cerr << "Centre frame now: " << m_centreFrame << " drawing to " << chunk.x() + x + xorigin << ", " << chunk.width() << std::endl; | |
1723 | |
1724 (*i)->paint(this, paint, chunk); | |
1725 | |
1726 paint.restore(); | |
1727 } | |
1728 } | |
1729 | |
1730 m_centreFrame = origCentreFrame; | |
1731 update(); | |
1732 return true; | |
1733 } | |
1734 | |
1735 QImage * | |
1736 View::toNewImage() | |
1737 { | |
1738 size_t f0 = getModelsStartFrame(); | |
1739 size_t f1 = getModelsEndFrame(); | |
1740 | |
1741 return toNewImage(f0, f1); | |
1742 } | |
1743 | |
1744 QImage * | |
1745 View::toNewImage(size_t f0, size_t f1) | |
1746 { | |
1747 size_t x0 = f0 / getZoomLevel(); | |
1748 size_t x1 = f1 / getZoomLevel(); | |
1749 | |
1750 QImage *image = new QImage(x1 - x0, height(), QImage::Format_RGB32); | |
1751 | |
1752 QPainter *paint = new QPainter(image); | |
1753 if (!render(*paint, 0, f0, f1)) { | |
1754 delete paint; | |
1755 delete image; | |
1756 return 0; | |
1757 } else { | |
1758 delete paint; | |
1759 return image; | |
1760 } | |
1761 } | |
1762 | |
1763 QSize | |
1764 View::getImageSize() | |
1765 { | |
1766 size_t f0 = getModelsStartFrame(); | |
1767 size_t f1 = getModelsEndFrame(); | |
1768 | |
1769 return getImageSize(f0, f1); | |
1770 } | |
1771 | |
1772 QSize | |
1773 View::getImageSize(size_t f0, size_t f1) | |
1774 { | |
1775 size_t x0 = f0 / getZoomLevel(); | |
1776 size_t x1 = f1 / getZoomLevel(); | |
1777 | |
1778 return QSize(x1 - x0, height()); | |
1779 } | |
1780 | |
1781 QString | |
1782 View::toXmlString(QString indent, QString extraAttributes) const | |
1783 { | |
1784 QString s; | |
1785 | |
1786 s += indent; | |
1787 | |
1788 s += QString("<view " | |
1789 "centre=\"%1\" " | |
1790 "zoom=\"%2\" " | |
1791 "followPan=\"%3\" " | |
1792 "followZoom=\"%4\" " | |
1793 "tracking=\"%5\" " | |
1794 " %6>\n") | |
1795 .arg(m_centreFrame) | |
1796 .arg(m_zoomLevel) | |
1797 .arg(m_followPan) | |
1798 .arg(m_followZoom) | |
1799 .arg(m_followPlay == PlaybackScrollContinuous ? "scroll" : | |
1800 m_followPlay == PlaybackScrollPage ? "page" : "ignore") | |
1801 .arg(extraAttributes); | |
1802 | |
1803 for (size_t i = 0; i < m_layers.size(); ++i) { | |
1804 bool visible = !m_layers[i]->isLayerDormant(this); | |
1805 s += m_layers[i]->toXmlString(indent + " ", | |
1806 QString("visible=\"%1\"") | |
1807 .arg(visible ? "true" : "false")); | |
1808 } | |
1809 | |
1810 s += indent + "</view>\n"; | |
1811 | |
1812 return s; | |
1813 } | |
1814 | |
1815 ViewPropertyContainer::ViewPropertyContainer(View *v) : | |
1816 m_v(v) | |
1817 { | |
1818 connect(m_v, SIGNAL(propertyChanged(PropertyContainer::PropertyName)), | |
1819 this, SIGNAL(propertyChanged(PropertyContainer::PropertyName))); | |
1820 } | |
1821 |