svgui
1.9
|
00001 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ 00002 00003 /* 00004 Sonic Visualiser 00005 An audio file viewer and annotation editor. 00006 Centre for Digital Music, Queen Mary, University of London. 00007 This file copyright 2006 Chris Cannam. 00008 00009 This program is free software; you can redistribute it and/or 00010 modify it under the terms of the GNU General Public License as 00011 published by the Free Software Foundation; either version 2 of the 00012 License, or (at your option) any later version. See the file 00013 COPYING included with this distribution for more information. 00014 */ 00015 00016 #include "View.h" 00017 #include "layer/Layer.h" 00018 #include "data/model/Model.h" 00019 #include "base/ZoomConstraint.h" 00020 #include "base/Profiler.h" 00021 #include "base/Pitch.h" 00022 #include "base/Preferences.h" 00023 00024 #include "layer/TimeRulerLayer.h" 00025 #include "layer/SingleColourLayer.h" 00026 #include "data/model/PowerOfSqrtTwoZoomConstraint.h" 00027 #include "data/model/RangeSummarisableTimeValueModel.h" 00028 00029 #include "widgets/IconLoader.h" 00030 00031 #include <QPainter> 00032 #include <QPaintEvent> 00033 #include <QRect> 00034 #include <QApplication> 00035 #include <QProgressDialog> 00036 #include <QTextStream> 00037 #include <QFont> 00038 #include <QMessageBox> 00039 #include <QPushButton> 00040 00041 #include <iostream> 00042 #include <cassert> 00043 #include <cmath> 00044 00045 #include <unistd.h> 00046 00047 //#define DEBUG_VIEW 1 00048 //#define DEBUG_VIEW_WIDGET_PAINT 1 00049 00050 00051 View::View(QWidget *w, bool showProgress) : 00052 QFrame(w), 00053 m_centreFrame(0), 00054 m_zoomLevel(1024), 00055 m_followPan(true), 00056 m_followZoom(true), 00057 m_followPlay(PlaybackScrollPageWithCentre), 00058 m_followPlayIsDetached(false), 00059 m_playPointerFrame(0), 00060 m_showProgress(showProgress), 00061 m_cache(0), 00062 m_cacheCentreFrame(0), 00063 m_cacheZoomLevel(1024), 00064 m_selectionCached(false), 00065 m_deleting(false), 00066 m_haveSelectedLayer(false), 00067 m_manager(0), 00068 m_propertyContainer(new ViewPropertyContainer(this)) 00069 { 00070 // cerr << "View::View(" << this << ")" << endl; 00071 } 00072 00073 View::~View() 00074 { 00075 // cerr << "View::~View(" << this << ")" << endl; 00076 00077 m_deleting = true; 00078 delete m_propertyContainer; 00079 } 00080 00081 PropertyContainer::PropertyList 00082 View::getProperties() const 00083 { 00084 PropertyContainer::PropertyList list; 00085 list.push_back("Global Scroll"); 00086 list.push_back("Global Zoom"); 00087 list.push_back("Follow Playback"); 00088 return list; 00089 } 00090 00091 QString 00092 View::getPropertyLabel(const PropertyName &pn) const 00093 { 00094 if (pn == "Global Scroll") return tr("Global Scroll"); 00095 if (pn == "Global Zoom") return tr("Global Zoom"); 00096 if (pn == "Follow Playback") return tr("Follow Playback"); 00097 return ""; 00098 } 00099 00100 PropertyContainer::PropertyType 00101 View::getPropertyType(const PropertyContainer::PropertyName &name) const 00102 { 00103 if (name == "Global Scroll") return PropertyContainer::ToggleProperty; 00104 if (name == "Global Zoom") return PropertyContainer::ToggleProperty; 00105 if (name == "Follow Playback") return PropertyContainer::ValueProperty; 00106 return PropertyContainer::InvalidProperty; 00107 } 00108 00109 int 00110 View::getPropertyRangeAndValue(const PropertyContainer::PropertyName &name, 00111 int *min, int *max, int *deflt) const 00112 { 00113 if (deflt) *deflt = 1; 00114 if (name == "Global Scroll") return m_followPan; 00115 if (name == "Global Zoom") return m_followZoom; 00116 if (name == "Follow Playback") { 00117 if (min) *min = 0; 00118 if (max) *max = 2; 00119 if (deflt) *deflt = int(PlaybackScrollPageWithCentre); 00120 switch (m_followPlay) { 00121 case PlaybackScrollContinuous: return 0; 00122 case PlaybackScrollPageWithCentre: case PlaybackScrollPage: return 1; 00123 case PlaybackIgnore: return 2; 00124 } 00125 } 00126 if (min) *min = 0; 00127 if (max) *max = 0; 00128 if (deflt) *deflt = 0; 00129 return 0; 00130 } 00131 00132 QString 00133 View::getPropertyValueLabel(const PropertyContainer::PropertyName &name, 00134 int value) const 00135 { 00136 if (name == "Follow Playback") { 00137 switch (value) { 00138 default: 00139 case 0: return tr("Scroll"); 00140 case 1: return tr("Page"); 00141 case 2: return tr("Off"); 00142 } 00143 } 00144 return tr("<unknown>"); 00145 } 00146 00147 void 00148 View::setProperty(const PropertyContainer::PropertyName &name, int value) 00149 { 00150 if (name == "Global Scroll") { 00151 setFollowGlobalPan(value != 0); 00152 } else if (name == "Global Zoom") { 00153 setFollowGlobalZoom(value != 0); 00154 } else if (name == "Follow Playback") { 00155 switch (value) { 00156 default: 00157 case 0: setPlaybackFollow(PlaybackScrollContinuous); break; 00158 case 1: setPlaybackFollow(PlaybackScrollPageWithCentre); break; 00159 case 2: setPlaybackFollow(PlaybackIgnore); break; 00160 } 00161 } 00162 } 00163 00164 int 00165 View::getPropertyContainerCount() const 00166 { 00167 return m_fixedOrderLayers.size() + 1; // the 1 is for me 00168 } 00169 00170 const PropertyContainer * 00171 View::getPropertyContainer(int i) const 00172 { 00173 return (const PropertyContainer *)(((View *)this)-> 00174 getPropertyContainer(i)); 00175 } 00176 00177 PropertyContainer * 00178 View::getPropertyContainer(int i) 00179 { 00180 if (i == 0) return m_propertyContainer; 00181 return m_fixedOrderLayers[i-1]; 00182 } 00183 00184 bool 00185 View::getValueExtents(QString unit, float &min, float &max, bool &log) const 00186 { 00187 bool have = false; 00188 00189 for (LayerList::const_iterator i = m_layerStack.begin(); 00190 i != m_layerStack.end(); ++i) { 00191 00192 QString layerUnit; 00193 float layerMin = 0.0, layerMax = 0.0; 00194 float displayMin = 0.0, displayMax = 0.0; 00195 bool layerLog = false; 00196 00197 if ((*i)->getValueExtents(layerMin, layerMax, layerLog, layerUnit) && 00198 layerUnit.toLower() == unit.toLower()) { 00199 00200 if ((*i)->getDisplayExtents(displayMin, displayMax)) { 00201 00202 min = displayMin; 00203 max = displayMax; 00204 log = layerLog; 00205 have = true; 00206 break; 00207 00208 } else { 00209 00210 if (!have || layerMin < min) min = layerMin; 00211 if (!have || layerMax > max) max = layerMax; 00212 if (layerLog) log = true; 00213 have = true; 00214 } 00215 } 00216 } 00217 00218 return have; 00219 } 00220 00221 int 00222 View::getTextLabelHeight(const Layer *layer, QPainter &paint) const 00223 { 00224 std::map<int, Layer *> sortedLayers; 00225 00226 for (LayerList::const_iterator i = m_layerStack.begin(); 00227 i != m_layerStack.end(); ++i) { 00228 if ((*i)->needsTextLabelHeight()) { 00229 sortedLayers[getObjectExportId(*i)] = *i; 00230 } 00231 } 00232 00233 int y = 15 + paint.fontMetrics().ascent(); 00234 00235 for (std::map<int, Layer *>::const_iterator i = sortedLayers.begin(); 00236 i != sortedLayers.end(); ++i) { 00237 if (i->second == layer) return y; 00238 y += paint.fontMetrics().height(); 00239 } 00240 00241 return y; 00242 } 00243 00244 void 00245 View::propertyContainerSelected(View *client, PropertyContainer *pc) 00246 { 00247 if (client != this) return; 00248 00249 if (pc == m_propertyContainer) { 00250 if (m_haveSelectedLayer) { 00251 m_haveSelectedLayer = false; 00252 update(); 00253 } 00254 return; 00255 } 00256 00257 delete m_cache; 00258 m_cache = 0; 00259 00260 Layer *selectedLayer = 0; 00261 00262 for (LayerList::iterator i = m_layerStack.begin(); i != m_layerStack.end(); ++i) { 00263 if (*i == pc) { 00264 selectedLayer = *i; 00265 m_layerStack.erase(i); 00266 break; 00267 } 00268 } 00269 00270 if (selectedLayer) { 00271 m_haveSelectedLayer = true; 00272 m_layerStack.push_back(selectedLayer); 00273 update(); 00274 } else { 00275 m_haveSelectedLayer = false; 00276 } 00277 00278 emit propertyContainerSelected(pc); 00279 } 00280 00281 void 00282 View::toolModeChanged() 00283 { 00284 // SVDEBUG << "View::toolModeChanged(" << m_manager->getToolMode() << ")" << endl; 00285 } 00286 00287 void 00288 View::overlayModeChanged() 00289 { 00290 delete m_cache; 00291 m_cache = 0; 00292 update(); 00293 } 00294 00295 void 00296 View::zoomWheelsEnabledChanged() 00297 { 00298 // subclass might override this 00299 } 00300 00301 int 00302 View::getStartFrame() const 00303 { 00304 return getFrameForX(0); 00305 } 00306 00307 int 00308 View::getEndFrame() const 00309 { 00310 return getFrameForX(width()) - 1; 00311 } 00312 00313 void 00314 View::setStartFrame(int f) 00315 { 00316 setCentreFrame(f + m_zoomLevel * (width() / 2)); 00317 } 00318 00319 bool 00320 View::setCentreFrame(int f, bool e) 00321 { 00322 bool changeVisible = false; 00323 00324 if (m_centreFrame != f) { 00325 00326 int formerPixel = m_centreFrame / m_zoomLevel; 00327 00328 m_centreFrame = f; 00329 00330 int newPixel = m_centreFrame / m_zoomLevel; 00331 00332 if (newPixel != formerPixel) { 00333 00334 #ifdef DEBUG_VIEW_WIDGET_PAINT 00335 cout << "View(" << this << ")::setCentreFrame: newPixel " << newPixel << ", formerPixel " << formerPixel << endl; 00336 #endif 00337 update(); 00338 00339 changeVisible = true; 00340 } 00341 00342 if (e) { 00343 int rf = alignToReference(f); 00344 #ifdef DEBUG_VIEW 00345 cerr << "View[" << this << "]::setCentreFrame(" << f 00346 << "): emitting centreFrameChanged(" 00347 << rf << ")" << endl; 00348 #endif 00349 emit centreFrameChanged(rf, m_followPan, m_followPlay); 00350 } 00351 } 00352 00353 return changeVisible; 00354 } 00355 00356 int 00357 View::getXForFrame(int frame) const 00358 { 00359 return (frame - getStartFrame()) / m_zoomLevel; 00360 } 00361 00362 int 00363 View::getFrameForX(int x) const 00364 { 00365 int z = m_zoomLevel; 00366 int frame = m_centreFrame - (width()/2) * z; 00367 00368 #ifdef DEBUG_VIEW_WIDGET_PAINT 00369 SVDEBUG << "View::getFrameForX(" << x << "): z = " << z << ", m_centreFrame = " << m_centreFrame << ", width() = " << width() << ", frame = " << frame << endl; 00370 #endif 00371 00372 frame = (frame / z) * z; // this is start frame 00373 return frame + x * z; 00374 } 00375 00376 float 00377 View::getYForFrequency(float frequency, 00378 float minf, 00379 float maxf, 00380 bool logarithmic) const 00381 { 00382 Profiler profiler("View::getYForFrequency"); 00383 00384 int h = height(); 00385 00386 if (logarithmic) { 00387 00388 static float lastminf = 0.0, lastmaxf = 0.0; 00389 static float logminf = 0.0, logmaxf = 0.0; 00390 00391 if (lastminf != minf) { 00392 lastminf = (minf == 0.0 ? 1.0 : minf); 00393 logminf = log10f(minf); 00394 } 00395 if (lastmaxf != maxf) { 00396 lastmaxf = (maxf < lastminf ? lastminf : maxf); 00397 logmaxf = log10f(maxf); 00398 } 00399 00400 if (logminf == logmaxf) return 0; 00401 return h - (h * (log10f(frequency) - logminf)) / (logmaxf - logminf); 00402 00403 } else { 00404 00405 if (minf == maxf) return 0; 00406 return h - (h * (frequency - minf)) / (maxf - minf); 00407 } 00408 } 00409 00410 float 00411 View::getFrequencyForY(int y, 00412 float minf, 00413 float maxf, 00414 bool logarithmic) const 00415 { 00416 int h = height(); 00417 00418 if (logarithmic) { 00419 00420 static float lastminf = 0.0, lastmaxf = 0.0; 00421 static float logminf = 0.0, logmaxf = 0.0; 00422 00423 if (lastminf != minf) { 00424 lastminf = (minf == 0.0 ? 1.0 : minf); 00425 logminf = log10f(minf); 00426 } 00427 if (lastmaxf != maxf) { 00428 lastmaxf = (maxf < lastminf ? lastminf : maxf); 00429 logmaxf = log10f(maxf); 00430 } 00431 00432 if (logminf == logmaxf) return 0; 00433 return pow(10.f, logminf + ((logmaxf - logminf) * (h - y)) / h); 00434 00435 } else { 00436 00437 if (minf == maxf) return 0; 00438 return minf + ((h - y) * (maxf - minf)) / h; 00439 } 00440 } 00441 00442 int 00443 View::getZoomLevel() const 00444 { 00445 #ifdef DEBUG_VIEW_WIDGET_PAINT 00446 // cout << "zoom level: " << m_zoomLevel << endl; 00447 #endif 00448 return m_zoomLevel; 00449 } 00450 00451 void 00452 View::setZoomLevel(int z) 00453 { 00454 if (z < 1) z = 1; 00455 if (m_zoomLevel != int(z)) { 00456 m_zoomLevel = z; 00457 emit zoomLevelChanged(z, m_followZoom); 00458 update(); 00459 } 00460 } 00461 00462 bool 00463 View::hasLightBackground() const 00464 { 00465 bool darkPalette = false; 00466 if (m_manager) darkPalette = m_manager->getGlobalDarkBackground(); 00467 00468 Layer::ColourSignificance maxSignificance = Layer::ColourAbsent; 00469 bool mostSignificantHasDarkBackground = false; 00470 00471 for (LayerList::const_iterator i = m_layerStack.begin(); 00472 i != m_layerStack.end(); ++i) { 00473 00474 Layer::ColourSignificance s = (*i)->getLayerColourSignificance(); 00475 bool light = (*i)->hasLightBackground(); 00476 00477 if (int(s) > int(maxSignificance)) { 00478 maxSignificance = s; 00479 mostSignificantHasDarkBackground = !light; 00480 } else if (s == maxSignificance && !light) { 00481 mostSignificantHasDarkBackground = true; 00482 } 00483 } 00484 00485 if (int(maxSignificance) >= int(Layer::ColourAndBackgroundSignificant)) { 00486 return !mostSignificantHasDarkBackground; 00487 } else { 00488 return !darkPalette; 00489 } 00490 } 00491 00492 QColor 00493 View::getBackground() const 00494 { 00495 bool light = hasLightBackground(); 00496 00497 QColor widgetbg = palette().window().color(); 00498 bool widgetLight = 00499 (widgetbg.red() + widgetbg.green() + widgetbg.blue()) > 384; 00500 00501 if (widgetLight == light) { 00502 if (widgetLight) { 00503 return widgetbg.light(); 00504 } else { 00505 return widgetbg.dark(); 00506 } 00507 } 00508 else if (light) return Qt::white; 00509 else return Qt::black; 00510 } 00511 00512 QColor 00513 View::getForeground() const 00514 { 00515 bool light = hasLightBackground(); 00516 00517 QColor widgetfg = palette().text().color(); 00518 bool widgetLight = 00519 (widgetfg.red() + widgetfg.green() + widgetfg.blue()) > 384; 00520 00521 if (widgetLight != light) return widgetfg; 00522 else if (light) return Qt::black; 00523 else return Qt::white; 00524 } 00525 00526 void 00527 View::addLayer(Layer *layer) 00528 { 00529 delete m_cache; 00530 m_cache = 0; 00531 00532 SingleColourLayer *scl = dynamic_cast<SingleColourLayer *>(layer); 00533 if (scl) scl->setDefaultColourFor(this); 00534 00535 m_fixedOrderLayers.push_back(layer); 00536 m_layerStack.push_back(layer); 00537 00538 QProgressBar *pb = new QProgressBar(this); 00539 pb->setMinimum(0); 00540 pb->setMaximum(0); 00541 pb->setFixedWidth(80); 00542 pb->setTextVisible(false); 00543 00544 QPushButton *cancel = new QPushButton(this); 00545 cancel->setIcon(IconLoader().load("fileclose")); 00546 cancel->setFlat(true); 00547 cancel->setFixedSize(QSize(20, 20)); 00548 connect(cancel, SIGNAL(clicked()), this, SLOT(cancelClicked())); 00549 00550 ProgressBarRec pbr; 00551 pbr.cancel = cancel; 00552 pbr.bar = pb; 00553 pbr.lastCheck = 0; 00554 pbr.checkTimer = new QTimer(); 00555 connect(pbr.checkTimer, SIGNAL(timeout()), this, 00556 SLOT(progressCheckStalledTimerElapsed())); 00557 00558 m_progressBars[layer] = pbr; 00559 00560 QFont f(pb->font()); 00561 int fs = Preferences::getInstance()->getViewFontSize(); 00562 f.setPointSize(std::min(fs, int(ceil(fs * 0.85)))); 00563 00564 cancel->hide(); 00565 00566 pb->setFont(f); 00567 pb->hide(); 00568 00569 connect(layer, SIGNAL(layerParametersChanged()), 00570 this, SLOT(layerParametersChanged())); 00571 connect(layer, SIGNAL(layerParameterRangesChanged()), 00572 this, SLOT(layerParameterRangesChanged())); 00573 connect(layer, SIGNAL(layerMeasurementRectsChanged()), 00574 this, SLOT(layerMeasurementRectsChanged())); 00575 connect(layer, SIGNAL(layerNameChanged()), 00576 this, SLOT(layerNameChanged())); 00577 connect(layer, SIGNAL(modelChanged()), 00578 this, SLOT(modelChanged())); 00579 connect(layer, SIGNAL(modelCompletionChanged()), 00580 this, SLOT(modelCompletionChanged())); 00581 connect(layer, SIGNAL(modelAlignmentCompletionChanged()), 00582 this, SLOT(modelAlignmentCompletionChanged())); 00583 connect(layer, SIGNAL(modelChangedWithin(int, int)), 00584 this, SLOT(modelChangedWithin(int, int))); 00585 connect(layer, SIGNAL(modelReplaced()), 00586 this, SLOT(modelReplaced())); 00587 00588 update(); 00589 00590 emit propertyContainerAdded(layer); 00591 } 00592 00593 void 00594 View::removeLayer(Layer *layer) 00595 { 00596 if (m_deleting) { 00597 return; 00598 } 00599 00600 delete m_cache; 00601 m_cache = 0; 00602 00603 for (LayerList::iterator i = m_fixedOrderLayers.begin(); 00604 i != m_fixedOrderLayers.end(); 00605 ++i) { 00606 if (*i == layer) { 00607 m_fixedOrderLayers.erase(i); 00608 break; 00609 } 00610 } 00611 00612 for (LayerList::iterator i = m_layerStack.begin(); 00613 i != m_layerStack.end(); 00614 ++i) { 00615 if (*i == layer) { 00616 m_layerStack.erase(i); 00617 if (m_progressBars.find(layer) != m_progressBars.end()) { 00618 delete m_progressBars[layer].bar; 00619 delete m_progressBars[layer].cancel; 00620 delete m_progressBars[layer].checkTimer; 00621 m_progressBars.erase(layer); 00622 } 00623 break; 00624 } 00625 } 00626 00627 disconnect(layer, SIGNAL(layerParametersChanged()), 00628 this, SLOT(layerParametersChanged())); 00629 disconnect(layer, SIGNAL(layerParameterRangesChanged()), 00630 this, SLOT(layerParameterRangesChanged())); 00631 disconnect(layer, SIGNAL(layerNameChanged()), 00632 this, SLOT(layerNameChanged())); 00633 disconnect(layer, SIGNAL(modelChanged()), 00634 this, SLOT(modelChanged())); 00635 disconnect(layer, SIGNAL(modelCompletionChanged()), 00636 this, SLOT(modelCompletionChanged())); 00637 disconnect(layer, SIGNAL(modelAlignmentCompletionChanged()), 00638 this, SLOT(modelAlignmentCompletionChanged())); 00639 disconnect(layer, SIGNAL(modelChangedWithin(int, int)), 00640 this, SLOT(modelChangedWithin(int, int))); 00641 disconnect(layer, SIGNAL(modelReplaced()), 00642 this, SLOT(modelReplaced())); 00643 00644 update(); 00645 00646 emit propertyContainerRemoved(layer); 00647 } 00648 00649 Layer * 00650 View::getInteractionLayer() 00651 { 00652 Layer *sl = getSelectedLayer(); 00653 if (sl && !(sl->isLayerDormant(this))) { 00654 return sl; 00655 } 00656 if (!m_layerStack.empty()) { 00657 int n = getLayerCount(); 00658 while (n > 0) { 00659 --n; 00660 Layer *layer = getLayer(n); 00661 if (!(layer->isLayerDormant(this))) { 00662 return layer; 00663 } 00664 } 00665 } 00666 return 0; 00667 } 00668 00669 const Layer * 00670 View::getInteractionLayer() const 00671 { 00672 return const_cast<const Layer *>(const_cast<View *>(this)->getInteractionLayer()); 00673 } 00674 00675 Layer * 00676 View::getSelectedLayer() 00677 { 00678 if (m_haveSelectedLayer && !m_layerStack.empty()) { 00679 return getLayer(getLayerCount() - 1); 00680 } else { 00681 return 0; 00682 } 00683 } 00684 00685 const Layer * 00686 View::getSelectedLayer() const 00687 { 00688 return const_cast<const Layer *>(const_cast<View *>(this)->getSelectedLayer()); 00689 } 00690 00691 void 00692 View::setViewManager(ViewManager *manager) 00693 { 00694 if (m_manager) { 00695 m_manager->disconnect(this, SLOT(globalCentreFrameChanged(int))); 00696 m_manager->disconnect(this, SLOT(viewCentreFrameChanged(View *, int))); 00697 m_manager->disconnect(this, SLOT(viewManagerPlaybackFrameChanged(int))); 00698 m_manager->disconnect(this, SLOT(viewZoomLevelChanged(View *, int, bool))); 00699 m_manager->disconnect(this, SLOT(toolModeChanged())); 00700 m_manager->disconnect(this, SLOT(selectionChanged())); 00701 m_manager->disconnect(this, SLOT(overlayModeChanged())); 00702 m_manager->disconnect(this, SLOT(zoomWheelsEnabledChanged())); 00703 disconnect(m_manager, SLOT(viewCentreFrameChanged(int, bool, PlaybackFollowMode))); 00704 disconnect(m_manager, SLOT(zoomLevelChanged(int, bool))); 00705 } 00706 00707 m_manager = manager; 00708 00709 connect(m_manager, SIGNAL(globalCentreFrameChanged(int)), 00710 this, SLOT(globalCentreFrameChanged(int))); 00711 connect(m_manager, SIGNAL(viewCentreFrameChanged(View *, int)), 00712 this, SLOT(viewCentreFrameChanged(View *, int))); 00713 connect(m_manager, SIGNAL(playbackFrameChanged(int)), 00714 this, SLOT(viewManagerPlaybackFrameChanged(int))); 00715 00716 connect(m_manager, SIGNAL(viewZoomLevelChanged(View *, int, bool)), 00717 this, SLOT(viewZoomLevelChanged(View *, int, bool))); 00718 00719 connect(m_manager, SIGNAL(toolModeChanged()), 00720 this, SLOT(toolModeChanged())); 00721 connect(m_manager, SIGNAL(selectionChanged()), 00722 this, SLOT(selectionChanged())); 00723 connect(m_manager, SIGNAL(inProgressSelectionChanged()), 00724 this, SLOT(selectionChanged())); 00725 connect(m_manager, SIGNAL(overlayModeChanged()), 00726 this, SLOT(overlayModeChanged())); 00727 connect(m_manager, SIGNAL(showCentreLineChanged()), 00728 this, SLOT(overlayModeChanged())); 00729 connect(m_manager, SIGNAL(zoomWheelsEnabledChanged()), 00730 this, SLOT(zoomWheelsEnabledChanged())); 00731 00732 connect(this, SIGNAL(centreFrameChanged(int, bool, 00733 PlaybackFollowMode)), 00734 m_manager, SLOT(viewCentreFrameChanged(int, bool, 00735 PlaybackFollowMode))); 00736 00737 connect(this, SIGNAL(zoomLevelChanged(int, bool)), 00738 m_manager, SLOT(viewZoomLevelChanged(int, bool))); 00739 00740 switch (m_followPlay) { 00741 00742 case PlaybackScrollPage: 00743 case PlaybackScrollPageWithCentre: 00744 setCentreFrame(m_manager->getGlobalCentreFrame(), false); 00745 break; 00746 00747 case PlaybackScrollContinuous: 00748 setCentreFrame(m_manager->getPlaybackFrame(), false); 00749 break; 00750 00751 case PlaybackIgnore: 00752 if (m_followPan) { 00753 setCentreFrame(m_manager->getGlobalCentreFrame(), false); 00754 } 00755 break; 00756 } 00757 00758 if (m_followZoom) setZoomLevel(m_manager->getGlobalZoom()); 00759 00760 movePlayPointer(getAlignedPlaybackFrame()); 00761 00762 toolModeChanged(); 00763 } 00764 00765 void 00766 View::setViewManager(ViewManager *vm, int initialCentreFrame) 00767 { 00768 setViewManager(vm); 00769 setCentreFrame(initialCentreFrame, false); 00770 } 00771 00772 void 00773 View::setFollowGlobalPan(bool f) 00774 { 00775 m_followPan = f; 00776 emit propertyContainerPropertyChanged(m_propertyContainer); 00777 } 00778 00779 void 00780 View::setFollowGlobalZoom(bool f) 00781 { 00782 m_followZoom = f; 00783 emit propertyContainerPropertyChanged(m_propertyContainer); 00784 } 00785 00786 void 00787 View::drawVisibleText(QPainter &paint, int x, int y, QString text, TextStyle style) const 00788 { 00789 if (style == OutlinedText || style == OutlinedItalicText) { 00790 00791 paint.save(); 00792 00793 if (style == OutlinedItalicText) { 00794 QFont f(paint.font()); 00795 f.setItalic(true); 00796 paint.setFont(f); 00797 } 00798 00799 QColor penColour, surroundColour, boxColour; 00800 00801 penColour = getForeground(); 00802 surroundColour = getBackground(); 00803 boxColour = surroundColour; 00804 boxColour.setAlpha(127); 00805 00806 paint.setPen(Qt::NoPen); 00807 paint.setBrush(boxColour); 00808 00809 QRect r = paint.fontMetrics().boundingRect(text); 00810 r.translate(QPoint(x, y)); 00811 // cerr << "drawVisibleText: r = " << r.x() << "," <<r.y() << " " << r.width() << "x" << r.height() << endl; 00812 paint.drawRect(r); 00813 paint.setBrush(Qt::NoBrush); 00814 00815 paint.setPen(surroundColour); 00816 00817 for (int dx = -1; dx <= 1; ++dx) { 00818 for (int dy = -1; dy <= 1; ++dy) { 00819 if (!(dx || dy)) continue; 00820 paint.drawText(x + dx, y + dy, text); 00821 } 00822 } 00823 00824 paint.setPen(penColour); 00825 00826 paint.drawText(x, y, text); 00827 00828 paint.restore(); 00829 00830 } else { 00831 00832 cerr << "ERROR: View::drawVisibleText: Boxed style not yet implemented!" << endl; 00833 } 00834 } 00835 00836 void 00837 View::setPlaybackFollow(PlaybackFollowMode m) 00838 { 00839 m_followPlay = m; 00840 emit propertyContainerPropertyChanged(m_propertyContainer); 00841 } 00842 00843 void 00844 View::modelChanged() 00845 { 00846 QObject *obj = sender(); 00847 00848 #ifdef DEBUG_VIEW_WIDGET_PAINT 00849 cerr << "View(" << this << ")::modelChanged()" << endl; 00850 #endif 00851 00852 // If the model that has changed is not used by any of the cached 00853 // layers, we won't need to recreate the cache 00854 00855 bool recreate = false; 00856 00857 bool discard; 00858 LayerList scrollables = getScrollableBackLayers(false, discard); 00859 for (LayerList::const_iterator i = scrollables.begin(); 00860 i != scrollables.end(); ++i) { 00861 if (*i == obj || (*i)->getModel() == obj) { 00862 recreate = true; 00863 break; 00864 } 00865 } 00866 00867 if (recreate) { 00868 delete m_cache; 00869 m_cache = 0; 00870 } 00871 00872 emit layerModelChanged(); 00873 00874 checkProgress(obj); 00875 00876 update(); 00877 } 00878 00879 void 00880 View::modelChangedWithin(int startFrame, int endFrame) 00881 { 00882 QObject *obj = sender(); 00883 00884 int myStartFrame = getStartFrame(); 00885 int myEndFrame = getEndFrame(); 00886 00887 #ifdef DEBUG_VIEW_WIDGET_PAINT 00888 cerr << "View(" << this << ")::modelChangedWithin(" << startFrame << "," << endFrame << ") [me " << myStartFrame << "," << myEndFrame << "]" << endl; 00889 #endif 00890 00891 if (myStartFrame > 0 && endFrame < int(myStartFrame)) { 00892 checkProgress(obj); 00893 return; 00894 } 00895 if (startFrame > myEndFrame) { 00896 checkProgress(obj); 00897 return; 00898 } 00899 00900 // If the model that has changed is not used by any of the cached 00901 // layers, we won't need to recreate the cache 00902 00903 bool recreate = false; 00904 00905 bool discard; 00906 LayerList scrollables = getScrollableBackLayers(false, discard); 00907 for (LayerList::const_iterator i = scrollables.begin(); 00908 i != scrollables.end(); ++i) { 00909 if (*i == obj || (*i)->getModel() == obj) { 00910 recreate = true; 00911 break; 00912 } 00913 } 00914 00915 if (recreate) { 00916 delete m_cache; 00917 m_cache = 0; 00918 } 00919 00920 if (startFrame < myStartFrame) startFrame = myStartFrame; 00921 if (endFrame > myEndFrame) endFrame = myEndFrame; 00922 00923 checkProgress(obj); 00924 00925 update(); 00926 } 00927 00928 void 00929 View::modelCompletionChanged() 00930 { 00931 // cerr << "View(" << this << ")::modelCompletionChanged()" << endl; 00932 00933 QObject *obj = sender(); 00934 checkProgress(obj); 00935 } 00936 00937 void 00938 View::modelAlignmentCompletionChanged() 00939 { 00940 // cerr << "View(" << this << ")::modelAlignmentCompletionChanged()" << endl; 00941 00942 QObject *obj = sender(); 00943 checkProgress(obj); 00944 } 00945 00946 void 00947 View::modelReplaced() 00948 { 00949 #ifdef DEBUG_VIEW_WIDGET_PAINT 00950 cerr << "View(" << this << ")::modelReplaced()" << endl; 00951 #endif 00952 delete m_cache; 00953 m_cache = 0; 00954 00955 update(); 00956 } 00957 00958 void 00959 View::layerParametersChanged() 00960 { 00961 Layer *layer = dynamic_cast<Layer *>(sender()); 00962 00963 #ifdef DEBUG_VIEW_WIDGET_PAINT 00964 SVDEBUG << "View::layerParametersChanged()" << endl; 00965 #endif 00966 00967 delete m_cache; 00968 m_cache = 0; 00969 update(); 00970 00971 if (layer) { 00972 emit propertyContainerPropertyChanged(layer); 00973 } 00974 } 00975 00976 void 00977 View::layerParameterRangesChanged() 00978 { 00979 Layer *layer = dynamic_cast<Layer *>(sender()); 00980 if (layer) emit propertyContainerPropertyRangeChanged(layer); 00981 } 00982 00983 void 00984 View::layerMeasurementRectsChanged() 00985 { 00986 Layer *layer = dynamic_cast<Layer *>(sender()); 00987 if (layer) update(); 00988 } 00989 00990 void 00991 View::layerNameChanged() 00992 { 00993 Layer *layer = dynamic_cast<Layer *>(sender()); 00994 if (layer) emit propertyContainerNameChanged(layer); 00995 } 00996 00997 void 00998 View::globalCentreFrameChanged(int rf) 00999 { 01000 if (m_followPan) { 01001 int f = alignFromReference(rf); 01002 #ifdef DEBUG_VIEW 01003 cerr << "View[" << this << "]::globalCentreFrameChanged(" << rf 01004 << "): setting centre frame to " << f << endl; 01005 #endif 01006 setCentreFrame(f, false); 01007 } 01008 } 01009 01010 void 01011 View::viewCentreFrameChanged(View *, int ) 01012 { 01013 // We do nothing with this, but a subclass might 01014 } 01015 01016 void 01017 View::viewManagerPlaybackFrameChanged(int f) 01018 { 01019 if (m_manager) { 01020 if (sender() != m_manager) return; 01021 } 01022 01023 #ifdef DEBUG_VIEW 01024 cerr << "View::viewManagerPlaybackFrameChanged(" << f << ")" << endl; 01025 #endif 01026 01027 f = getAlignedPlaybackFrame(); 01028 01029 #ifdef DEBUG_VIEW 01030 cerr << " -> aligned frame = " << af << endl; 01031 #endif 01032 01033 movePlayPointer(f); 01034 } 01035 01036 void 01037 View::movePlayPointer(int newFrame) 01038 { 01039 #ifdef DEBUG_VIEW 01040 cerr << "View(" << this << ")::movePlayPointer(" << newFrame << ")" << endl; 01041 #endif 01042 01043 if (m_playPointerFrame == newFrame) return; 01044 bool visibleChange = 01045 (getXForFrame(m_playPointerFrame) != getXForFrame(newFrame)); 01046 int oldPlayPointerFrame = m_playPointerFrame; 01047 m_playPointerFrame = newFrame; 01048 if (!visibleChange) return; 01049 01050 bool somethingGoingOn = 01051 ((QApplication::mouseButtons() != Qt::NoButton) || 01052 (QApplication::keyboardModifiers() & Qt::AltModifier)); 01053 01054 bool pointerInVisibleArea = 01055 long(m_playPointerFrame) >= getStartFrame() && 01056 (m_playPointerFrame < getEndFrame() || 01057 // include old pointer location so we know to refresh when moving out 01058 oldPlayPointerFrame < getEndFrame()); 01059 01060 switch (m_followPlay) { 01061 01062 case PlaybackScrollContinuous: 01063 if (!somethingGoingOn) { 01064 setCentreFrame(m_playPointerFrame, false); 01065 } 01066 break; 01067 01068 case PlaybackScrollPage: 01069 case PlaybackScrollPageWithCentre: 01070 01071 if (!pointerInVisibleArea && somethingGoingOn) { 01072 01073 m_followPlayIsDetached = true; 01074 01075 } else if (!pointerInVisibleArea && m_followPlayIsDetached) { 01076 01077 // do nothing; we aren't tracking until the pointer comes back in 01078 01079 } else { 01080 01081 int xold = getXForFrame(oldPlayPointerFrame); 01082 update(xold - 4, 0, 9, height()); 01083 01084 int w = getEndFrame() - getStartFrame(); 01085 w -= w/5; 01086 int sf = (m_playPointerFrame / w) * w - w/8; 01087 01088 if (m_manager && 01089 m_manager->isPlaying() && 01090 m_manager->getPlaySelectionMode()) { 01091 MultiSelection::SelectionList selections = m_manager->getSelections(); 01092 if (!selections.empty()) { 01093 int selectionStart = selections.begin()->getStartFrame(); 01094 if (sf < selectionStart - w / 10) { 01095 sf = selectionStart - w / 10; 01096 } 01097 } 01098 } 01099 01100 #ifdef DEBUG_VIEW_WIDGET_PAINT 01101 cerr << "PlaybackScrollPage: f = " << m_playPointerFrame << ", sf = " << sf << ", start frame " 01102 << getStartFrame() << endl; 01103 #endif 01104 01105 // We don't consider scrolling unless the pointer is outside 01106 // the central visible range already 01107 01108 int xnew = getXForFrame(m_playPointerFrame); 01109 01110 #ifdef DEBUG_VIEW_WIDGET_PAINT 01111 cerr << "xnew = " << xnew << ", width = " << width() << endl; 01112 #endif 01113 01114 bool shouldScroll = (xnew > (width() * 7) / 8); 01115 01116 if (!m_followPlayIsDetached && (xnew < width() / 8)) { 01117 shouldScroll = true; 01118 } 01119 01120 if (xnew > width() / 8) { 01121 m_followPlayIsDetached = false; 01122 } else if (somethingGoingOn) { 01123 m_followPlayIsDetached = true; 01124 } 01125 01126 if (!somethingGoingOn && shouldScroll) { 01127 int offset = getFrameForX(width()/2) - getStartFrame(); 01128 int newCentre = sf + offset; 01129 bool changed = setCentreFrame(newCentre, false); 01130 if (changed) { 01131 xold = getXForFrame(oldPlayPointerFrame); 01132 update(xold - 4, 0, 9, height()); 01133 } 01134 } 01135 01136 update(xnew - 4, 0, 9, height()); 01137 } 01138 break; 01139 01140 case PlaybackIgnore: 01141 if (m_playPointerFrame >= getStartFrame() && 01142 m_playPointerFrame < getEndFrame()) { 01143 update(); 01144 } 01145 break; 01146 } 01147 } 01148 01149 void 01150 View::viewZoomLevelChanged(View *p, int z, bool locked) 01151 { 01152 #ifdef DEBUG_VIEW_WIDGET_PAINT 01153 cerr << "View[" << this << "]: viewZoomLevelChanged(" << p << ", " << z << ", " << locked << ")" << endl; 01154 #endif 01155 if (m_followZoom && p != this && locked) { 01156 setZoomLevel(z); 01157 } 01158 } 01159 01160 void 01161 View::selectionChanged() 01162 { 01163 if (m_selectionCached) { 01164 delete m_cache; 01165 m_cache = 0; 01166 m_selectionCached = false; 01167 } 01168 update(); 01169 } 01170 01171 int 01172 View::getFirstVisibleFrame() const 01173 { 01174 int f0 = getStartFrame(); 01175 int f = getModelsStartFrame(); 01176 if (f0 < 0 || f0 < f) return f; 01177 return f0; 01178 } 01179 01180 int 01181 View::getLastVisibleFrame() const 01182 { 01183 int f0 = getEndFrame(); 01184 int f = getModelsEndFrame(); 01185 if (f0 > f) return f; 01186 return f0; 01187 } 01188 01189 int 01190 View::getModelsStartFrame() const 01191 { 01192 bool first = true; 01193 int startFrame = 0; 01194 01195 for (LayerList::const_iterator i = m_layerStack.begin(); i != m_layerStack.end(); ++i) { 01196 01197 if ((*i)->getModel() && (*i)->getModel()->isOK()) { 01198 01199 int thisStartFrame = (*i)->getModel()->getStartFrame(); 01200 01201 if (first || thisStartFrame < startFrame) { 01202 startFrame = thisStartFrame; 01203 } 01204 first = false; 01205 } 01206 } 01207 return startFrame; 01208 } 01209 01210 int 01211 View::getModelsEndFrame() const 01212 { 01213 bool first = true; 01214 int endFrame = 0; 01215 01216 for (LayerList::const_iterator i = m_layerStack.begin(); i != m_layerStack.end(); ++i) { 01217 01218 if ((*i)->getModel() && (*i)->getModel()->isOK()) { 01219 01220 int thisEndFrame = (*i)->getModel()->getEndFrame(); 01221 01222 if (first || thisEndFrame > endFrame) { 01223 endFrame = thisEndFrame; 01224 } 01225 first = false; 01226 } 01227 } 01228 01229 if (first) return getModelsStartFrame(); 01230 return endFrame; 01231 } 01232 01233 int 01234 View::getModelsSampleRate() const 01235 { 01237 // multiple samplerates, we'd probably want to do frame/time 01238 // conversion in the model 01239 01241 01242 for (LayerList::const_iterator i = m_layerStack.begin(); i != m_layerStack.end(); ++i) { 01243 if ((*i)->getModel() && (*i)->getModel()->isOK()) { 01244 return (*i)->getModel()->getSampleRate(); 01245 } 01246 } 01247 return 0; 01248 } 01249 01250 View::ModelSet 01251 View::getModels() 01252 { 01253 ModelSet models; 01254 01255 for (int i = 0; i < getLayerCount(); ++i) { 01256 01257 Layer *layer = getLayer(i); 01258 01259 if (dynamic_cast<TimeRulerLayer *>(layer)) { 01260 continue; 01261 } 01262 01263 if (layer && layer->getModel()) { 01264 Model *model = layer->getModel(); 01265 models.insert(model); 01266 } 01267 } 01268 01269 return models; 01270 } 01271 01272 Model * 01273 View::getAligningModel() const 01274 { 01275 if (!m_manager || 01276 !m_manager->getAlignMode() || 01277 !m_manager->getPlaybackModel()) { 01278 return 0; 01279 } 01280 01281 Model *anyModel = 0; 01282 Model *alignedModel = 0; 01283 Model *goodModel = 0; 01284 01285 for (LayerList::const_iterator i = m_layerStack.begin(); 01286 i != m_layerStack.end(); ++i) { 01287 01288 Layer *layer = *i; 01289 01290 if (!layer) continue; 01291 if (dynamic_cast<TimeRulerLayer *>(layer)) continue; 01292 01293 Model *model = (*i)->getModel(); 01294 if (!model) continue; 01295 01296 anyModel = model; 01297 01298 if (model->getAlignmentReference()) { 01299 alignedModel = model; 01300 if (layer->isLayerOpaque() || 01301 dynamic_cast<RangeSummarisableTimeValueModel *>(model)) { 01302 goodModel = model; 01303 } 01304 } 01305 } 01306 01307 if (goodModel) return goodModel; 01308 else if (alignedModel) return alignedModel; 01309 else return anyModel; 01310 } 01311 01312 int 01313 View::alignFromReference(int f) const 01314 { 01315 if (!m_manager || !m_manager->getAlignMode()) return f; 01316 Model *aligningModel = getAligningModel(); 01317 if (!aligningModel) return f; 01318 return aligningModel->alignFromReference(f); 01319 } 01320 01321 int 01322 View::alignToReference(int f) const 01323 { 01324 if (!m_manager->getAlignMode()) return f; 01325 Model *aligningModel = getAligningModel(); 01326 if (!aligningModel) return f; 01327 return aligningModel->alignToReference(f); 01328 } 01329 01330 int 01331 View::getAlignedPlaybackFrame() const 01332 { 01333 if (!m_manager) return 0; 01334 int pf = m_manager->getPlaybackFrame(); 01335 if (!m_manager->getAlignMode()) return pf; 01336 01337 Model *aligningModel = getAligningModel(); 01338 if (!aligningModel) return pf; 01339 01340 int af = aligningModel->alignFromReference(pf); 01341 01342 return af; 01343 } 01344 01345 bool 01346 View::areLayersScrollable() const 01347 { 01348 // True iff all views are scrollable 01349 for (LayerList::const_iterator i = m_layerStack.begin(); i != m_layerStack.end(); ++i) { 01350 if (!(*i)->isLayerScrollable(this)) return false; 01351 } 01352 return true; 01353 } 01354 01355 View::LayerList 01356 View::getScrollableBackLayers(bool testChanged, bool &changed) const 01357 { 01358 changed = false; 01359 01360 // We want a list of all the scrollable layers that are behind the 01361 // backmost non-scrollable layer. 01362 01363 LayerList scrollables; 01364 bool metUnscrollable = false; 01365 01366 for (LayerList::const_iterator i = m_layerStack.begin(); i != m_layerStack.end(); ++i) { 01367 // SVDEBUG << "View::getScrollableBackLayers: calling isLayerDormant on layer " << *i << endl; 01368 // cerr << "(name is " << (*i)->objectName() << ")" 01369 // << endl; 01370 // SVDEBUG << "View::getScrollableBackLayers: I am " << this << endl; 01371 if ((*i)->isLayerDormant(this)) continue; 01372 if ((*i)->isLayerOpaque()) { 01373 // You can't see anything behind an opaque layer! 01374 scrollables.clear(); 01375 if (metUnscrollable) break; 01376 } 01377 if (!metUnscrollable && (*i)->isLayerScrollable(this)) { 01378 scrollables.push_back(*i); 01379 } else { 01380 metUnscrollable = true; 01381 } 01382 } 01383 01384 if (testChanged && scrollables != m_lastScrollableBackLayers) { 01385 m_lastScrollableBackLayers = scrollables; 01386 changed = true; 01387 } 01388 return scrollables; 01389 } 01390 01391 View::LayerList 01392 View::getNonScrollableFrontLayers(bool testChanged, bool &changed) const 01393 { 01394 changed = false; 01395 LayerList nonScrollables; 01396 01397 // Everything in front of the first non-scrollable from the back 01398 // should also be considered non-scrollable 01399 01400 bool started = false; 01401 01402 for (LayerList::const_iterator i = m_layerStack.begin(); i != m_layerStack.end(); ++i) { 01403 if ((*i)->isLayerDormant(this)) continue; 01404 if (!started && (*i)->isLayerScrollable(this)) { 01405 continue; 01406 } 01407 started = true; 01408 if ((*i)->isLayerOpaque()) { 01409 // You can't see anything behind an opaque layer! 01410 nonScrollables.clear(); 01411 } 01412 nonScrollables.push_back(*i); 01413 } 01414 01415 if (testChanged && nonScrollables != m_lastNonScrollableBackLayers) { 01416 m_lastNonScrollableBackLayers = nonScrollables; 01417 changed = true; 01418 } 01419 01420 return nonScrollables; 01421 } 01422 01423 int 01424 View::getZoomConstraintBlockSize(int blockSize, 01425 ZoomConstraint::RoundingDirection dir) 01426 const 01427 { 01428 int candidate = blockSize; 01429 bool haveCandidate = false; 01430 01431 PowerOfSqrtTwoZoomConstraint defaultZoomConstraint; 01432 01433 for (LayerList::const_iterator i = m_layerStack.begin(); i != m_layerStack.end(); ++i) { 01434 01435 const ZoomConstraint *zoomConstraint = (*i)->getZoomConstraint(); 01436 if (!zoomConstraint) zoomConstraint = &defaultZoomConstraint; 01437 01438 int thisBlockSize = 01439 zoomConstraint->getNearestBlockSize(blockSize, dir); 01440 01441 // Go for the block size that's furthest from the one 01442 // passed in. Most of the time, that's what we want. 01443 if (!haveCandidate || 01444 (thisBlockSize > blockSize && thisBlockSize > candidate) || 01445 (thisBlockSize < blockSize && thisBlockSize < candidate)) { 01446 candidate = thisBlockSize; 01447 haveCandidate = true; 01448 } 01449 } 01450 01451 return candidate; 01452 } 01453 01454 bool 01455 View::areLayerColoursSignificant() const 01456 { 01457 for (LayerList::const_iterator i = m_layerStack.begin(); i != m_layerStack.end(); ++i) { 01458 if ((*i)->getLayerColourSignificance() == 01459 Layer::ColourHasMeaningfulValue) return true; 01460 if ((*i)->isLayerOpaque()) break; 01461 } 01462 return false; 01463 } 01464 01465 bool 01466 View::hasTopLayerTimeXAxis() const 01467 { 01468 LayerList::const_iterator i = m_layerStack.end(); 01469 if (i == m_layerStack.begin()) return false; 01470 --i; 01471 return (*i)->hasTimeXAxis(); 01472 } 01473 01474 void 01475 View::zoom(bool in) 01476 { 01477 int newZoomLevel = m_zoomLevel; 01478 01479 if (in) { 01480 newZoomLevel = getZoomConstraintBlockSize(newZoomLevel - 1, 01481 ZoomConstraint::RoundDown); 01482 } else { 01483 newZoomLevel = getZoomConstraintBlockSize(newZoomLevel + 1, 01484 ZoomConstraint::RoundUp); 01485 } 01486 01487 if (newZoomLevel != m_zoomLevel) { 01488 setZoomLevel(newZoomLevel); 01489 } 01490 } 01491 01492 void 01493 View::scroll(bool right, bool lots, bool e) 01494 { 01495 int delta; 01496 if (lots) { 01497 delta = (getEndFrame() - getStartFrame()) / 2; 01498 } else { 01499 delta = (getEndFrame() - getStartFrame()) / 20; 01500 } 01501 if (right) delta = -delta; 01502 01503 if (int(m_centreFrame) < delta) { 01504 setCentreFrame(0, e); 01505 } else if (int(m_centreFrame) - delta >= int(getModelsEndFrame())) { 01506 setCentreFrame(getModelsEndFrame(), e); 01507 } else { 01508 setCentreFrame(m_centreFrame - delta, e); 01509 } 01510 } 01511 01512 void 01513 View::cancelClicked() 01514 { 01515 QPushButton *cancel = qobject_cast<QPushButton *>(sender()); 01516 if (!cancel) return; 01517 01518 for (ProgressMap::iterator i = m_progressBars.begin(); 01519 i != m_progressBars.end(); ++i) { 01520 01521 if (i->second.cancel == cancel) { 01522 01523 Layer *layer = i->first; 01524 Model *model = layer->getModel(); 01525 01526 if (model) model->abandon(); 01527 } 01528 } 01529 } 01530 01531 void 01532 View::checkProgress(void *object) 01533 { 01534 if (!m_showProgress) return; 01535 01536 int ph = height(); 01537 01538 for (ProgressMap::iterator i = m_progressBars.begin(); 01539 i != m_progressBars.end(); ++i) { 01540 01541 QProgressBar *pb = i->second.bar; 01542 QPushButton *cancel = i->second.cancel; 01543 01544 if (i->first == object) { 01545 01546 // The timer is used to test for stalls. If the progress 01547 // bar does not get updated for some length of time, the 01548 // timer prompts it to go back into "indeterminate" mode 01549 QTimer *timer = i->second.checkTimer; 01550 01551 int completion = i->first->getCompletion(this); 01552 QString text = i->first->getPropertyContainerName(); 01553 QString error = i->first->getError(this); 01554 01555 if (error != "" && error != m_lastError) { 01556 QMessageBox::critical(this, tr("Layer rendering error"), error); 01557 m_lastError = error; 01558 } 01559 01560 Model *model = i->first->getModel(); 01561 RangeSummarisableTimeValueModel *wfm = 01562 dynamic_cast<RangeSummarisableTimeValueModel *>(model); 01563 01564 if (completion > 0) { 01565 pb->setMaximum(100); // was 0, for indeterminate start 01566 } 01567 01568 if (completion >= 100) { 01569 01571 if (wfm || 01572 (model && 01573 (wfm = dynamic_cast<RangeSummarisableTimeValueModel *> 01574 (model->getSourceModel())))) { 01575 completion = wfm->getAlignmentCompletion(); 01576 // SVDEBUG << "View::checkProgress: Alignment completion = " << completion << endl; 01577 if (completion < 100) { 01578 text = tr("Alignment"); 01579 } 01580 } 01581 01582 } else if (wfm) { 01583 update(); // ensure duration &c gets updated 01584 } 01585 01586 if (completion >= 100) { 01587 01588 pb->hide(); 01589 cancel->hide(); 01590 timer->stop(); 01591 01592 } else { 01593 01594 // cerr << "progress = " << completion << endl; 01595 01596 if (!pb->isVisible()) { 01597 i->second.lastCheck = 0; 01598 timer->setInterval(2000); 01599 timer->start(); 01600 } 01601 01602 cancel->move(0, ph - pb->height()/2 - 10); 01603 cancel->show(); 01604 01605 pb->setValue(completion); 01606 pb->move(20, ph - pb->height()); 01607 01608 pb->show(); 01609 pb->update(); 01610 01611 ph -= pb->height(); 01612 } 01613 } else { 01614 if (pb->isVisible()) { 01615 ph -= pb->height(); 01616 } 01617 } 01618 } 01619 } 01620 01621 void 01622 View::progressCheckStalledTimerElapsed() 01623 { 01624 QObject *s = sender(); 01625 QTimer *t = qobject_cast<QTimer *>(s); 01626 if (!t) return; 01627 for (ProgressMap::iterator i = m_progressBars.begin(); 01628 i != m_progressBars.end(); ++i) { 01629 if (i->second.checkTimer == t) { 01630 int value = i->second.bar->value(); 01631 if (value > 0 && value == i->second.lastCheck) { 01632 i->second.bar->setMaximum(0); // indeterminate 01633 } 01634 i->second.lastCheck = value; 01635 return; 01636 } 01637 } 01638 } 01639 01640 int 01641 View::getProgressBarWidth() const 01642 { 01643 for (ProgressMap::const_iterator i = m_progressBars.begin(); 01644 i != m_progressBars.end(); ++i) { 01645 if (i->second.bar && i->second.bar->isVisible()) { 01646 return i->second.bar->width(); 01647 } 01648 } 01649 01650 return 0; 01651 } 01652 01653 void 01654 View::setPaintFont(QPainter &paint) 01655 { 01656 QFont font(paint.font()); 01657 font.setPointSize(Preferences::getInstance()->getViewFontSize()); 01658 paint.setFont(font); 01659 } 01660 01661 void 01662 View::paintEvent(QPaintEvent *e) 01663 { 01664 // Profiler prof("View::paintEvent", false); 01665 // cerr << "View::paintEvent: centre frame is " << m_centreFrame << endl; 01666 01667 if (m_layerStack.empty()) { 01668 QFrame::paintEvent(e); 01669 return; 01670 } 01671 01672 // ensure our constraints are met 01673 01681 QPainter paint; 01682 bool repaintCache = false; 01683 bool paintedCacheRect = false; 01684 01685 QRect cacheRect(rect()); 01686 01687 if (e) { 01688 cacheRect &= e->rect(); 01689 #ifdef DEBUG_VIEW_WIDGET_PAINT 01690 cerr << "paint rect " << cacheRect.width() << "x" << cacheRect.height() 01691 << ", my rect " << width() << "x" << height() << endl; 01692 #endif 01693 } 01694 01695 QRect nonCacheRect(cacheRect); 01696 01697 // If not all layers are scrollable, but some of the back layers 01698 // are, we should store only those in the cache. 01699 01700 bool layersChanged = false; 01701 LayerList scrollables = getScrollableBackLayers(true, layersChanged); 01702 LayerList nonScrollables = getNonScrollableFrontLayers(true, layersChanged); 01703 bool selectionCacheable = nonScrollables.empty(); 01704 bool haveSelections = m_manager && !m_manager->getSelections().empty(); 01705 01706 // If all the non-scrollable layers are non-opaque, then we draw 01707 // the selection rectangle behind them and cache it. If any are 01708 // opaque, however, we can't cache. 01709 // 01710 if (!selectionCacheable) { 01711 selectionCacheable = true; 01712 for (LayerList::const_iterator i = nonScrollables.begin(); 01713 i != nonScrollables.end(); ++i) { 01714 if ((*i)->isLayerOpaque()) { 01715 selectionCacheable = false; 01716 break; 01717 } 01718 } 01719 } 01720 01721 if (selectionCacheable) { 01722 QPoint localPos; 01723 bool closeToLeft, closeToRight; 01724 if (shouldIlluminateLocalSelection(localPos, closeToLeft, closeToRight)) { 01725 selectionCacheable = false; 01726 } 01727 } 01728 01729 #ifdef DEBUG_VIEW_WIDGET_PAINT 01730 cerr << "View(" << this << ")::paintEvent: have " << scrollables.size() 01731 << " scrollable back layers and " << nonScrollables.size() 01732 << " non-scrollable front layers" << endl; 01733 cerr << "haveSelections " << haveSelections << ", selectionCacheable " 01734 << selectionCacheable << ", m_selectionCached " << m_selectionCached << endl; 01735 #endif 01736 01737 if (layersChanged || scrollables.empty() || 01738 (haveSelections && (selectionCacheable != m_selectionCached))) { 01739 delete m_cache; 01740 m_cache = 0; 01741 m_selectionCached = false; 01742 } 01743 01744 if (!scrollables.empty()) { 01745 01746 #ifdef DEBUG_VIEW_WIDGET_PAINT 01747 cerr << "View(" << this << "): cache " << m_cache << ", cache zoom " 01748 << m_cacheZoomLevel << ", zoom " << m_zoomLevel << endl; 01749 #endif 01750 01751 if (!m_cache || 01752 m_cacheZoomLevel != m_zoomLevel || 01753 width() != m_cache->width() || 01754 height() != m_cache->height()) { 01755 01756 // cache is not valid 01757 01758 if (cacheRect.width() < width()/10) { 01759 delete m_cache; 01760 m_cache = 0; 01761 #ifdef DEBUG_VIEW_WIDGET_PAINT 01762 cerr << "View(" << this << ")::paintEvent: small repaint, not bothering to recreate cache" << endl; 01763 #endif 01764 } else { 01765 delete m_cache; 01766 m_cache = new QPixmap(width(), height()); 01767 #ifdef DEBUG_VIEW_WIDGET_PAINT 01768 cerr << "View(" << this << ")::paintEvent: recreated cache" << endl; 01769 #endif 01770 cacheRect = rect(); 01771 repaintCache = true; 01772 } 01773 01774 } else if (m_cacheCentreFrame != m_centreFrame) { 01775 01776 int dx = 01777 getXForFrame(m_cacheCentreFrame) - 01778 getXForFrame(m_centreFrame); 01779 01780 if (dx > -width() && dx < width()) { 01781 #ifdef PIXMAP_COPY_TO_SELF 01782 // This is not normally defined. Copying a pixmap to 01783 // itself doesn't work properly on Windows, Mac, or 01784 // X11 with the raster backend (it only works when 01785 // moving in one direction and then presumably only by 01786 // accident). It does actually seem to be fine on X11 01787 // with the native backend, but we prefer not to use 01788 // that anyway 01789 paint.begin(m_cache); 01790 paint.drawPixmap(dx, 0, *m_cache); 01791 paint.end(); 01792 #else 01793 static QPixmap *tmpPixmap = 0; 01794 if (!tmpPixmap || 01795 tmpPixmap->width() != width() || 01796 tmpPixmap->height() != height()) { 01797 delete tmpPixmap; 01798 tmpPixmap = new QPixmap(width(), height()); 01799 } 01800 paint.begin(tmpPixmap); 01801 paint.drawPixmap(0, 0, *m_cache); 01802 paint.end(); 01803 paint.begin(m_cache); 01804 paint.drawPixmap(dx, 0, *tmpPixmap); 01805 paint.end(); 01806 #endif 01807 if (dx < 0) { 01808 cacheRect = QRect(width() + dx, 0, -dx, height()); 01809 } else { 01810 cacheRect = QRect(0, 0, dx, height()); 01811 } 01812 #ifdef DEBUG_VIEW_WIDGET_PAINT 01813 cerr << "View(" << this << ")::paintEvent: scrolled cache by " << dx << endl; 01814 #endif 01815 } else { 01816 cacheRect = rect(); 01817 #ifdef DEBUG_VIEW_WIDGET_PAINT 01818 cerr << "View(" << this << ")::paintEvent: scrolling too far" << endl; 01819 #endif 01820 } 01821 repaintCache = true; 01822 01823 } else { 01824 #ifdef DEBUG_VIEW_WIDGET_PAINT 01825 cerr << "View(" << this << ")::paintEvent: cache is good" << endl; 01826 #endif 01827 paint.begin(this); 01828 paint.drawPixmap(cacheRect, *m_cache, cacheRect); 01829 paint.end(); 01830 QFrame::paintEvent(e); 01831 paintedCacheRect = true; 01832 } 01833 01834 m_cacheCentreFrame = m_centreFrame; 01835 m_cacheZoomLevel = m_zoomLevel; 01836 } 01837 01838 #ifdef DEBUG_VIEW_WIDGET_PAINT 01839 // cerr << "View(" << this << ")::paintEvent: cacheRect " << cacheRect << ", nonCacheRect " << (nonCacheRect | cacheRect) << ", repaintCache " << repaintCache << ", paintedCacheRect " << paintedCacheRect << endl; 01840 #endif 01841 01842 // Scrollable (cacheable) items first 01843 01844 if (!paintedCacheRect) { 01845 01846 if (repaintCache) paint.begin(m_cache); 01847 else paint.begin(this); 01848 setPaintFont(paint); 01849 paint.setClipRect(cacheRect); 01850 01851 paint.setPen(getBackground()); 01852 paint.setBrush(getBackground()); 01853 paint.drawRect(cacheRect); 01854 01855 paint.setPen(getForeground()); 01856 paint.setBrush(Qt::NoBrush); 01857 01858 for (LayerList::iterator i = scrollables.begin(); i != scrollables.end(); ++i) { 01859 paint.setRenderHint(QPainter::Antialiasing, false); 01860 paint.save(); 01861 (*i)->paint(this, paint, cacheRect); 01862 paint.restore(); 01863 } 01864 01865 if (haveSelections && selectionCacheable) { 01866 drawSelections(paint); 01867 m_selectionCached = repaintCache; 01868 } 01869 01870 paint.end(); 01871 01872 if (repaintCache) { 01873 cacheRect |= (e ? e->rect() : rect()); 01874 paint.begin(this); 01875 paint.drawPixmap(cacheRect, *m_cache, cacheRect); 01876 paint.end(); 01877 } 01878 } 01879 01880 // Now non-cacheable items. We always need to redraw the 01881 // non-cacheable items across at least the area we drew of the 01882 // cacheable items. 01883 01884 nonCacheRect |= cacheRect; 01885 01886 paint.begin(this); 01887 paint.setClipRect(nonCacheRect); 01888 setPaintFont(paint); 01889 if (scrollables.empty()) { 01890 paint.setPen(getBackground()); 01891 paint.setBrush(getBackground()); 01892 paint.drawRect(nonCacheRect); 01893 } 01894 01895 paint.setPen(getForeground()); 01896 paint.setBrush(Qt::NoBrush); 01897 01898 for (LayerList::iterator i = nonScrollables.begin(); i != nonScrollables.end(); ++i) { 01899 // Profiler profiler2("View::paintEvent non-cacheable"); 01900 (*i)->paint(this, paint, nonCacheRect); 01901 } 01902 01903 paint.end(); 01904 01905 paint.begin(this); 01906 setPaintFont(paint); 01907 if (e) paint.setClipRect(e->rect()); 01908 if (!m_selectionCached) { 01909 drawSelections(paint); 01910 } 01911 paint.end(); 01912 01913 bool showPlayPointer = true; 01914 if (m_followPlay == PlaybackScrollContinuous) { 01915 showPlayPointer = false; 01916 } else if (m_playPointerFrame <= getStartFrame() || 01917 m_playPointerFrame >= getEndFrame()) { 01918 showPlayPointer = false; 01919 } else if (m_manager && !m_manager->isPlaying()) { 01920 if (m_playPointerFrame == getCentreFrame() && 01921 m_manager->shouldShowCentreLine() && 01922 m_followPlay != PlaybackIgnore) { 01923 // Don't show the play pointer when it is redundant with 01924 // the centre line 01925 showPlayPointer = false; 01926 } 01927 } 01928 01929 if (showPlayPointer) { 01930 01931 paint.begin(this); 01932 01933 int playx = getXForFrame(m_playPointerFrame); 01934 01935 paint.setPen(getForeground()); 01936 paint.drawLine(playx - 1, 0, playx - 1, height() - 1); 01937 paint.drawLine(playx + 1, 0, playx + 1, height() - 1); 01938 paint.drawPoint(playx, 0); 01939 paint.drawPoint(playx, height() - 1); 01940 paint.setPen(getBackground()); 01941 paint.drawLine(playx, 1, playx, height() - 2); 01942 01943 paint.end(); 01944 } 01945 01946 QFrame::paintEvent(e); 01947 } 01948 01949 void 01950 View::drawSelections(QPainter &paint) 01951 { 01952 if (!hasTopLayerTimeXAxis()) return; 01953 01954 MultiSelection::SelectionList selections; 01955 01956 if (m_manager) { 01957 selections = m_manager->getSelections(); 01958 if (m_manager->haveInProgressSelection()) { 01959 bool exclusive; 01960 Selection inProgressSelection = 01961 m_manager->getInProgressSelection(exclusive); 01962 if (exclusive) selections.clear(); 01963 selections.insert(inProgressSelection); 01964 } 01965 } 01966 01967 paint.save(); 01968 01969 bool translucent = !areLayerColoursSignificant(); 01970 01971 if (translucent) { 01972 paint.setBrush(QColor(150, 150, 255, 80)); 01973 } else { 01974 paint.setBrush(Qt::NoBrush); 01975 } 01976 01977 int sampleRate = getModelsSampleRate(); 01978 01979 QPoint localPos; 01980 int illuminateFrame = -1; 01981 bool closeToLeft, closeToRight; 01982 01983 if (shouldIlluminateLocalSelection(localPos, closeToLeft, closeToRight)) { 01984 illuminateFrame = getFrameForX(localPos.x()); 01985 } 01986 01987 const QFontMetrics &metrics = paint.fontMetrics(); 01988 01989 for (MultiSelection::SelectionList::iterator i = selections.begin(); 01990 i != selections.end(); ++i) { 01991 01992 int p0 = getXForFrame(alignFromReference(i->getStartFrame())); 01993 int p1 = getXForFrame(alignFromReference(i->getEndFrame())); 01994 01995 if (p1 < 0 || p0 > width()) continue; 01996 01997 #ifdef DEBUG_VIEW_WIDGET_PAINT 01998 SVDEBUG << "View::drawSelections: " << p0 << ",-1 [" << (p1-p0) << "x" << (height()+1) << "]" << endl; 01999 #endif 02000 02001 bool illuminateThis = 02002 (illuminateFrame >= 0 && i->contains(illuminateFrame)); 02003 02004 paint.setPen(QColor(150, 150, 255)); 02005 02006 if (translucent && shouldLabelSelections()) { 02007 paint.drawRect(p0, -1, p1 - p0, height() + 1); 02008 } else { 02009 // Make the top & bottom lines of the box visible if we 02010 // are lacking some of the other visual cues. There's no 02011 // particular logic to this, it's just a question of what 02012 // I happen to think looks nice. 02013 paint.drawRect(p0, 0, p1 - p0, height() - 1); 02014 } 02015 02016 if (illuminateThis) { 02017 paint.save(); 02018 paint.setPen(QPen(getForeground(), 2)); 02019 if (closeToLeft) { 02020 paint.drawLine(p0, 1, p1, 1); 02021 paint.drawLine(p0, 0, p0, height()); 02022 paint.drawLine(p0, height() - 1, p1, height() - 1); 02023 } else if (closeToRight) { 02024 paint.drawLine(p0, 1, p1, 1); 02025 paint.drawLine(p1, 0, p1, height()); 02026 paint.drawLine(p0, height() - 1, p1, height() - 1); 02027 } else { 02028 paint.setBrush(Qt::NoBrush); 02029 paint.drawRect(p0, 1, p1 - p0, height() - 2); 02030 } 02031 paint.restore(); 02032 } 02033 02034 if (sampleRate && shouldLabelSelections() && m_manager && 02035 m_manager->shouldShowSelectionExtents()) { 02036 02037 QString startText = QString("%1 / %2") 02038 .arg(QString::fromStdString 02039 (RealTime::frame2RealTime 02040 (i->getStartFrame(), sampleRate).toText(true))) 02041 .arg(i->getStartFrame()); 02042 02043 QString endText = QString(" %1 / %2") 02044 .arg(QString::fromStdString 02045 (RealTime::frame2RealTime 02046 (i->getEndFrame(), sampleRate).toText(true))) 02047 .arg(i->getEndFrame()); 02048 02049 QString durationText = QString("(%1 / %2) ") 02050 .arg(QString::fromStdString 02051 (RealTime::frame2RealTime 02052 (i->getEndFrame() - i->getStartFrame(), sampleRate) 02053 .toText(true))) 02054 .arg(i->getEndFrame() - i->getStartFrame()); 02055 02056 int sw = metrics.width(startText), 02057 ew = metrics.width(endText), 02058 dw = metrics.width(durationText); 02059 02060 int sy = metrics.ascent() + metrics.height() + 4; 02061 int ey = sy; 02062 int dy = sy + metrics.height(); 02063 02064 int sx = p0 + 2; 02065 int ex = sx; 02066 int dx = sx; 02067 02068 bool durationBothEnds = true; 02069 02070 if (sw + ew > (p1 - p0)) { 02071 ey += metrics.height(); 02072 dy += metrics.height(); 02073 durationBothEnds = false; 02074 } 02075 02076 if (ew < (p1 - p0)) { 02077 ex = p1 - 2 - ew; 02078 } 02079 02080 if (dw < (p1 - p0)) { 02081 dx = p1 - 2 - dw; 02082 } 02083 02084 paint.drawText(sx, sy, startText); 02085 paint.drawText(ex, ey, endText); 02086 paint.drawText(dx, dy, durationText); 02087 if (durationBothEnds) { 02088 paint.drawText(sx, dy, durationText); 02089 } 02090 } 02091 } 02092 02093 paint.restore(); 02094 } 02095 02096 void 02097 View::drawMeasurementRect(QPainter &paint, const Layer *topLayer, QRect r, 02098 bool focus) const 02099 { 02100 // SVDEBUG << "View::drawMeasurementRect(" << r.x() << "," << r.y() << " " 02101 // << r.width() << "x" << r.height() << ")" << endl; 02102 02103 if (r.x() + r.width() < 0 || r.x() >= width()) return; 02104 02105 if (r.width() != 0 || r.height() != 0) { 02106 paint.save(); 02107 if (focus) { 02108 paint.setPen(Qt::NoPen); 02109 QColor brushColour(Qt::black); 02110 brushColour.setAlpha(hasLightBackground() ? 15 : 40); 02111 paint.setBrush(brushColour); 02112 if (r.x() > 0) { 02113 paint.drawRect(0, 0, r.x(), height()); 02114 } 02115 if (r.x() + r.width() < width()) { 02116 paint.drawRect(r.x() + r.width(), 0, width()-r.x()-r.width(), height()); 02117 } 02118 if (r.y() > 0) { 02119 paint.drawRect(r.x(), 0, r.width(), r.y()); 02120 } 02121 if (r.y() + r.height() < height()) { 02122 paint.drawRect(r.x(), r.y() + r.height(), r.width(), height()-r.y()-r.height()); 02123 } 02124 paint.setBrush(Qt::NoBrush); 02125 } 02126 paint.setPen(Qt::green); 02127 paint.drawRect(r); 02128 paint.restore(); 02129 } else { 02130 paint.save(); 02131 paint.setPen(Qt::green); 02132 paint.drawPoint(r.x(), r.y()); 02133 paint.restore(); 02134 } 02135 02136 if (!focus) return; 02137 02138 paint.save(); 02139 QFont fn = paint.font(); 02140 if (fn.pointSize() > 8) { 02141 fn.setPointSize(fn.pointSize() - 1); 02142 paint.setFont(fn); 02143 } 02144 02145 int fontHeight = paint.fontMetrics().height(); 02146 int fontAscent = paint.fontMetrics().ascent(); 02147 02148 float v0, v1; 02149 QString u0, u1; 02150 bool b0 = false, b1 = false; 02151 02152 QString axs, ays, bxs, bys, dxs, dys; 02153 02154 int axx, axy, bxx, bxy, dxx, dxy; 02155 int aw = 0, bw = 0, dw = 0; 02156 02157 int labelCount = 0; 02158 02159 // top-left point, x-coord 02160 02161 if ((b0 = topLayer->getXScaleValue(this, r.x(), v0, u0))) { 02162 axs = QString("%1 %2").arg(v0).arg(u0); 02163 if (u0 == "Hz" && Pitch::isFrequencyInMidiRange(v0)) { 02164 axs = QString("%1 (%2)").arg(axs) 02165 .arg(Pitch::getPitchLabelForFrequency(v0)); 02166 } 02167 aw = paint.fontMetrics().width(axs); 02168 ++labelCount; 02169 } 02170 02171 // bottom-right point, x-coord 02172 02173 if (r.width() > 0) { 02174 if ((b1 = topLayer->getXScaleValue(this, r.x() + r.width(), v1, u1))) { 02175 bxs = QString("%1 %2").arg(v1).arg(u1); 02176 if (u1 == "Hz" && Pitch::isFrequencyInMidiRange(v1)) { 02177 bxs = QString("%1 (%2)").arg(bxs) 02178 .arg(Pitch::getPitchLabelForFrequency(v1)); 02179 } 02180 bw = paint.fontMetrics().width(bxs); 02181 } 02182 } 02183 02184 // dimension, width 02185 02186 if (b0 && b1 && v1 != v0 && u0 == u1) { 02187 dxs = QString("[%1 %2]").arg(fabs(v1 - v0)).arg(u1); 02188 dw = paint.fontMetrics().width(dxs); 02189 } 02190 02191 b0 = false; 02192 b1 = false; 02193 02194 // top-left point, y-coord 02195 02196 if ((b0 = topLayer->getYScaleValue(this, r.y(), v0, u0))) { 02197 ays = QString("%1 %2").arg(v0).arg(u0); 02198 if (u0 == "Hz" && Pitch::isFrequencyInMidiRange(v0)) { 02199 ays = QString("%1 (%2)").arg(ays) 02200 .arg(Pitch::getPitchLabelForFrequency(v0)); 02201 } 02202 aw = std::max(aw, paint.fontMetrics().width(ays)); 02203 ++labelCount; 02204 } 02205 02206 // bottom-right point, y-coord 02207 02208 if (r.height() > 0) { 02209 if ((b1 = topLayer->getYScaleValue(this, r.y() + r.height(), v1, u1))) { 02210 bys = QString("%1 %2").arg(v1).arg(u1); 02211 if (u1 == "Hz" && Pitch::isFrequencyInMidiRange(v1)) { 02212 bys = QString("%1 (%2)").arg(bys) 02213 .arg(Pitch::getPitchLabelForFrequency(v1)); 02214 } 02215 bw = std::max(bw, paint.fontMetrics().width(bys)); 02216 } 02217 } 02218 02219 bool bd = false; 02220 float dy = 0.f; 02221 QString du; 02222 02223 // dimension, height 02224 02225 if ((bd = topLayer->getYScaleDifference(this, r.y(), r.y() + r.height(), 02226 dy, du)) && 02227 dy != 0) { 02228 if (du != "") { 02229 if (du == "Hz") { 02230 int semis; 02231 float cents; 02232 semis = Pitch::getPitchForFrequencyDifference(v0, v1, ¢s); 02233 dys = QString("[%1 %2 (%3)]") 02234 .arg(dy).arg(du) 02235 .arg(Pitch::getLabelForPitchRange(semis, cents)); 02236 } else { 02237 dys = QString("[%1 %2]").arg(dy).arg(du); 02238 } 02239 } else { 02240 dys = QString("[%1]").arg(dy); 02241 } 02242 dw = std::max(dw, paint.fontMetrics().width(dys)); 02243 } 02244 02245 int mw = r.width(); 02246 int mh = r.height(); 02247 02248 bool edgeLabelsInside = false; 02249 bool sizeLabelsInside = false; 02250 02251 if (mw < std::max(aw, std::max(bw, dw)) + 4) { 02252 // defaults stand 02253 } else if (mw < aw + bw + 4) { 02254 if (mh > fontHeight * labelCount * 3 + 4) { 02255 edgeLabelsInside = true; 02256 sizeLabelsInside = true; 02257 } else if (mh > fontHeight * labelCount * 2 + 4) { 02258 edgeLabelsInside = true; 02259 } 02260 } else if (mw < aw + bw + dw + 4) { 02261 if (mh > fontHeight * labelCount * 3 + 4) { 02262 edgeLabelsInside = true; 02263 sizeLabelsInside = true; 02264 } else if (mh > fontHeight * labelCount + 4) { 02265 edgeLabelsInside = true; 02266 } 02267 } else { 02268 if (mh > fontHeight * labelCount + 4) { 02269 edgeLabelsInside = true; 02270 sizeLabelsInside = true; 02271 } 02272 } 02273 02274 if (edgeLabelsInside) { 02275 02276 axx = r.x() + 2; 02277 axy = r.y() + fontAscent + 2; 02278 02279 bxx = r.x() + r.width() - bw - 2; 02280 bxy = r.y() + r.height() - (labelCount-1) * fontHeight - 2; 02281 02282 } else { 02283 02284 axx = r.x() - aw - 2; 02285 axy = r.y() + fontAscent; 02286 02287 bxx = r.x() + r.width() + 2; 02288 bxy = r.y() + r.height() - (labelCount-1) * fontHeight; 02289 } 02290 02291 dxx = r.width()/2 + r.x() - dw/2; 02292 02293 if (sizeLabelsInside) { 02294 02295 dxy = r.height()/2 + r.y() - (labelCount * fontHeight)/2 + fontAscent; 02296 02297 } else { 02298 02299 dxy = r.y() + r.height() + fontAscent + 2; 02300 } 02301 02302 if (axs != "") { 02303 drawVisibleText(paint, axx, axy, axs, OutlinedText); 02304 axy += fontHeight; 02305 } 02306 02307 if (ays != "") { 02308 drawVisibleText(paint, axx, axy, ays, OutlinedText); 02309 axy += fontHeight; 02310 } 02311 02312 if (bxs != "") { 02313 drawVisibleText(paint, bxx, bxy, bxs, OutlinedText); 02314 bxy += fontHeight; 02315 } 02316 02317 if (bys != "") { 02318 drawVisibleText(paint, bxx, bxy, bys, OutlinedText); 02319 bxy += fontHeight; 02320 } 02321 02322 if (dxs != "") { 02323 drawVisibleText(paint, dxx, dxy, dxs, OutlinedText); 02324 dxy += fontHeight; 02325 } 02326 02327 if (dys != "") { 02328 drawVisibleText(paint, dxx, dxy, dys, OutlinedText); 02329 dxy += fontHeight; 02330 } 02331 02332 paint.restore(); 02333 } 02334 02335 bool 02336 View::render(QPainter &paint, int xorigin, int f0, int f1) 02337 { 02338 int x0 = f0 / m_zoomLevel; 02339 int x1 = f1 / m_zoomLevel; 02340 02341 int w = x1 - x0; 02342 02343 int origCentreFrame = m_centreFrame; 02344 02345 bool someLayersIncomplete = false; 02346 02347 for (LayerList::iterator i = m_layerStack.begin(); 02348 i != m_layerStack.end(); ++i) { 02349 02350 int c = (*i)->getCompletion(this); 02351 if (c < 100) { 02352 someLayersIncomplete = true; 02353 break; 02354 } 02355 } 02356 02357 if (someLayersIncomplete) { 02358 02359 QProgressDialog progress(tr("Waiting for layers to be ready..."), 02360 tr("Cancel"), 0, 100, this); 02361 02362 int layerCompletion = 0; 02363 02364 while (layerCompletion < 100) { 02365 02366 for (LayerList::iterator i = m_layerStack.begin(); 02367 i != m_layerStack.end(); ++i) { 02368 02369 int c = (*i)->getCompletion(this); 02370 if (i == m_layerStack.begin() || c < layerCompletion) { 02371 layerCompletion = c; 02372 } 02373 } 02374 02375 if (layerCompletion >= 100) break; 02376 02377 progress.setValue(layerCompletion); 02378 qApp->processEvents(); 02379 if (progress.wasCanceled()) { 02380 update(); 02381 return false; 02382 } 02383 02384 usleep(50000); 02385 } 02386 } 02387 02388 QProgressDialog progress(tr("Rendering image..."), 02389 tr("Cancel"), 0, w / width(), this); 02390 02391 for (int x = 0; x < w; x += width()) { 02392 02393 progress.setValue(x / width()); 02394 qApp->processEvents(); 02395 if (progress.wasCanceled()) { 02396 m_centreFrame = origCentreFrame; 02397 update(); 02398 return false; 02399 } 02400 02401 m_centreFrame = f0 + (x + width()/2) * m_zoomLevel; 02402 02403 QRect chunk(0, 0, width(), height()); 02404 02405 paint.setPen(getBackground()); 02406 paint.setBrush(getBackground()); 02407 02408 paint.drawRect(QRect(xorigin + x, 0, width(), height())); 02409 02410 paint.setPen(getForeground()); 02411 paint.setBrush(Qt::NoBrush); 02412 02413 for (LayerList::iterator i = m_layerStack.begin(); 02414 i != m_layerStack.end(); ++i) { 02415 if(!((*i)->isLayerDormant(this))){ 02416 02417 paint.setRenderHint(QPainter::Antialiasing, false); 02418 02419 paint.save(); 02420 paint.translate(xorigin + x, 0); 02421 02422 cerr << "Centre frame now: " << m_centreFrame << " drawing to " << chunk.x() + x + xorigin << ", " << chunk.width() << endl; 02423 02424 (*i)->setSynchronousPainting(true); 02425 02426 (*i)->paint(this, paint, chunk); 02427 02428 (*i)->setSynchronousPainting(false); 02429 02430 paint.restore(); 02431 } 02432 } 02433 } 02434 02435 m_centreFrame = origCentreFrame; 02436 update(); 02437 return true; 02438 } 02439 02440 QImage * 02441 View::toNewImage() 02442 { 02443 int f0 = getModelsStartFrame(); 02444 int f1 = getModelsEndFrame(); 02445 02446 return toNewImage(f0, f1); 02447 } 02448 02449 QImage * 02450 View::toNewImage(int f0, int f1) 02451 { 02452 int x0 = f0 / getZoomLevel(); 02453 int x1 = f1 / getZoomLevel(); 02454 02455 QImage *image = new QImage(x1 - x0, height(), QImage::Format_RGB32); 02456 02457 QPainter *paint = new QPainter(image); 02458 if (!render(*paint, 0, f0, f1)) { 02459 delete paint; 02460 delete image; 02461 return 0; 02462 } else { 02463 delete paint; 02464 return image; 02465 } 02466 } 02467 02468 QSize 02469 View::getImageSize() 02470 { 02471 int f0 = getModelsStartFrame(); 02472 int f1 = getModelsEndFrame(); 02473 02474 return getImageSize(f0, f1); 02475 } 02476 02477 QSize 02478 View::getImageSize(int f0, int f1) 02479 { 02480 int x0 = f0 / getZoomLevel(); 02481 int x1 = f1 / getZoomLevel(); 02482 02483 return QSize(x1 - x0, height()); 02484 } 02485 02486 void 02487 View::toXml(QTextStream &stream, 02488 QString indent, QString extraAttributes) const 02489 { 02490 stream << indent; 02491 02492 stream << QString("<view " 02493 "centre=\"%1\" " 02494 "zoom=\"%2\" " 02495 "followPan=\"%3\" " 02496 "followZoom=\"%4\" " 02497 "tracking=\"%5\" " 02498 " %6>\n") 02499 .arg(m_centreFrame) 02500 .arg(m_zoomLevel) 02501 .arg(m_followPan) 02502 .arg(m_followZoom) 02503 .arg(m_followPlay == PlaybackScrollContinuous ? "scroll" : 02504 m_followPlay == PlaybackScrollPageWithCentre ? "page" : 02505 m_followPlay == PlaybackScrollPage ? "daw" : 02506 "ignore") 02507 .arg(extraAttributes); 02508 02509 for (int i = 0; i < (int)m_fixedOrderLayers.size(); ++i) { 02510 bool visible = !m_fixedOrderLayers[i]->isLayerDormant(this); 02511 m_fixedOrderLayers[i]->toBriefXml(stream, indent + " ", 02512 QString("visible=\"%1\"") 02513 .arg(visible ? "true" : "false")); 02514 } 02515 02516 stream << indent + "</view>\n"; 02517 } 02518 02519 ViewPropertyContainer::ViewPropertyContainer(View *v) : 02520 m_v(v) 02521 { 02522 // cerr << "ViewPropertyContainer: " << this << " is owned by View " << v << endl; 02523 connect(m_v, SIGNAL(propertyChanged(PropertyContainer::PropertyName)), 02524 this, SIGNAL(propertyChanged(PropertyContainer::PropertyName))); 02525 } 02526 02527 ViewPropertyContainer::~ViewPropertyContainer() 02528 { 02529 }