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

Generated on Wed Feb 20 15:45:29 2008 for SonicVisualiser by  doxygen 1.5.1