svgui  1.9
View.cpp
Go to the documentation of this file.
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, &cents);
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 }