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