| Chris@127 | 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */ | 
| Chris@127 | 2 | 
| Chris@127 | 3 /* | 
| Chris@127 | 4     Sonic Visualiser | 
| Chris@127 | 5     An audio file viewer and annotation editor. | 
| Chris@127 | 6     Centre for Digital Music, Queen Mary, University of London. | 
| Chris@127 | 7     This file copyright 2006 Chris Cannam. | 
| Chris@127 | 8 | 
| Chris@127 | 9     This program is free software; you can redistribute it and/or | 
| Chris@127 | 10     modify it under the terms of the GNU General Public License as | 
| Chris@127 | 11     published by the Free Software Foundation; either version 2 of the | 
| Chris@127 | 12     License, or (at your option) any later version.  See the file | 
| Chris@127 | 13     COPYING included with this distribution for more information. | 
| Chris@127 | 14 */ | 
| Chris@127 | 15 | 
| Chris@128 | 16 #include "View.h" | 
| Chris@128 | 17 #include "layer/Layer.h" | 
| Chris@128 | 18 #include "data/model/Model.h" | 
| Chris@127 | 19 #include "base/ZoomConstraint.h" | 
| Chris@127 | 20 #include "base/Profiler.h" | 
| Chris@278 | 21 #include "base/Pitch.h" | 
| Chris@338 | 22 #include "base/Preferences.h" | 
| Chris@127 | 23 | 
| Chris@301 | 24 #include "layer/TimeRulerLayer.h" | 
| Chris@287 | 25 #include "layer/SingleColourLayer.h" | 
| Chris@301 | 26 #include "data/model/PowerOfSqrtTwoZoomConstraint.h" | 
| Chris@301 | 27 #include "data/model/RangeSummarisableTimeValueModel.h" | 
| Chris@127 | 28 | 
| Chris@797 | 29 #include "widgets/IconLoader.h" | 
| Chris@797 | 30 | 
| Chris@127 | 31 #include <QPainter> | 
| Chris@127 | 32 #include <QPaintEvent> | 
| Chris@127 | 33 #include <QRect> | 
| Chris@127 | 34 #include <QApplication> | 
| Chris@226 | 35 #include <QProgressDialog> | 
| Chris@316 | 36 #include <QTextStream> | 
| Chris@338 | 37 #include <QFont> | 
| Chris@583 | 38 #include <QMessageBox> | 
| Chris@797 | 39 #include <QPushButton> | 
| Chris@127 | 40 | 
| Chris@127 | 41 #include <iostream> | 
| Chris@127 | 42 #include <cassert> | 
| Chris@520 | 43 #include <cmath> | 
| Chris@127 | 44 | 
| Chris@545 | 45 #include <unistd.h> | 
| Chris@545 | 46 | 
| Chris@830 | 47 //#define DEBUG_VIEW 1 | 
| Chris@127 | 48 //#define DEBUG_VIEW_WIDGET_PAINT 1 | 
| Chris@127 | 49 | 
| Chris@682 | 50 | 
| Chris@127 | 51 View::View(QWidget *w, bool showProgress) : | 
| Chris@127 | 52     QFrame(w), | 
| Chris@127 | 53     m_centreFrame(0), | 
| Chris@127 | 54     m_zoomLevel(1024), | 
| Chris@127 | 55     m_followPan(true), | 
| Chris@127 | 56     m_followZoom(true), | 
| Chris@815 | 57     m_followPlay(PlaybackScrollPageWithCentre), | 
| Chris@789 | 58     m_followPlayIsDetached(false), | 
| Chris@153 | 59     m_playPointerFrame(0), | 
| Chris@127 | 60     m_showProgress(showProgress), | 
| Chris@127 | 61     m_cache(0), | 
| Chris@127 | 62     m_cacheCentreFrame(0), | 
| Chris@127 | 63     m_cacheZoomLevel(1024), | 
| Chris@127 | 64     m_selectionCached(false), | 
| Chris@127 | 65     m_deleting(false), | 
| Chris@127 | 66     m_haveSelectedLayer(false), | 
| Chris@127 | 67     m_manager(0), | 
| Chris@127 | 68     m_propertyContainer(new ViewPropertyContainer(this)) | 
| Chris@127 | 69 { | 
| Chris@728 | 70 //    cerr << "View::View(" << this << ")" << endl; | 
| Chris@127 | 71 } | 
| Chris@127 | 72 | 
| Chris@127 | 73 View::~View() | 
| Chris@127 | 74 { | 
| Chris@728 | 75 //    cerr << "View::~View(" << this << ")" << endl; | 
| Chris@127 | 76 | 
| Chris@127 | 77     m_deleting = true; | 
| Chris@127 | 78     delete m_propertyContainer; | 
| Chris@127 | 79 } | 
| Chris@127 | 80 | 
| Chris@127 | 81 PropertyContainer::PropertyList | 
| Chris@127 | 82 View::getProperties() const | 
| Chris@127 | 83 { | 
| Chris@127 | 84     PropertyContainer::PropertyList list; | 
| Chris@127 | 85     list.push_back("Global Scroll"); | 
| Chris@127 | 86     list.push_back("Global Zoom"); | 
| Chris@127 | 87     list.push_back("Follow Playback"); | 
| Chris@127 | 88     return list; | 
| Chris@127 | 89 } | 
| Chris@127 | 90 | 
| Chris@127 | 91 QString | 
| Chris@127 | 92 View::getPropertyLabel(const PropertyName &pn) const | 
| Chris@127 | 93 { | 
| Chris@127 | 94     if (pn == "Global Scroll") return tr("Global Scroll"); | 
| Chris@127 | 95     if (pn == "Global Zoom") return tr("Global Zoom"); | 
| Chris@127 | 96     if (pn == "Follow Playback") return tr("Follow Playback"); | 
| Chris@127 | 97     return ""; | 
| Chris@127 | 98 } | 
| Chris@127 | 99 | 
| Chris@127 | 100 PropertyContainer::PropertyType | 
| Chris@127 | 101 View::getPropertyType(const PropertyContainer::PropertyName &name) const | 
| Chris@127 | 102 { | 
| Chris@127 | 103     if (name == "Global Scroll") return PropertyContainer::ToggleProperty; | 
| Chris@127 | 104     if (name == "Global Zoom") return PropertyContainer::ToggleProperty; | 
| Chris@127 | 105     if (name == "Follow Playback") return PropertyContainer::ValueProperty; | 
| Chris@127 | 106     return PropertyContainer::InvalidProperty; | 
| Chris@127 | 107 } | 
| Chris@127 | 108 | 
| Chris@127 | 109 int | 
| Chris@127 | 110 View::getPropertyRangeAndValue(const PropertyContainer::PropertyName &name, | 
| Chris@216 | 111 			       int *min, int *max, int *deflt) const | 
| Chris@127 | 112 { | 
| Chris@216 | 113     if (deflt) *deflt = 1; | 
| Chris@127 | 114     if (name == "Global Scroll") return m_followPan; | 
| Chris@127 | 115     if (name == "Global Zoom") return m_followZoom; | 
| Chris@127 | 116     if (name == "Follow Playback") { | 
| Chris@127 | 117 	if (min) *min = 0; | 
| Chris@127 | 118 	if (max) *max = 2; | 
| Chris@815 | 119         if (deflt) *deflt = int(PlaybackScrollPageWithCentre); | 
| Chris@815 | 120         switch (m_followPlay) { | 
| Chris@815 | 121         case PlaybackScrollContinuous: return 0; | 
| Chris@815 | 122         case PlaybackScrollPageWithCentre: case PlaybackScrollPage: return 1; | 
| Chris@815 | 123         case PlaybackIgnore: return 2; | 
| Chris@815 | 124         } | 
| Chris@127 | 125     } | 
| Chris@127 | 126     if (min) *min = 0; | 
| Chris@127 | 127     if (max) *max = 0; | 
| Chris@216 | 128     if (deflt) *deflt = 0; | 
| Chris@127 | 129     return 0; | 
| Chris@127 | 130 } | 
| Chris@127 | 131 | 
| Chris@127 | 132 QString | 
| Chris@127 | 133 View::getPropertyValueLabel(const PropertyContainer::PropertyName &name, | 
| Chris@127 | 134 			    int value) const | 
| Chris@127 | 135 { | 
| Chris@127 | 136     if (name == "Follow Playback") { | 
| Chris@127 | 137 	switch (value) { | 
| Chris@127 | 138 	default: | 
| Chris@127 | 139 	case 0: return tr("Scroll"); | 
| Chris@127 | 140 	case 1: return tr("Page"); | 
| Chris@127 | 141 	case 2: return tr("Off"); | 
| Chris@127 | 142 	} | 
| Chris@127 | 143     } | 
| Chris@127 | 144     return tr("<unknown>"); | 
| Chris@127 | 145 } | 
| Chris@127 | 146 | 
| Chris@127 | 147 void | 
| Chris@127 | 148 View::setProperty(const PropertyContainer::PropertyName &name, int value) | 
| Chris@127 | 149 { | 
| Chris@127 | 150     if (name == "Global Scroll") { | 
| Chris@127 | 151 	setFollowGlobalPan(value != 0); | 
| Chris@127 | 152     } else if (name == "Global Zoom") { | 
| Chris@127 | 153 	setFollowGlobalZoom(value != 0); | 
| Chris@127 | 154     } else if (name == "Follow Playback") { | 
| Chris@127 | 155 	switch (value) { | 
| Chris@127 | 156 	default: | 
| Chris@127 | 157 	case 0: setPlaybackFollow(PlaybackScrollContinuous); break; | 
| Chris@815 | 158 	case 1: setPlaybackFollow(PlaybackScrollPageWithCentre); break; | 
| Chris@127 | 159 	case 2: setPlaybackFollow(PlaybackIgnore); break; | 
| Chris@127 | 160 	} | 
| Chris@127 | 161     } | 
| Chris@127 | 162 } | 
| Chris@127 | 163 | 
| Chris@806 | 164 int | 
| Chris@127 | 165 View::getPropertyContainerCount() const | 
| Chris@127 | 166 { | 
| Chris@837 | 167     return m_fixedOrderLayers.size() + 1; // the 1 is for me | 
| Chris@127 | 168 } | 
| Chris@127 | 169 | 
| Chris@127 | 170 const PropertyContainer * | 
| Chris@806 | 171 View::getPropertyContainer(int i) const | 
| Chris@127 | 172 { | 
| Chris@127 | 173     return (const PropertyContainer *)(((View *)this)-> | 
| Chris@127 | 174 				       getPropertyContainer(i)); | 
| Chris@127 | 175 } | 
| Chris@127 | 176 | 
| Chris@127 | 177 PropertyContainer * | 
| Chris@806 | 178 View::getPropertyContainer(int i) | 
| Chris@127 | 179 { | 
| Chris@127 | 180     if (i == 0) return m_propertyContainer; | 
| Chris@837 | 181     return m_fixedOrderLayers[i-1]; | 
| Chris@127 | 182 } | 
| Chris@127 | 183 | 
| Chris@127 | 184 bool | 
| Chris@127 | 185 View::getValueExtents(QString unit, float &min, float &max, bool &log) const | 
| Chris@127 | 186 { | 
| Chris@127 | 187     bool have = false; | 
| Chris@127 | 188 | 
| Chris@835 | 189     for (LayerList::const_iterator i = m_layerStack.begin(); | 
| Chris@835 | 190          i != m_layerStack.end(); ++i) { | 
| Chris@127 | 191 | 
| Chris@127 | 192         QString layerUnit; | 
| Chris@127 | 193         float layerMin = 0.0, layerMax = 0.0; | 
| Chris@127 | 194         float displayMin = 0.0, displayMax = 0.0; | 
| Chris@127 | 195         bool layerLog = false; | 
| Chris@127 | 196 | 
| Chris@127 | 197         if ((*i)->getValueExtents(layerMin, layerMax, layerLog, layerUnit) && | 
| Chris@127 | 198             layerUnit.toLower() == unit.toLower()) { | 
| Chris@127 | 199 | 
| Chris@127 | 200             if ((*i)->getDisplayExtents(displayMin, displayMax)) { | 
| Chris@127 | 201 | 
| Chris@127 | 202                 min = displayMin; | 
| Chris@127 | 203                 max = displayMax; | 
| Chris@127 | 204                 log = layerLog; | 
| Chris@127 | 205                 have = true; | 
| Chris@127 | 206                 break; | 
| Chris@127 | 207 | 
| Chris@127 | 208             } else { | 
| Chris@127 | 209 | 
| Chris@127 | 210                 if (!have || layerMin < min) min = layerMin; | 
| Chris@127 | 211                 if (!have || layerMax > max) max = layerMax; | 
| Chris@127 | 212                 if (layerLog) log = true; | 
| Chris@127 | 213                 have = true; | 
| Chris@127 | 214             } | 
| Chris@127 | 215         } | 
| Chris@127 | 216     } | 
| Chris@127 | 217 | 
| Chris@127 | 218     return have; | 
| Chris@127 | 219 } | 
| Chris@127 | 220 | 
| Chris@127 | 221 int | 
| Chris@127 | 222 View::getTextLabelHeight(const Layer *layer, QPainter &paint) const | 
| Chris@127 | 223 { | 
| Chris@127 | 224     std::map<int, Layer *> sortedLayers; | 
| Chris@127 | 225 | 
| Chris@835 | 226     for (LayerList::const_iterator i = m_layerStack.begin(); | 
| Chris@835 | 227          i != m_layerStack.end(); ++i) { | 
| Chris@127 | 228         if ((*i)->needsTextLabelHeight()) { | 
| Chris@127 | 229             sortedLayers[getObjectExportId(*i)] = *i; | 
| Chris@127 | 230         } | 
| Chris@127 | 231     } | 
| Chris@127 | 232 | 
| Chris@127 | 233     int y = 15 + paint.fontMetrics().ascent(); | 
| Chris@127 | 234 | 
| Chris@127 | 235     for (std::map<int, Layer *>::const_iterator i = sortedLayers.begin(); | 
| Chris@127 | 236          i != sortedLayers.end(); ++i) { | 
| Chris@127 | 237         if (i->second == layer) return y; | 
| Chris@127 | 238         y += paint.fontMetrics().height(); | 
| Chris@127 | 239     } | 
| Chris@127 | 240 | 
| Chris@127 | 241     return y; | 
| Chris@127 | 242 } | 
| Chris@127 | 243 | 
| Chris@127 | 244 void | 
| Chris@127 | 245 View::propertyContainerSelected(View *client, PropertyContainer *pc) | 
| Chris@127 | 246 { | 
| Chris@127 | 247     if (client != this) return; | 
| Chris@127 | 248 | 
| Chris@127 | 249     if (pc == m_propertyContainer) { | 
| Chris@127 | 250 	if (m_haveSelectedLayer) { | 
| Chris@127 | 251 	    m_haveSelectedLayer = false; | 
| Chris@127 | 252 	    update(); | 
| Chris@127 | 253 	} | 
| Chris@127 | 254 	return; | 
| Chris@127 | 255     } | 
| Chris@127 | 256 | 
| Chris@127 | 257     delete m_cache; | 
| Chris@127 | 258     m_cache = 0; | 
| Chris@127 | 259 | 
| Chris@127 | 260     Layer *selectedLayer = 0; | 
| Chris@127 | 261 | 
| Chris@835 | 262     for (LayerList::iterator i = m_layerStack.begin(); i != m_layerStack.end(); ++i) { | 
| Chris@127 | 263 	if (*i == pc) { | 
| Chris@127 | 264 	    selectedLayer = *i; | 
| Chris@835 | 265 	    m_layerStack.erase(i); | 
| Chris@127 | 266 	    break; | 
| Chris@127 | 267 	} | 
| Chris@127 | 268     } | 
| Chris@127 | 269 | 
| Chris@127 | 270     if (selectedLayer) { | 
| Chris@127 | 271 	m_haveSelectedLayer = true; | 
| Chris@835 | 272 	m_layerStack.push_back(selectedLayer); | 
| Chris@127 | 273 	update(); | 
| Chris@127 | 274     } else { | 
| Chris@127 | 275 	m_haveSelectedLayer = false; | 
| Chris@127 | 276     } | 
| Chris@298 | 277 | 
| Chris@298 | 278     emit propertyContainerSelected(pc); | 
| Chris@127 | 279 } | 
| Chris@127 | 280 | 
| Chris@127 | 281 void | 
| Chris@127 | 282 View::toolModeChanged() | 
| Chris@127 | 283 { | 
| Chris@587 | 284 //    SVDEBUG << "View::toolModeChanged(" << m_manager->getToolMode() << ")" << endl; | 
| Chris@127 | 285 } | 
| Chris@127 | 286 | 
| Chris@133 | 287 void | 
| Chris@133 | 288 View::overlayModeChanged() | 
| Chris@133 | 289 { | 
| Chris@206 | 290     delete m_cache; | 
| Chris@206 | 291     m_cache = 0; | 
| Chris@133 | 292     update(); | 
| Chris@133 | 293 } | 
| Chris@133 | 294 | 
| Chris@133 | 295 void | 
| Chris@133 | 296 View::zoomWheelsEnabledChanged() | 
| Chris@133 | 297 { | 
| Chris@133 | 298     // subclass might override this | 
| Chris@133 | 299 } | 
| Chris@133 | 300 | 
| Chris@806 | 301 int | 
| Chris@127 | 302 View::getStartFrame() const | 
| Chris@127 | 303 { | 
| Chris@313 | 304     return getFrameForX(0); | 
| Chris@127 | 305 } | 
| Chris@127 | 306 | 
| Chris@806 | 307 int | 
| Chris@127 | 308 View::getEndFrame() const | 
| Chris@127 | 309 { | 
| Chris@127 | 310     return getFrameForX(width()) - 1; | 
| Chris@127 | 311 } | 
| Chris@127 | 312 | 
| Chris@127 | 313 void | 
| Chris@806 | 314 View::setStartFrame(int f) | 
| Chris@127 | 315 { | 
| Chris@127 | 316     setCentreFrame(f + m_zoomLevel * (width() / 2)); | 
| Chris@127 | 317 } | 
| Chris@127 | 318 | 
| Chris@127 | 319 bool | 
| Chris@806 | 320 View::setCentreFrame(int f, bool e) | 
| Chris@127 | 321 { | 
| Chris@127 | 322     bool changeVisible = false; | 
| Chris@127 | 323 | 
| Chris@127 | 324     if (m_centreFrame != f) { | 
| Chris@127 | 325 | 
| Chris@127 | 326 	int formerPixel = m_centreFrame / m_zoomLevel; | 
| Chris@127 | 327 | 
| Chris@127 | 328 	m_centreFrame = f; | 
| Chris@127 | 329 | 
| Chris@127 | 330 	int newPixel = m_centreFrame / m_zoomLevel; | 
| Chris@127 | 331 | 
| Chris@127 | 332 	if (newPixel != formerPixel) { | 
| Chris@127 | 333 | 
| Chris@127 | 334 #ifdef DEBUG_VIEW_WIDGET_PAINT | 
| Chris@682 | 335 	    cout << "View(" << this << ")::setCentreFrame: newPixel " << newPixel << ", formerPixel " << formerPixel << endl; | 
| Chris@127 | 336 #endif | 
| Chris@127 | 337 	    update(); | 
| Chris@127 | 338 | 
| Chris@127 | 339 	    changeVisible = true; | 
| Chris@127 | 340 	} | 
| Chris@127 | 341 | 
| Chris@333 | 342 	if (e) { | 
| Chris@806 | 343             int rf = alignToReference(f); | 
| Chris@830 | 344 #ifdef DEBUG_VIEW | 
| Chris@682 | 345             cerr << "View[" << this << "]::setCentreFrame(" << f | 
| Chris@333 | 346                       << "): emitting centreFrameChanged(" | 
| Chris@682 | 347                       << rf << ")" << endl; | 
| Chris@355 | 348 #endif | 
| Chris@333 | 349             emit centreFrameChanged(rf, m_followPan, m_followPlay); | 
| Chris@333 | 350         } | 
| Chris@127 | 351     } | 
| Chris@127 | 352 | 
| Chris@127 | 353     return changeVisible; | 
| Chris@127 | 354 } | 
| Chris@127 | 355 | 
| Chris@127 | 356 int | 
| Chris@806 | 357 View::getXForFrame(int frame) const | 
| Chris@127 | 358 { | 
| Chris@127 | 359     return (frame - getStartFrame()) / m_zoomLevel; | 
| Chris@127 | 360 } | 
| Chris@127 | 361 | 
| Chris@806 | 362 int | 
| Chris@127 | 363 View::getFrameForX(int x) const | 
| Chris@127 | 364 { | 
| Chris@806 | 365     int z = m_zoomLevel; | 
| Chris@806 | 366     int frame = m_centreFrame - (width()/2) * z; | 
| Chris@354 | 367 | 
| Chris@355 | 368 #ifdef DEBUG_VIEW_WIDGET_PAINT | 
| Chris@587 | 369     SVDEBUG << "View::getFrameForX(" << x << "): z = " << z << ", m_centreFrame = " << m_centreFrame << ", width() = " << width() << ", frame = " << frame << endl; | 
| Chris@355 | 370 #endif | 
| Chris@354 | 371 | 
| Chris@313 | 372     frame = (frame / z) * z; // this is start frame | 
| Chris@313 | 373     return frame + x * z; | 
| Chris@127 | 374 } | 
| Chris@127 | 375 | 
| Chris@127 | 376 float | 
| Chris@127 | 377 View::getYForFrequency(float frequency, | 
| Chris@127 | 378 		       float minf, | 
| Chris@127 | 379 		       float maxf, | 
| Chris@127 | 380 		       bool logarithmic) const | 
| Chris@127 | 381 { | 
| Chris@382 | 382     Profiler profiler("View::getYForFrequency"); | 
| Chris@382 | 383 | 
| Chris@127 | 384     int h = height(); | 
| Chris@127 | 385 | 
| Chris@127 | 386     if (logarithmic) { | 
| Chris@127 | 387 | 
| Chris@127 | 388 	static float lastminf = 0.0, lastmaxf = 0.0; | 
| Chris@127 | 389 	static float logminf = 0.0, logmaxf = 0.0; | 
| Chris@127 | 390 | 
| Chris@127 | 391 	if (lastminf != minf) { | 
| Chris@127 | 392 	    lastminf = (minf == 0.0 ? 1.0 : minf); | 
| Chris@127 | 393 	    logminf = log10f(minf); | 
| Chris@127 | 394 	} | 
| Chris@127 | 395 	if (lastmaxf != maxf) { | 
| Chris@127 | 396 	    lastmaxf = (maxf < lastminf ? lastminf : maxf); | 
| Chris@127 | 397 	    logmaxf = log10f(maxf); | 
| Chris@127 | 398 	} | 
| Chris@127 | 399 | 
| Chris@127 | 400 	if (logminf == logmaxf) return 0; | 
| Chris@127 | 401 	return h - (h * (log10f(frequency) - logminf)) / (logmaxf - logminf); | 
| Chris@127 | 402 | 
| Chris@127 | 403     } else { | 
| Chris@127 | 404 | 
| Chris@127 | 405 	if (minf == maxf) return 0; | 
| Chris@127 | 406 	return h - (h * (frequency - minf)) / (maxf - minf); | 
| Chris@127 | 407     } | 
| Chris@127 | 408 } | 
| Chris@127 | 409 | 
| Chris@127 | 410 float | 
| Chris@127 | 411 View::getFrequencyForY(int y, | 
| Chris@127 | 412 		       float minf, | 
| Chris@127 | 413 		       float maxf, | 
| Chris@127 | 414 		       bool logarithmic) const | 
| Chris@127 | 415 { | 
| Chris@127 | 416     int h = height(); | 
| Chris@127 | 417 | 
| Chris@127 | 418     if (logarithmic) { | 
| Chris@127 | 419 | 
| Chris@127 | 420 	static float lastminf = 0.0, lastmaxf = 0.0; | 
| Chris@127 | 421 	static float logminf = 0.0, logmaxf = 0.0; | 
| Chris@127 | 422 | 
| Chris@127 | 423 	if (lastminf != minf) { | 
| Chris@127 | 424 	    lastminf = (minf == 0.0 ? 1.0 : minf); | 
| Chris@127 | 425 	    logminf = log10f(minf); | 
| Chris@127 | 426 	} | 
| Chris@127 | 427 	if (lastmaxf != maxf) { | 
| Chris@127 | 428 	    lastmaxf = (maxf < lastminf ? lastminf : maxf); | 
| Chris@127 | 429 	    logmaxf = log10f(maxf); | 
| Chris@127 | 430 	} | 
| Chris@127 | 431 | 
| Chris@127 | 432 	if (logminf == logmaxf) return 0; | 
| Chris@127 | 433 	return pow(10.f, logminf + ((logmaxf - logminf) * (h - y)) / h); | 
| Chris@127 | 434 | 
| Chris@127 | 435     } else { | 
| Chris@127 | 436 | 
| Chris@127 | 437 	if (minf == maxf) return 0; | 
| Chris@127 | 438 	return minf + ((h - y) * (maxf - minf)) / h; | 
| Chris@127 | 439     } | 
| Chris@127 | 440 } | 
| Chris@127 | 441 | 
| Chris@127 | 442 int | 
| Chris@127 | 443 View::getZoomLevel() const | 
| Chris@127 | 444 { | 
| Chris@127 | 445 #ifdef DEBUG_VIEW_WIDGET_PAINT | 
| Chris@682 | 446 //	cout << "zoom level: " << m_zoomLevel << endl; | 
| Chris@127 | 447 #endif | 
| Chris@127 | 448     return m_zoomLevel; | 
| Chris@127 | 449 } | 
| Chris@127 | 450 | 
| Chris@127 | 451 void | 
| Chris@806 | 452 View::setZoomLevel(int z) | 
| Chris@127 | 453 { | 
| Chris@462 | 454     if (z < 1) z = 1; | 
| Chris@127 | 455     if (m_zoomLevel != int(z)) { | 
| Chris@127 | 456 	m_zoomLevel = z; | 
| Chris@222 | 457 	emit zoomLevelChanged(z, m_followZoom); | 
| Chris@127 | 458 	update(); | 
| Chris@127 | 459     } | 
| Chris@127 | 460 } | 
| Chris@127 | 461 | 
| Chris@224 | 462 bool | 
| Chris@224 | 463 View::hasLightBackground() const | 
| Chris@224 | 464 { | 
| Chris@287 | 465     bool darkPalette = false; | 
| Chris@292 | 466     if (m_manager) darkPalette = m_manager->getGlobalDarkBackground(); | 
| Chris@287 | 467 | 
| Chris@287 | 468     Layer::ColourSignificance maxSignificance = Layer::ColourAbsent; | 
| Chris@287 | 469     bool mostSignificantHasDarkBackground = false; | 
| Chris@287 | 470 | 
| Chris@835 | 471     for (LayerList::const_iterator i = m_layerStack.begin(); | 
| Chris@835 | 472          i != m_layerStack.end(); ++i) { | 
| Chris@287 | 473 | 
| Chris@287 | 474         Layer::ColourSignificance s = (*i)->getLayerColourSignificance(); | 
| Chris@287 | 475         bool light = (*i)->hasLightBackground(); | 
| Chris@287 | 476 | 
| Chris@287 | 477         if (int(s) > int(maxSignificance)) { | 
| Chris@287 | 478             maxSignificance = s; | 
| Chris@287 | 479             mostSignificantHasDarkBackground = !light; | 
| Chris@287 | 480         } else if (s == maxSignificance && !light) { | 
| Chris@287 | 481             mostSignificantHasDarkBackground = true; | 
| Chris@287 | 482         } | 
| Chris@224 | 483     } | 
| Chris@287 | 484 | 
| Chris@287 | 485     if (int(maxSignificance) >= int(Layer::ColourAndBackgroundSignificant)) { | 
| Chris@287 | 486         return !mostSignificantHasDarkBackground; | 
| Chris@287 | 487     } else { | 
| Chris@287 | 488         return !darkPalette; | 
| Chris@287 | 489     } | 
| Chris@287 | 490 } | 
| Chris@287 | 491 | 
| Chris@287 | 492 QColor | 
| Chris@287 | 493 View::getBackground() const | 
| Chris@287 | 494 { | 
| Chris@287 | 495     bool light = hasLightBackground(); | 
| Chris@287 | 496 | 
| Chris@287 | 497     QColor widgetbg = palette().window().color(); | 
| Chris@287 | 498     bool widgetLight = | 
| Chris@287 | 499         (widgetbg.red() + widgetbg.green() + widgetbg.blue()) > 384; | 
| Chris@287 | 500 | 
| Chris@296 | 501     if (widgetLight == light) { | 
| Chris@296 | 502         if (widgetLight) { | 
| Chris@296 | 503             return widgetbg.light(); | 
| Chris@296 | 504         } else { | 
| Chris@296 | 505             return widgetbg.dark(); | 
| Chris@296 | 506         } | 
| Chris@296 | 507     } | 
| Chris@287 | 508     else if (light) return Qt::white; | 
| Chris@287 | 509     else return Qt::black; | 
| Chris@287 | 510 } | 
| Chris@287 | 511 | 
| Chris@287 | 512 QColor | 
| Chris@287 | 513 View::getForeground() const | 
| Chris@287 | 514 { | 
| Chris@287 | 515     bool light = hasLightBackground(); | 
| Chris@287 | 516 | 
| Chris@287 | 517     QColor widgetfg = palette().text().color(); | 
| Chris@287 | 518     bool widgetLight = | 
| Chris@287 | 519         (widgetfg.red() + widgetfg.green() + widgetfg.blue()) > 384; | 
| Chris@287 | 520 | 
| Chris@287 | 521     if (widgetLight != light) return widgetfg; | 
| Chris@287 | 522     else if (light) return Qt::black; | 
| Chris@287 | 523     else return Qt::white; | 
| Chris@224 | 524 } | 
| Chris@224 | 525 | 
| Chris@127 | 526 void | 
| Chris@127 | 527 View::addLayer(Layer *layer) | 
| Chris@127 | 528 { | 
| Chris@127 | 529     delete m_cache; | 
| Chris@127 | 530     m_cache = 0; | 
| Chris@127 | 531 | 
| Chris@287 | 532     SingleColourLayer *scl = dynamic_cast<SingleColourLayer *>(layer); | 
| Chris@287 | 533     if (scl) scl->setDefaultColourFor(this); | 
| Chris@287 | 534 | 
| Chris@836 | 535     m_fixedOrderLayers.push_back(layer); | 
| Chris@835 | 536     m_layerStack.push_back(layer); | 
| Chris@127 | 537 | 
| Chris@555 | 538     QProgressBar *pb = new QProgressBar(this); | 
| Chris@555 | 539     pb->setMinimum(0); | 
| Chris@555 | 540     pb->setMaximum(0); | 
| Chris@555 | 541     pb->setFixedWidth(80); | 
| Chris@555 | 542     pb->setTextVisible(false); | 
| Chris@555 | 543 | 
| Chris@797 | 544     QPushButton *cancel = new QPushButton(this); | 
| Chris@797 | 545     cancel->setIcon(IconLoader().load("fileclose")); | 
| Chris@797 | 546     cancel->setFlat(true); | 
| Chris@797 | 547     cancel->setFixedSize(QSize(20, 20)); | 
| Chris@797 | 548     connect(cancel, SIGNAL(clicked()), this, SLOT(cancelClicked())); | 
| Chris@797 | 549 | 
| Chris@555 | 550     ProgressBarRec pbr; | 
| Chris@797 | 551     pbr.cancel = cancel; | 
| Chris@555 | 552     pbr.bar = pb; | 
| Chris@555 | 553     pbr.lastCheck = 0; | 
| Chris@555 | 554     pbr.checkTimer = new QTimer(); | 
| Chris@555 | 555     connect(pbr.checkTimer, SIGNAL(timeout()), this, | 
| Chris@555 | 556             SLOT(progressCheckStalledTimerElapsed())); | 
| Chris@555 | 557 | 
| Chris@555 | 558     m_progressBars[layer] = pbr; | 
| Chris@555 | 559 | 
| Chris@555 | 560     QFont f(pb->font()); | 
| Chris@339 | 561     int fs = Preferences::getInstance()->getViewFontSize(); | 
| Chris@339 | 562     f.setPointSize(std::min(fs, int(ceil(fs * 0.85)))); | 
| Chris@339 | 563 | 
| Chris@797 | 564     cancel->hide(); | 
| Chris@797 | 565 | 
| Chris@555 | 566     pb->setFont(f); | 
| Chris@555 | 567     pb->hide(); | 
| Chris@127 | 568 | 
| Chris@127 | 569     connect(layer, SIGNAL(layerParametersChanged()), | 
| Chris@127 | 570 	    this,    SLOT(layerParametersChanged())); | 
| Chris@197 | 571     connect(layer, SIGNAL(layerParameterRangesChanged()), | 
| Chris@197 | 572 	    this,    SLOT(layerParameterRangesChanged())); | 
| Chris@268 | 573     connect(layer, SIGNAL(layerMeasurementRectsChanged()), | 
| Chris@268 | 574 	    this,    SLOT(layerMeasurementRectsChanged())); | 
| Chris@127 | 575     connect(layer, SIGNAL(layerNameChanged()), | 
| Chris@127 | 576 	    this,    SLOT(layerNameChanged())); | 
| Chris@127 | 577     connect(layer, SIGNAL(modelChanged()), | 
| Chris@127 | 578 	    this,    SLOT(modelChanged())); | 
| Chris@127 | 579     connect(layer, SIGNAL(modelCompletionChanged()), | 
| Chris@127 | 580 	    this,    SLOT(modelCompletionChanged())); | 
| Chris@320 | 581     connect(layer, SIGNAL(modelAlignmentCompletionChanged()), | 
| Chris@320 | 582 	    this,    SLOT(modelAlignmentCompletionChanged())); | 
| Chris@806 | 583     connect(layer, SIGNAL(modelChangedWithin(int, int)), | 
| Chris@806 | 584 	    this,    SLOT(modelChangedWithin(int, int))); | 
| Chris@127 | 585     connect(layer, SIGNAL(modelReplaced()), | 
| Chris@127 | 586 	    this,    SLOT(modelReplaced())); | 
| Chris@127 | 587 | 
| Chris@127 | 588     update(); | 
| Chris@127 | 589 | 
| Chris@127 | 590     emit propertyContainerAdded(layer); | 
| Chris@127 | 591 } | 
| Chris@127 | 592 | 
| Chris@127 | 593 void | 
| Chris@127 | 594 View::removeLayer(Layer *layer) | 
| Chris@127 | 595 { | 
| Chris@127 | 596     if (m_deleting) { | 
| Chris@127 | 597 	return; | 
| Chris@127 | 598     } | 
| Chris@127 | 599 | 
| Chris@127 | 600     delete m_cache; | 
| Chris@127 | 601     m_cache = 0; | 
| Chris@127 | 602 | 
| Chris@836 | 603     for (LayerList::iterator i = m_fixedOrderLayers.begin(); | 
| Chris@836 | 604          i != m_fixedOrderLayers.end(); | 
| Chris@836 | 605          ++i) { | 
| Chris@836 | 606 	if (*i == layer) { | 
| Chris@836 | 607 	    m_fixedOrderLayers.erase(i); | 
| Chris@836 | 608             break; | 
| Chris@836 | 609         } | 
| Chris@836 | 610     } | 
| Chris@836 | 611 | 
| Chris@836 | 612     for (LayerList::iterator i = m_layerStack.begin(); | 
| Chris@836 | 613          i != m_layerStack.end(); | 
| Chris@836 | 614          ++i) { | 
| Chris@127 | 615 	if (*i == layer) { | 
| Chris@835 | 616 	    m_layerStack.erase(i); | 
| Chris@127 | 617 	    if (m_progressBars.find(layer) != m_progressBars.end()) { | 
| Chris@555 | 618 		delete m_progressBars[layer].bar; | 
| Chris@797 | 619                 delete m_progressBars[layer].cancel; | 
| Chris@555 | 620 		delete m_progressBars[layer].checkTimer; | 
| Chris@127 | 621 		m_progressBars.erase(layer); | 
| Chris@127 | 622 	    } | 
| Chris@127 | 623 	    break; | 
| Chris@127 | 624 	} | 
| Chris@127 | 625     } | 
| Chris@127 | 626 | 
| Chris@197 | 627     disconnect(layer, SIGNAL(layerParametersChanged()), | 
| Chris@197 | 628                this,    SLOT(layerParametersChanged())); | 
| Chris@197 | 629     disconnect(layer, SIGNAL(layerParameterRangesChanged()), | 
| Chris@197 | 630                this,    SLOT(layerParameterRangesChanged())); | 
| Chris@197 | 631     disconnect(layer, SIGNAL(layerNameChanged()), | 
| Chris@197 | 632                this,    SLOT(layerNameChanged())); | 
| Chris@197 | 633     disconnect(layer, SIGNAL(modelChanged()), | 
| Chris@197 | 634                this,    SLOT(modelChanged())); | 
| Chris@197 | 635     disconnect(layer, SIGNAL(modelCompletionChanged()), | 
| Chris@197 | 636                this,    SLOT(modelCompletionChanged())); | 
| Chris@320 | 637     disconnect(layer, SIGNAL(modelAlignmentCompletionChanged()), | 
| Chris@320 | 638                this,    SLOT(modelAlignmentCompletionChanged())); | 
| Chris@806 | 639     disconnect(layer, SIGNAL(modelChangedWithin(int, int)), | 
| Chris@806 | 640                this,    SLOT(modelChangedWithin(int, int))); | 
| Chris@197 | 641     disconnect(layer, SIGNAL(modelReplaced()), | 
| Chris@197 | 642                this,    SLOT(modelReplaced())); | 
| Chris@197 | 643 | 
| Chris@127 | 644     update(); | 
| Chris@127 | 645 | 
| Chris@127 | 646     emit propertyContainerRemoved(layer); | 
| Chris@127 | 647 } | 
| Chris@127 | 648 | 
| Chris@127 | 649 Layer * | 
| Chris@834 | 650 View::getInteractionLayer() | 
| Chris@834 | 651 { | 
| Chris@834 | 652     Layer *sl = getSelectedLayer(); | 
| Chris@834 | 653     if (sl && !(sl->isLayerDormant(this))) { | 
| Chris@834 | 654         return sl; | 
| Chris@834 | 655     } | 
| Chris@835 | 656     if (!m_layerStack.empty()) { | 
| Chris@834 | 657         int n = getLayerCount(); | 
| Chris@834 | 658         while (n > 0) { | 
| Chris@834 | 659             --n; | 
| Chris@834 | 660             Layer *layer = getLayer(n); | 
| Chris@834 | 661             if (!(layer->isLayerDormant(this))) { | 
| Chris@834 | 662                 return layer; | 
| Chris@834 | 663             } | 
| Chris@834 | 664         } | 
| Chris@834 | 665     } | 
| Chris@834 | 666     return 0; | 
| Chris@834 | 667 } | 
| Chris@834 | 668 | 
| Chris@834 | 669 Layer * | 
| Chris@127 | 670 View::getSelectedLayer() | 
| Chris@127 | 671 { | 
| Chris@835 | 672     if (m_haveSelectedLayer && !m_layerStack.empty()) { | 
| Chris@839 | 673         return getLayer(getLayerCount() - 1); | 
| Chris@127 | 674     } else { | 
| Chris@127 | 675 	return 0; | 
| Chris@127 | 676     } | 
| Chris@127 | 677 } | 
| Chris@127 | 678 | 
| Chris@127 | 679 const Layer * | 
| Chris@127 | 680 View::getSelectedLayer() const | 
| Chris@127 | 681 { | 
| Chris@127 | 682     return const_cast<const Layer *>(const_cast<View *>(this)->getSelectedLayer()); | 
| Chris@127 | 683 } | 
| Chris@127 | 684 | 
| Chris@127 | 685 void | 
| Chris@127 | 686 View::setViewManager(ViewManager *manager) | 
| Chris@127 | 687 { | 
| Chris@127 | 688     if (m_manager) { | 
| Chris@806 | 689 	m_manager->disconnect(this, SLOT(globalCentreFrameChanged(int))); | 
| Chris@806 | 690 	m_manager->disconnect(this, SLOT(viewCentreFrameChanged(View *, int))); | 
| Chris@806 | 691 	m_manager->disconnect(this, SLOT(viewManagerPlaybackFrameChanged(int))); | 
| Chris@806 | 692 	m_manager->disconnect(this, SLOT(viewZoomLevelChanged(View *, int, bool))); | 
| Chris@211 | 693         m_manager->disconnect(this, SLOT(toolModeChanged())); | 
| Chris@211 | 694         m_manager->disconnect(this, SLOT(selectionChanged())); | 
| Chris@211 | 695         m_manager->disconnect(this, SLOT(overlayModeChanged())); | 
| Chris@211 | 696         m_manager->disconnect(this, SLOT(zoomWheelsEnabledChanged())); | 
| Chris@806 | 697         disconnect(m_manager, SLOT(viewCentreFrameChanged(int, bool, PlaybackFollowMode))); | 
| Chris@806 | 698 	disconnect(m_manager, SLOT(zoomLevelChanged(int, bool))); | 
| Chris@127 | 699     } | 
| Chris@127 | 700 | 
| Chris@127 | 701     m_manager = manager; | 
| Chris@127 | 702 | 
| Chris@806 | 703     connect(m_manager, SIGNAL(globalCentreFrameChanged(int)), | 
| Chris@806 | 704 	    this, SLOT(globalCentreFrameChanged(int))); | 
| Chris@806 | 705     connect(m_manager, SIGNAL(viewCentreFrameChanged(View *, int)), | 
| Chris@806 | 706 	    this, SLOT(viewCentreFrameChanged(View *, int))); | 
| Chris@806 | 707     connect(m_manager, SIGNAL(playbackFrameChanged(int)), | 
| Chris@806 | 708 	    this, SLOT(viewManagerPlaybackFrameChanged(int))); | 
| Chris@806 | 709 | 
| Chris@806 | 710     connect(m_manager, SIGNAL(viewZoomLevelChanged(View *, int, bool)), | 
| Chris@806 | 711 	    this, SLOT(viewZoomLevelChanged(View *, int, bool))); | 
| Chris@211 | 712 | 
| Chris@127 | 713     connect(m_manager, SIGNAL(toolModeChanged()), | 
| Chris@127 | 714 	    this, SLOT(toolModeChanged())); | 
| Chris@127 | 715     connect(m_manager, SIGNAL(selectionChanged()), | 
| Chris@127 | 716 	    this, SLOT(selectionChanged())); | 
| Chris@127 | 717     connect(m_manager, SIGNAL(inProgressSelectionChanged()), | 
| Chris@127 | 718 	    this, SLOT(selectionChanged())); | 
| Chris@127 | 719     connect(m_manager, SIGNAL(overlayModeChanged()), | 
| Chris@133 | 720             this, SLOT(overlayModeChanged())); | 
| Chris@607 | 721     connect(m_manager, SIGNAL(showCentreLineChanged()), | 
| Chris@607 | 722             this, SLOT(overlayModeChanged())); | 
| Chris@133 | 723     connect(m_manager, SIGNAL(zoomWheelsEnabledChanged()), | 
| Chris@133 | 724             this, SLOT(zoomWheelsEnabledChanged())); | 
| Chris@127 | 725 | 
| Chris@806 | 726     connect(this, SIGNAL(centreFrameChanged(int, bool, | 
| Chris@211 | 727                                             PlaybackFollowMode)), | 
| Chris@806 | 728             m_manager, SLOT(viewCentreFrameChanged(int, bool, | 
| Chris@211 | 729                                                    PlaybackFollowMode))); | 
| Chris@211 | 730 | 
| Chris@806 | 731     connect(this, SIGNAL(zoomLevelChanged(int, bool)), | 
| Chris@806 | 732 	    m_manager, SLOT(viewZoomLevelChanged(int, bool))); | 
| Chris@127 | 733 | 
| Chris@815 | 734     switch (m_followPlay) { | 
| Chris@815 | 735 | 
| Chris@815 | 736     case PlaybackScrollPage: | 
| Chris@815 | 737     case PlaybackScrollPageWithCentre: | 
| Chris@364 | 738         setCentreFrame(m_manager->getGlobalCentreFrame(), false); | 
| Chris@815 | 739         break; | 
| Chris@815 | 740 | 
| Chris@815 | 741     case PlaybackScrollContinuous: | 
| Chris@236 | 742         setCentreFrame(m_manager->getPlaybackFrame(), false); | 
| Chris@815 | 743         break; | 
| Chris@815 | 744 | 
| Chris@815 | 745     case PlaybackIgnore: | 
| Chris@815 | 746         if (m_followPan) { | 
| Chris@815 | 747             setCentreFrame(m_manager->getGlobalCentreFrame(), false); | 
| Chris@815 | 748         } | 
| Chris@815 | 749         break; | 
| Chris@236 | 750     } | 
| Chris@516 | 751 | 
| Chris@236 | 752     if (m_followZoom) setZoomLevel(m_manager->getGlobalZoom()); | 
| Chris@236 | 753 | 
| Chris@511 | 754     movePlayPointer(getAlignedPlaybackFrame()); | 
| Chris@511 | 755 | 
| Chris@127 | 756     toolModeChanged(); | 
| Chris@127 | 757 } | 
| Chris@127 | 758 | 
| Chris@127 | 759 void | 
| Chris@806 | 760 View::setViewManager(ViewManager *vm, int initialCentreFrame) | 
| Chris@516 | 761 { | 
| Chris@516 | 762     setViewManager(vm); | 
| Chris@516 | 763     setCentreFrame(initialCentreFrame, false); | 
| Chris@516 | 764 } | 
| Chris@516 | 765 | 
| Chris@516 | 766 void | 
| Chris@127 | 767 View::setFollowGlobalPan(bool f) | 
| Chris@127 | 768 { | 
| Chris@127 | 769     m_followPan = f; | 
| Chris@127 | 770     emit propertyContainerPropertyChanged(m_propertyContainer); | 
| Chris@127 | 771 } | 
| Chris@127 | 772 | 
| Chris@127 | 773 void | 
| Chris@127 | 774 View::setFollowGlobalZoom(bool f) | 
| Chris@127 | 775 { | 
| Chris@127 | 776     m_followZoom = f; | 
| Chris@127 | 777     emit propertyContainerPropertyChanged(m_propertyContainer); | 
| Chris@127 | 778 } | 
| Chris@127 | 779 | 
| Chris@127 | 780 void | 
| Chris@267 | 781 View::drawVisibleText(QPainter &paint, int x, int y, QString text, TextStyle style) const | 
| Chris@127 | 782 { | 
| Chris@630 | 783     if (style == OutlinedText || style == OutlinedItalicText) { | 
| Chris@127 | 784 | 
| Chris@279 | 785         paint.save(); | 
| Chris@279 | 786 | 
| Chris@630 | 787         if (style == OutlinedItalicText) { | 
| Chris@630 | 788             QFont f(paint.font()); | 
| Chris@630 | 789             f.setItalic(true); | 
| Chris@630 | 790             paint.setFont(f); | 
| Chris@630 | 791         } | 
| Chris@630 | 792 | 
| Chris@575 | 793         QColor penColour, surroundColour, boxColour; | 
| Chris@279 | 794 | 
| Chris@287 | 795         penColour = getForeground(); | 
| Chris@287 | 796         surroundColour = getBackground(); | 
| Chris@575 | 797         boxColour = surroundColour; | 
| Chris@575 | 798         boxColour.setAlpha(127); | 
| Chris@575 | 799 | 
| Chris@575 | 800         paint.setPen(Qt::NoPen); | 
| Chris@575 | 801         paint.setBrush(boxColour); | 
| Chris@630 | 802 | 
| Chris@575 | 803         QRect r = paint.fontMetrics().boundingRect(text); | 
| Chris@575 | 804         r.translate(QPoint(x, y)); | 
| Chris@682 | 805 //        cerr << "drawVisibleText: r = " << r.x() << "," <<r.y() << " " << r.width() << "x" << r.height() << endl; | 
| Chris@575 | 806         paint.drawRect(r); | 
| Chris@575 | 807         paint.setBrush(Qt::NoBrush); | 
| Chris@279 | 808 | 
| Chris@127 | 809 	paint.setPen(surroundColour); | 
| Chris@127 | 810 | 
| Chris@127 | 811 	for (int dx = -1; dx <= 1; ++dx) { | 
| Chris@127 | 812 	    for (int dy = -1; dy <= 1; ++dy) { | 
| Chris@127 | 813 		if (!(dx || dy)) continue; | 
| Chris@127 | 814 		paint.drawText(x + dx, y + dy, text); | 
| Chris@127 | 815 	    } | 
| Chris@127 | 816 	} | 
| Chris@127 | 817 | 
| Chris@127 | 818 	paint.setPen(penColour); | 
| Chris@127 | 819 | 
| Chris@127 | 820 	paint.drawText(x, y, text); | 
| Chris@287 | 821 | 
| Chris@279 | 822         paint.restore(); | 
| Chris@127 | 823 | 
| Chris@127 | 824     } else { | 
| Chris@127 | 825 | 
| Chris@682 | 826 	cerr << "ERROR: View::drawVisibleText: Boxed style not yet implemented!" << endl; | 
| Chris@127 | 827     } | 
| Chris@127 | 828 } | 
| Chris@127 | 829 | 
| Chris@127 | 830 void | 
| Chris@127 | 831 View::setPlaybackFollow(PlaybackFollowMode m) | 
| Chris@127 | 832 { | 
| Chris@127 | 833     m_followPlay = m; | 
| Chris@127 | 834     emit propertyContainerPropertyChanged(m_propertyContainer); | 
| Chris@127 | 835 } | 
| Chris@127 | 836 | 
| Chris@127 | 837 void | 
| Chris@127 | 838 View::modelChanged() | 
| Chris@127 | 839 { | 
| Chris@127 | 840     QObject *obj = sender(); | 
| Chris@127 | 841 | 
| Chris@127 | 842 #ifdef DEBUG_VIEW_WIDGET_PAINT | 
| Chris@682 | 843     cerr << "View(" << this << ")::modelChanged()" << endl; | 
| Chris@127 | 844 #endif | 
| Chris@127 | 845 | 
| Chris@127 | 846     // If the model that has changed is not used by any of the cached | 
| Chris@127 | 847     // layers, we won't need to recreate the cache | 
| Chris@127 | 848 | 
| Chris@127 | 849     bool recreate = false; | 
| Chris@127 | 850 | 
| Chris@127 | 851     bool discard; | 
| Chris@127 | 852     LayerList scrollables = getScrollableBackLayers(false, discard); | 
| Chris@127 | 853     for (LayerList::const_iterator i = scrollables.begin(); | 
| Chris@127 | 854 	 i != scrollables.end(); ++i) { | 
| Chris@127 | 855 	if (*i == obj || (*i)->getModel() == obj) { | 
| Chris@127 | 856 	    recreate = true; | 
| Chris@127 | 857 	    break; | 
| Chris@127 | 858 	} | 
| Chris@127 | 859     } | 
| Chris@127 | 860 | 
| Chris@127 | 861     if (recreate) { | 
| Chris@127 | 862 	delete m_cache; | 
| Chris@127 | 863 	m_cache = 0; | 
| Chris@127 | 864     } | 
| Chris@127 | 865 | 
| Chris@336 | 866     emit layerModelChanged(); | 
| Chris@336 | 867 | 
| Chris@127 | 868     checkProgress(obj); | 
| Chris@127 | 869 | 
| Chris@127 | 870     update(); | 
| Chris@127 | 871 } | 
| Chris@127 | 872 | 
| Chris@127 | 873 void | 
| Chris@806 | 874 View::modelChangedWithin(int startFrame, int endFrame) | 
| Chris@127 | 875 { | 
| Chris@127 | 876     QObject *obj = sender(); | 
| Chris@127 | 877 | 
| Chris@806 | 878     int myStartFrame = getStartFrame(); | 
| Chris@806 | 879     int myEndFrame = getEndFrame(); | 
| Chris@127 | 880 | 
| Chris@127 | 881 #ifdef DEBUG_VIEW_WIDGET_PAINT | 
| Chris@806 | 882     cerr << "View(" << this << ")::modelChangedWithin(" << startFrame << "," << endFrame << ") [me " << myStartFrame << "," << myEndFrame << "]" << endl; | 
| Chris@127 | 883 #endif | 
| Chris@127 | 884 | 
| Chris@806 | 885     if (myStartFrame > 0 && endFrame < int(myStartFrame)) { | 
| Chris@127 | 886 	checkProgress(obj); | 
| Chris@127 | 887 	return; | 
| Chris@127 | 888     } | 
| Chris@127 | 889     if (startFrame > myEndFrame) { | 
| Chris@127 | 890 	checkProgress(obj); | 
| Chris@127 | 891 	return; | 
| Chris@127 | 892     } | 
| Chris@127 | 893 | 
| Chris@127 | 894     // If the model that has changed is not used by any of the cached | 
| Chris@127 | 895     // layers, we won't need to recreate the cache | 
| Chris@127 | 896 | 
| Chris@127 | 897     bool recreate = false; | 
| Chris@127 | 898 | 
| Chris@127 | 899     bool discard; | 
| Chris@127 | 900     LayerList scrollables = getScrollableBackLayers(false, discard); | 
| Chris@127 | 901     for (LayerList::const_iterator i = scrollables.begin(); | 
| Chris@127 | 902 	 i != scrollables.end(); ++i) { | 
| Chris@127 | 903 	if (*i == obj || (*i)->getModel() == obj) { | 
| Chris@127 | 904 	    recreate = true; | 
| Chris@127 | 905 	    break; | 
| Chris@127 | 906 	} | 
| Chris@127 | 907     } | 
| Chris@127 | 908 | 
| Chris@127 | 909     if (recreate) { | 
| Chris@127 | 910 	delete m_cache; | 
| Chris@127 | 911 	m_cache = 0; | 
| Chris@127 | 912     } | 
| Chris@127 | 913 | 
| Chris@806 | 914     if (startFrame < myStartFrame) startFrame = myStartFrame; | 
| Chris@127 | 915     if (endFrame > myEndFrame) endFrame = myEndFrame; | 
| Chris@127 | 916 | 
| Chris@127 | 917     checkProgress(obj); | 
| Chris@127 | 918 | 
| Chris@127 | 919     update(); | 
| Chris@127 | 920 } | 
| Chris@127 | 921 | 
| Chris@127 | 922 void | 
| Chris@127 | 923 View::modelCompletionChanged() | 
| Chris@127 | 924 { | 
| Chris@682 | 925 //    cerr << "View(" << this << ")::modelCompletionChanged()" << endl; | 
| Chris@301 | 926 | 
| Chris@127 | 927     QObject *obj = sender(); | 
| Chris@127 | 928     checkProgress(obj); | 
| Chris@127 | 929 } | 
| Chris@127 | 930 | 
| Chris@127 | 931 void | 
| Chris@320 | 932 View::modelAlignmentCompletionChanged() | 
| Chris@320 | 933 { | 
| Chris@682 | 934 //    cerr << "View(" << this << ")::modelAlignmentCompletionChanged()" << endl; | 
| Chris@320 | 935 | 
| Chris@320 | 936     QObject *obj = sender(); | 
| Chris@320 | 937     checkProgress(obj); | 
| Chris@320 | 938 } | 
| Chris@320 | 939 | 
| Chris@320 | 940 void | 
| Chris@127 | 941 View::modelReplaced() | 
| Chris@127 | 942 { | 
| Chris@127 | 943 #ifdef DEBUG_VIEW_WIDGET_PAINT | 
| Chris@682 | 944     cerr << "View(" << this << ")::modelReplaced()" << endl; | 
| Chris@127 | 945 #endif | 
| Chris@127 | 946     delete m_cache; | 
| Chris@127 | 947     m_cache = 0; | 
| Chris@127 | 948 | 
| Chris@127 | 949     update(); | 
| Chris@127 | 950 } | 
| Chris@127 | 951 | 
| Chris@127 | 952 void | 
| Chris@127 | 953 View::layerParametersChanged() | 
| Chris@127 | 954 { | 
| Chris@127 | 955     Layer *layer = dynamic_cast<Layer *>(sender()); | 
| Chris@127 | 956 | 
| Chris@127 | 957 #ifdef DEBUG_VIEW_WIDGET_PAINT | 
| Chris@587 | 958     SVDEBUG << "View::layerParametersChanged()" << endl; | 
| Chris@127 | 959 #endif | 
| Chris@127 | 960 | 
| Chris@127 | 961     delete m_cache; | 
| Chris@127 | 962     m_cache = 0; | 
| Chris@127 | 963     update(); | 
| Chris@127 | 964 | 
| Chris@127 | 965     if (layer) { | 
| Chris@127 | 966 	emit propertyContainerPropertyChanged(layer); | 
| Chris@127 | 967     } | 
| Chris@127 | 968 } | 
| Chris@127 | 969 | 
| Chris@127 | 970 void | 
| Chris@197 | 971 View::layerParameterRangesChanged() | 
| Chris@197 | 972 { | 
| Chris@197 | 973     Layer *layer = dynamic_cast<Layer *>(sender()); | 
| Chris@197 | 974     if (layer) emit propertyContainerPropertyRangeChanged(layer); | 
| Chris@197 | 975 } | 
| Chris@197 | 976 | 
| Chris@197 | 977 void | 
| Chris@268 | 978 View::layerMeasurementRectsChanged() | 
| Chris@268 | 979 { | 
| Chris@268 | 980     Layer *layer = dynamic_cast<Layer *>(sender()); | 
| Chris@268 | 981     if (layer) update(); | 
| Chris@268 | 982 } | 
| Chris@268 | 983 | 
| Chris@268 | 984 void | 
| Chris@127 | 985 View::layerNameChanged() | 
| Chris@127 | 986 { | 
| Chris@127 | 987     Layer *layer = dynamic_cast<Layer *>(sender()); | 
| Chris@127 | 988     if (layer) emit propertyContainerNameChanged(layer); | 
| Chris@127 | 989 } | 
| Chris@127 | 990 | 
| Chris@127 | 991 void | 
| Chris@806 | 992 View::globalCentreFrameChanged(int rf) | 
| Chris@127 | 993 { | 
| Chris@211 | 994     if (m_followPan) { | 
| Chris@806 | 995         int f = alignFromReference(rf); | 
| Chris@830 | 996 #ifdef DEBUG_VIEW | 
| Chris@682 | 997         cerr << "View[" << this << "]::globalCentreFrameChanged(" << rf | 
| Chris@682 | 998                   << "): setting centre frame to " << f << endl; | 
| Chris@363 | 999 #endif | 
| Chris@333 | 1000         setCentreFrame(f, false); | 
| Chris@127 | 1001     } | 
| Chris@127 | 1002 } | 
| Chris@127 | 1003 | 
| Chris@127 | 1004 void | 
| Chris@806 | 1005 View::viewCentreFrameChanged(View *, int ) | 
| Chris@211 | 1006 { | 
| Chris@211 | 1007     // We do nothing with this, but a subclass might | 
| Chris@211 | 1008 } | 
| Chris@211 | 1009 | 
| Chris@211 | 1010 void | 
| Chris@806 | 1011 View::viewManagerPlaybackFrameChanged(int f) | 
| Chris@127 | 1012 { | 
| Chris@127 | 1013     if (m_manager) { | 
| Chris@127 | 1014 	if (sender() != m_manager) return; | 
| Chris@127 | 1015     } | 
| Chris@127 | 1016 | 
| Chris@830 | 1017 #ifdef DEBUG_VIEW | 
| Chris@830 | 1018     cerr << "View::viewManagerPlaybackFrameChanged(" << f << ")" << endl; | 
| Chris@830 | 1019 #endif | 
| Chris@830 | 1020 | 
| Chris@301 | 1021     f = getAlignedPlaybackFrame(); | 
| Chris@301 | 1022 | 
| Chris@830 | 1023 #ifdef DEBUG_VIEW | 
| Chris@830 | 1024     cerr << " -> aligned frame = " << af << endl; | 
| Chris@830 | 1025 #endif | 
| Chris@830 | 1026 | 
| Chris@511 | 1027     movePlayPointer(f); | 
| Chris@511 | 1028 } | 
| Chris@511 | 1029 | 
| Chris@511 | 1030 void | 
| Chris@806 | 1031 View::movePlayPointer(int newFrame) | 
| Chris@511 | 1032 { | 
| Chris@830 | 1033 #ifdef DEBUG_VIEW | 
| Chris@830 | 1034     cerr << "View(" << this << ")::movePlayPointer(" << newFrame << ")" << endl; | 
| Chris@830 | 1035 #endif | 
| Chris@830 | 1036 | 
| Chris@511 | 1037     if (m_playPointerFrame == newFrame) return; | 
| Chris@511 | 1038     bool visibleChange = | 
| Chris@511 | 1039         (getXForFrame(m_playPointerFrame) != getXForFrame(newFrame)); | 
| Chris@806 | 1040     int oldPlayPointerFrame = m_playPointerFrame; | 
| Chris@511 | 1041     m_playPointerFrame = newFrame; | 
| Chris@511 | 1042     if (!visibleChange) return; | 
| Chris@127 | 1043 | 
| Chris@513 | 1044     bool somethingGoingOn = | 
| Chris@513 | 1045         ((QApplication::mouseButtons() != Qt::NoButton) || | 
| Chris@513 | 1046          (QApplication::keyboardModifiers() & Qt::AltModifier)); | 
| Chris@513 | 1047 | 
| Chris@789 | 1048     bool pointerInVisibleArea = | 
| Chris@789 | 1049 	long(m_playPointerFrame) >= getStartFrame() && | 
| Chris@789 | 1050         (m_playPointerFrame < getEndFrame() || | 
| Chris@789 | 1051          // include old pointer location so we know to refresh when moving out | 
| Chris@789 | 1052          oldPlayPointerFrame < getEndFrame()); | 
| Chris@789 | 1053 | 
| Chris@127 | 1054     switch (m_followPlay) { | 
| Chris@127 | 1055 | 
| Chris@127 | 1056     case PlaybackScrollContinuous: | 
| Chris@513 | 1057 	if (!somethingGoingOn) { | 
| Chris@511 | 1058 	    setCentreFrame(m_playPointerFrame, false); | 
| Chris@127 | 1059 	} | 
| Chris@127 | 1060 	break; | 
| Chris@127 | 1061 | 
| Chris@127 | 1062     case PlaybackScrollPage: | 
| Chris@815 | 1063     case PlaybackScrollPageWithCentre: | 
| Chris@789 | 1064 | 
| Chris@789 | 1065         if (!pointerInVisibleArea && somethingGoingOn) { | 
| Chris@789 | 1066 | 
| Chris@789 | 1067             m_followPlayIsDetached = true; | 
| Chris@789 | 1068 | 
| Chris@789 | 1069         } else if (!pointerInVisibleArea && m_followPlayIsDetached) { | 
| Chris@789 | 1070 | 
| Chris@789 | 1071             // do nothing; we aren't tracking until the pointer comes back in | 
| Chris@789 | 1072 | 
| Chris@789 | 1073         } else { | 
| Chris@789 | 1074 | 
| Chris@789 | 1075             int xold = getXForFrame(oldPlayPointerFrame); | 
| Chris@789 | 1076             update(xold - 4, 0, 9, height()); | 
| Chris@789 | 1077 | 
| Chris@808 | 1078             int w = getEndFrame() - getStartFrame(); | 
| Chris@789 | 1079             w -= w/5; | 
| Chris@808 | 1080             int sf = (m_playPointerFrame / w) * w - w/8; | 
| Chris@789 | 1081 | 
| Chris@789 | 1082             if (m_manager && | 
| Chris@789 | 1083                 m_manager->isPlaying() && | 
| Chris@789 | 1084                 m_manager->getPlaySelectionMode()) { | 
| Chris@789 | 1085                 MultiSelection::SelectionList selections = m_manager->getSelections(); | 
| Chris@789 | 1086                 if (!selections.empty()) { | 
| Chris@808 | 1087                     int selectionStart = selections.begin()->getStartFrame(); | 
| Chris@808 | 1088                     if (sf < selectionStart - w / 10) { | 
| Chris@808 | 1089                         sf = selectionStart - w / 10; | 
| Chris@789 | 1090                     } | 
| Chris@789 | 1091                 } | 
| Chris@789 | 1092             } | 
| Chris@127 | 1093 | 
| Chris@127 | 1094 #ifdef DEBUG_VIEW_WIDGET_PAINT | 
| Chris@789 | 1095             cerr << "PlaybackScrollPage: f = " << m_playPointerFrame << ", sf = " << sf << ", start frame " | 
| Chris@789 | 1096                  << getStartFrame() << endl; | 
| Chris@127 | 1097 #endif | 
| Chris@127 | 1098 | 
| Chris@789 | 1099             // We don't consider scrolling unless the pointer is outside | 
| Chris@789 | 1100             // the central visible range already | 
| Chris@789 | 1101 | 
| Chris@789 | 1102             int xnew = getXForFrame(m_playPointerFrame); | 
| Chris@127 | 1103 | 
| Chris@127 | 1104 #ifdef DEBUG_VIEW_WIDGET_PAINT | 
| Chris@789 | 1105             cerr << "xnew = " << xnew << ", width = " << width() << endl; | 
| Chris@127 | 1106 #endif | 
| Chris@127 | 1107 | 
| Chris@789 | 1108             bool shouldScroll = (xnew > (width() * 7) / 8); | 
| Chris@789 | 1109 | 
| Chris@789 | 1110             if (!m_followPlayIsDetached && (xnew < width() / 8)) { | 
| Chris@789 | 1111                 shouldScroll = true; | 
| Chris@789 | 1112             } | 
| Chris@789 | 1113 | 
| Chris@789 | 1114             if (xnew > width() / 8) { | 
| Chris@789 | 1115                 m_followPlayIsDetached = false; | 
| Chris@791 | 1116             } else if (somethingGoingOn) { | 
| Chris@791 | 1117                 m_followPlayIsDetached = true; | 
| Chris@789 | 1118             } | 
| Chris@789 | 1119 | 
| Chris@789 | 1120             if (!somethingGoingOn && shouldScroll) { | 
| Chris@808 | 1121                 int offset = getFrameForX(width()/2) - getStartFrame(); | 
| Chris@808 | 1122                 int newCentre = sf + offset; | 
| Chris@789 | 1123                 bool changed = setCentreFrame(newCentre, false); | 
| Chris@789 | 1124                 if (changed) { | 
| Chris@789 | 1125                     xold = getXForFrame(oldPlayPointerFrame); | 
| Chris@789 | 1126                     update(xold - 4, 0, 9, height()); | 
| Chris@789 | 1127                 } | 
| Chris@789 | 1128             } | 
| Chris@789 | 1129 | 
| Chris@789 | 1130             update(xnew - 4, 0, 9, height()); | 
| Chris@789 | 1131         } | 
| Chris@789 | 1132         break; | 
| Chris@127 | 1133 | 
| Chris@127 | 1134     case PlaybackIgnore: | 
| Chris@806 | 1135 	if (m_playPointerFrame >= getStartFrame() && | 
| Chris@511 | 1136             m_playPointerFrame < getEndFrame()) { | 
| Chris@127 | 1137 	    update(); | 
| Chris@127 | 1138 	} | 
| Chris@127 | 1139 	break; | 
| Chris@127 | 1140     } | 
| Chris@127 | 1141 } | 
| Chris@127 | 1142 | 
| Chris@127 | 1143 void | 
| Chris@806 | 1144 View::viewZoomLevelChanged(View *p, int z, bool locked) | 
| Chris@127 | 1145 { | 
| Chris@244 | 1146 #ifdef DEBUG_VIEW_WIDGET_PAINT | 
| Chris@682 | 1147     cerr  << "View[" << this << "]: viewZoomLevelChanged(" << p << ", " << z << ", " << locked << ")" << endl; | 
| Chris@244 | 1148 #endif | 
| Chris@127 | 1149     if (m_followZoom && p != this && locked) { | 
| Chris@222 | 1150         setZoomLevel(z); | 
| Chris@127 | 1151     } | 
| Chris@127 | 1152 } | 
| Chris@127 | 1153 | 
| Chris@127 | 1154 void | 
| Chris@127 | 1155 View::selectionChanged() | 
| Chris@127 | 1156 { | 
| Chris@127 | 1157     if (m_selectionCached) { | 
| Chris@127 | 1158 	delete m_cache; | 
| Chris@127 | 1159 	m_cache = 0; | 
| Chris@127 | 1160 	m_selectionCached = false; | 
| Chris@127 | 1161     } | 
| Chris@127 | 1162     update(); | 
| Chris@127 | 1163 } | 
| Chris@127 | 1164 | 
| Chris@806 | 1165 int | 
| Chris@222 | 1166 View::getFirstVisibleFrame() const | 
| Chris@222 | 1167 { | 
| Chris@806 | 1168     int f0 = getStartFrame(); | 
| Chris@806 | 1169     int f = getModelsStartFrame(); | 
| Chris@806 | 1170     if (f0 < 0 || f0 < f) return f; | 
| Chris@222 | 1171     return f0; | 
| Chris@222 | 1172 } | 
| Chris@222 | 1173 | 
| Chris@806 | 1174 int | 
| Chris@222 | 1175 View::getLastVisibleFrame() const | 
| Chris@222 | 1176 { | 
| Chris@806 | 1177     int f0 = getEndFrame(); | 
| Chris@806 | 1178     int f = getModelsEndFrame(); | 
| Chris@222 | 1179     if (f0 > f) return f; | 
| Chris@222 | 1180     return f0; | 
| Chris@222 | 1181 } | 
| Chris@222 | 1182 | 
| Chris@806 | 1183 int | 
| Chris@127 | 1184 View::getModelsStartFrame() const | 
| Chris@127 | 1185 { | 
| Chris@127 | 1186     bool first = true; | 
| Chris@806 | 1187     int startFrame = 0; | 
| Chris@127 | 1188 | 
| Chris@835 | 1189     for (LayerList::const_iterator i = m_layerStack.begin(); i != m_layerStack.end(); ++i) { | 
| Chris@127 | 1190 | 
| Chris@127 | 1191 	if ((*i)->getModel() && (*i)->getModel()->isOK()) { | 
| Chris@127 | 1192 | 
| Chris@806 | 1193 	    int thisStartFrame = (*i)->getModel()->getStartFrame(); | 
| Chris@127 | 1194 | 
| Chris@127 | 1195 	    if (first || thisStartFrame < startFrame) { | 
| Chris@127 | 1196 		startFrame = thisStartFrame; | 
| Chris@127 | 1197 	    } | 
| Chris@127 | 1198 	    first = false; | 
| Chris@127 | 1199 	} | 
| Chris@127 | 1200     } | 
| Chris@127 | 1201     return startFrame; | 
| Chris@127 | 1202 } | 
| Chris@127 | 1203 | 
| Chris@806 | 1204 int | 
| Chris@127 | 1205 View::getModelsEndFrame() const | 
| Chris@127 | 1206 { | 
| Chris@127 | 1207     bool first = true; | 
| Chris@806 | 1208     int endFrame = 0; | 
| Chris@127 | 1209 | 
| Chris@835 | 1210     for (LayerList::const_iterator i = m_layerStack.begin(); i != m_layerStack.end(); ++i) { | 
| Chris@127 | 1211 | 
| Chris@127 | 1212 	if ((*i)->getModel() && (*i)->getModel()->isOK()) { | 
| Chris@127 | 1213 | 
| Chris@806 | 1214 	    int thisEndFrame = (*i)->getModel()->getEndFrame(); | 
| Chris@127 | 1215 | 
| Chris@127 | 1216 	    if (first || thisEndFrame > endFrame) { | 
| Chris@127 | 1217 		endFrame = thisEndFrame; | 
| Chris@127 | 1218 	    } | 
| Chris@127 | 1219 	    first = false; | 
| Chris@127 | 1220 	} | 
| Chris@127 | 1221     } | 
| Chris@127 | 1222 | 
| Chris@127 | 1223     if (first) return getModelsStartFrame(); | 
| Chris@127 | 1224     return endFrame; | 
| Chris@127 | 1225 } | 
| Chris@127 | 1226 | 
| Chris@127 | 1227 int | 
| Chris@127 | 1228 View::getModelsSampleRate() const | 
| Chris@127 | 1229 { | 
| Chris@127 | 1230     //!!! Just go for the first, for now.  If we were supporting | 
| Chris@127 | 1231     // multiple samplerates, we'd probably want to do frame/time | 
| Chris@127 | 1232     // conversion in the model | 
| Chris@127 | 1233 | 
| Chris@159 | 1234     //!!! nah, this wants to always return the sr of the main model! | 
| Chris@159 | 1235 | 
| Chris@835 | 1236     for (LayerList::const_iterator i = m_layerStack.begin(); i != m_layerStack.end(); ++i) { | 
| Chris@127 | 1237 	if ((*i)->getModel() && (*i)->getModel()->isOK()) { | 
| Chris@127 | 1238 	    return (*i)->getModel()->getSampleRate(); | 
| Chris@127 | 1239 	} | 
| Chris@127 | 1240     } | 
| Chris@127 | 1241     return 0; | 
| Chris@127 | 1242 } | 
| Chris@127 | 1243 | 
| Chris@315 | 1244 View::ModelSet | 
| Chris@315 | 1245 View::getModels() | 
| Chris@315 | 1246 { | 
| Chris@315 | 1247     ModelSet models; | 
| Chris@315 | 1248 | 
| Chris@315 | 1249     for (int i = 0; i < getLayerCount(); ++i) { | 
| Chris@315 | 1250 | 
| Chris@315 | 1251         Layer *layer = getLayer(i); | 
| Chris@315 | 1252 | 
| Chris@315 | 1253         if (dynamic_cast<TimeRulerLayer *>(layer)) { | 
| Chris@315 | 1254             continue; | 
| Chris@315 | 1255         } | 
| Chris@315 | 1256 | 
| Chris@315 | 1257         if (layer && layer->getModel()) { | 
| Chris@315 | 1258             Model *model = layer->getModel(); | 
| Chris@315 | 1259             models.insert(model); | 
| Chris@315 | 1260         } | 
| Chris@315 | 1261     } | 
| Chris@315 | 1262 | 
| Chris@315 | 1263     return models; | 
| Chris@315 | 1264 } | 
| Chris@315 | 1265 | 
| Chris@320 | 1266 Model * | 
| Chris@320 | 1267 View::getAligningModel() const | 
| Chris@301 | 1268 { | 
| Chris@320 | 1269     if (!m_manager || | 
| Chris@320 | 1270         !m_manager->getAlignMode() || | 
| Chris@314 | 1271         !m_manager->getPlaybackModel()) { | 
| Chris@320 | 1272         return 0; | 
| Chris@314 | 1273     } | 
| Chris@301 | 1274 | 
| Chris@320 | 1275     Model *anyModel = 0; | 
| Chris@359 | 1276     Model *alignedModel = 0; | 
| Chris@320 | 1277     Model *goodModel = 0; | 
| Chris@301 | 1278 | 
| Chris@835 | 1279     for (LayerList::const_iterator i = m_layerStack.begin(); | 
| Chris@835 | 1280          i != m_layerStack.end(); ++i) { | 
| Chris@320 | 1281 | 
| Chris@320 | 1282         Layer *layer = *i; | 
| Chris@320 | 1283 | 
| Chris@320 | 1284         if (!layer) continue; | 
| Chris@320 | 1285         if (dynamic_cast<TimeRulerLayer *>(layer)) continue; | 
| Chris@301 | 1286 | 
| Chris@301 | 1287         Model *model = (*i)->getModel(); | 
| Chris@301 | 1288         if (!model) continue; | 
| Chris@301 | 1289 | 
| Chris@359 | 1290         anyModel = model; | 
| Chris@359 | 1291 | 
| Chris@320 | 1292         if (model->getAlignmentReference()) { | 
| Chris@359 | 1293             alignedModel = model; | 
| Chris@320 | 1294             if (layer->isLayerOpaque() || | 
| Chris@320 | 1295                 dynamic_cast<RangeSummarisableTimeValueModel *>(model)) { | 
| Chris@320 | 1296                 goodModel = model; | 
| Chris@320 | 1297             } | 
| Chris@301 | 1298         } | 
| Chris@320 | 1299     } | 
| Chris@301 | 1300 | 
| Chris@320 | 1301     if (goodModel) return goodModel; | 
| Chris@359 | 1302     else if (alignedModel) return alignedModel; | 
| Chris@320 | 1303     else return anyModel; | 
| Chris@320 | 1304 } | 
| Chris@320 | 1305 | 
| Chris@806 | 1306 int | 
| Chris@806 | 1307 View::alignFromReference(int f) const | 
| Chris@320 | 1308 { | 
| Chris@321 | 1309     if (!m_manager->getAlignMode()) return f; | 
| Chris@320 | 1310     Model *aligningModel = getAligningModel(); | 
| Chris@320 | 1311     if (!aligningModel) return f; | 
| Chris@320 | 1312     return aligningModel->alignFromReference(f); | 
| Chris@320 | 1313 } | 
| Chris@320 | 1314 | 
| Chris@806 | 1315 int | 
| Chris@806 | 1316 View::alignToReference(int f) const | 
| Chris@320 | 1317 { | 
| Chris@321 | 1318     if (!m_manager->getAlignMode()) return f; | 
| Chris@320 | 1319     Model *aligningModel = getAligningModel(); | 
| Chris@320 | 1320     if (!aligningModel) return f; | 
| Chris@320 | 1321     return aligningModel->alignToReference(f); | 
| Chris@320 | 1322 } | 
| Chris@320 | 1323 | 
| Chris@320 | 1324 int | 
| Chris@320 | 1325 View::getAlignedPlaybackFrame() const | 
| Chris@320 | 1326 { | 
| Chris@321 | 1327     int pf = m_manager->getPlaybackFrame(); | 
| Chris@321 | 1328     if (!m_manager->getAlignMode()) return pf; | 
| Chris@321 | 1329 | 
| Chris@320 | 1330     Model *aligningModel = getAligningModel(); | 
| Chris@320 | 1331     if (!aligningModel) return pf; | 
| Chris@830 | 1332 | 
| Chris@333 | 1333     int af = aligningModel->alignFromReference(pf); | 
| Chris@301 | 1334 | 
| Chris@301 | 1335     return af; | 
| Chris@301 | 1336 } | 
| Chris@301 | 1337 | 
| Chris@127 | 1338 bool | 
| Chris@127 | 1339 View::areLayersScrollable() const | 
| Chris@127 | 1340 { | 
| Chris@127 | 1341     // True iff all views are scrollable | 
| Chris@835 | 1342     for (LayerList::const_iterator i = m_layerStack.begin(); i != m_layerStack.end(); ++i) { | 
| Chris@127 | 1343 	if (!(*i)->isLayerScrollable(this)) return false; | 
| Chris@127 | 1344     } | 
| Chris@127 | 1345     return true; | 
| Chris@127 | 1346 } | 
| Chris@127 | 1347 | 
| Chris@127 | 1348 View::LayerList | 
| Chris@127 | 1349 View::getScrollableBackLayers(bool testChanged, bool &changed) const | 
| Chris@127 | 1350 { | 
| Chris@127 | 1351     changed = false; | 
| Chris@127 | 1352 | 
| Chris@127 | 1353     // We want a list of all the scrollable layers that are behind the | 
| Chris@127 | 1354     // backmost non-scrollable layer. | 
| Chris@127 | 1355 | 
| Chris@127 | 1356     LayerList scrollables; | 
| Chris@127 | 1357     bool metUnscrollable = false; | 
| Chris@127 | 1358 | 
| Chris@835 | 1359     for (LayerList::const_iterator i = m_layerStack.begin(); i != m_layerStack.end(); ++i) { | 
| Chris@587 | 1360 //        SVDEBUG << "View::getScrollableBackLayers: calling isLayerDormant on layer " << *i << endl; | 
| Chris@682 | 1361 //        cerr << "(name is " << (*i)->objectName() << ")" | 
| Chris@682 | 1362 //                  << endl; | 
| Chris@587 | 1363 //        SVDEBUG << "View::getScrollableBackLayers: I am " << this << endl; | 
| Chris@127 | 1364 	if ((*i)->isLayerDormant(this)) continue; | 
| Chris@127 | 1365 	if ((*i)->isLayerOpaque()) { | 
| Chris@127 | 1366 	    // You can't see anything behind an opaque layer! | 
| Chris@127 | 1367 	    scrollables.clear(); | 
| Chris@127 | 1368             if (metUnscrollable) break; | 
| Chris@127 | 1369 	} | 
| Chris@127 | 1370 	if (!metUnscrollable && (*i)->isLayerScrollable(this)) { | 
| Chris@127 | 1371             scrollables.push_back(*i); | 
| Chris@127 | 1372         } else { | 
| Chris@127 | 1373             metUnscrollable = true; | 
| Chris@127 | 1374         } | 
| Chris@127 | 1375     } | 
| Chris@127 | 1376 | 
| Chris@127 | 1377     if (testChanged && scrollables != m_lastScrollableBackLayers) { | 
| Chris@127 | 1378 	m_lastScrollableBackLayers = scrollables; | 
| Chris@127 | 1379 	changed = true; | 
| Chris@127 | 1380     } | 
| Chris@127 | 1381     return scrollables; | 
| Chris@127 | 1382 } | 
| Chris@127 | 1383 | 
| Chris@127 | 1384 View::LayerList | 
| Chris@127 | 1385 View::getNonScrollableFrontLayers(bool testChanged, bool &changed) const | 
| Chris@127 | 1386 { | 
| Chris@127 | 1387     changed = false; | 
| Chris@127 | 1388     LayerList nonScrollables; | 
| Chris@127 | 1389 | 
| Chris@127 | 1390     // Everything in front of the first non-scrollable from the back | 
| Chris@127 | 1391     // should also be considered non-scrollable | 
| Chris@127 | 1392 | 
| Chris@127 | 1393     bool started = false; | 
| Chris@127 | 1394 | 
| Chris@835 | 1395     for (LayerList::const_iterator i = m_layerStack.begin(); i != m_layerStack.end(); ++i) { | 
| Chris@127 | 1396 	if ((*i)->isLayerDormant(this)) continue; | 
| Chris@127 | 1397 	if (!started && (*i)->isLayerScrollable(this)) { | 
| Chris@127 | 1398 	    continue; | 
| Chris@127 | 1399 	} | 
| Chris@127 | 1400 	started = true; | 
| Chris@127 | 1401 	if ((*i)->isLayerOpaque()) { | 
| Chris@127 | 1402 	    // You can't see anything behind an opaque layer! | 
| Chris@127 | 1403 	    nonScrollables.clear(); | 
| Chris@127 | 1404 	} | 
| Chris@127 | 1405 	nonScrollables.push_back(*i); | 
| Chris@127 | 1406     } | 
| Chris@127 | 1407 | 
| Chris@127 | 1408     if (testChanged && nonScrollables != m_lastNonScrollableBackLayers) { | 
| Chris@127 | 1409 	m_lastNonScrollableBackLayers = nonScrollables; | 
| Chris@127 | 1410 	changed = true; | 
| Chris@127 | 1411     } | 
| Chris@127 | 1412 | 
| Chris@127 | 1413     return nonScrollables; | 
| Chris@127 | 1414 } | 
| Chris@127 | 1415 | 
| Chris@806 | 1416 int | 
| Chris@806 | 1417 View::getZoomConstraintBlockSize(int blockSize, | 
| Chris@127 | 1418 				 ZoomConstraint::RoundingDirection dir) | 
| Chris@127 | 1419     const | 
| Chris@127 | 1420 { | 
| Chris@806 | 1421     int candidate = blockSize; | 
| Chris@127 | 1422     bool haveCandidate = false; | 
| Chris@127 | 1423 | 
| Chris@127 | 1424     PowerOfSqrtTwoZoomConstraint defaultZoomConstraint; | 
| Chris@127 | 1425 | 
| Chris@835 | 1426     for (LayerList::const_iterator i = m_layerStack.begin(); i != m_layerStack.end(); ++i) { | 
| Chris@127 | 1427 | 
| Chris@127 | 1428 	const ZoomConstraint *zoomConstraint = (*i)->getZoomConstraint(); | 
| Chris@127 | 1429 	if (!zoomConstraint) zoomConstraint = &defaultZoomConstraint; | 
| Chris@127 | 1430 | 
| Chris@806 | 1431 	int thisBlockSize = | 
| Chris@127 | 1432 	    zoomConstraint->getNearestBlockSize(blockSize, dir); | 
| Chris@127 | 1433 | 
| Chris@127 | 1434 	// Go for the block size that's furthest from the one | 
| Chris@127 | 1435 	// passed in.  Most of the time, that's what we want. | 
| Chris@127 | 1436 	if (!haveCandidate || | 
| Chris@127 | 1437 	    (thisBlockSize > blockSize && thisBlockSize > candidate) || | 
| Chris@127 | 1438 	    (thisBlockSize < blockSize && thisBlockSize < candidate)) { | 
| Chris@127 | 1439 	    candidate = thisBlockSize; | 
| Chris@127 | 1440 	    haveCandidate = true; | 
| Chris@127 | 1441 	} | 
| Chris@127 | 1442     } | 
| Chris@127 | 1443 | 
| Chris@127 | 1444     return candidate; | 
| Chris@127 | 1445 } | 
| Chris@127 | 1446 | 
| Chris@183 | 1447 bool | 
| Chris@183 | 1448 View::areLayerColoursSignificant() const | 
| Chris@183 | 1449 { | 
| Chris@835 | 1450     for (LayerList::const_iterator i = m_layerStack.begin(); i != m_layerStack.end(); ++i) { | 
| Chris@287 | 1451 	if ((*i)->getLayerColourSignificance() == | 
| Chris@287 | 1452             Layer::ColourHasMeaningfulValue) return true; | 
| Chris@183 | 1453         if ((*i)->isLayerOpaque()) break; | 
| Chris@183 | 1454     } | 
| Chris@183 | 1455     return false; | 
| Chris@183 | 1456 } | 
| Chris@183 | 1457 | 
| Chris@217 | 1458 bool | 
| Chris@217 | 1459 View::hasTopLayerTimeXAxis() const | 
| Chris@217 | 1460 { | 
| Chris@835 | 1461     LayerList::const_iterator i = m_layerStack.end(); | 
| Chris@835 | 1462     if (i == m_layerStack.begin()) return false; | 
| Chris@217 | 1463     --i; | 
| Chris@217 | 1464     return (*i)->hasTimeXAxis(); | 
| Chris@217 | 1465 } | 
| Chris@217 | 1466 | 
| Chris@127 | 1467 void | 
| Chris@127 | 1468 View::zoom(bool in) | 
| Chris@127 | 1469 { | 
| Chris@127 | 1470     int newZoomLevel = m_zoomLevel; | 
| Chris@127 | 1471 | 
| Chris@127 | 1472     if (in) { | 
| Chris@127 | 1473 	newZoomLevel = getZoomConstraintBlockSize(newZoomLevel - 1, | 
| Chris@127 | 1474 						  ZoomConstraint::RoundDown); | 
| Chris@127 | 1475     } else { | 
| Chris@127 | 1476 	newZoomLevel = getZoomConstraintBlockSize(newZoomLevel + 1, | 
| Chris@127 | 1477 						  ZoomConstraint::RoundUp); | 
| Chris@127 | 1478     } | 
| Chris@127 | 1479 | 
| Chris@127 | 1480     if (newZoomLevel != m_zoomLevel) { | 
| Chris@127 | 1481 	setZoomLevel(newZoomLevel); | 
| Chris@127 | 1482     } | 
| Chris@127 | 1483 } | 
| Chris@127 | 1484 | 
| Chris@127 | 1485 void | 
| Chris@510 | 1486 View::scroll(bool right, bool lots, bool e) | 
| Chris@127 | 1487 { | 
| Chris@806 | 1488     int delta; | 
| Chris@127 | 1489     if (lots) { | 
| Chris@127 | 1490 	delta = (getEndFrame() - getStartFrame()) / 2; | 
| Chris@127 | 1491     } else { | 
| Chris@127 | 1492 	delta = (getEndFrame() - getStartFrame()) / 20; | 
| Chris@127 | 1493     } | 
| Chris@127 | 1494     if (right) delta = -delta; | 
| Chris@127 | 1495 | 
| Chris@127 | 1496     if (int(m_centreFrame) < delta) { | 
| Chris@510 | 1497 	setCentreFrame(0, e); | 
| Chris@127 | 1498     } else if (int(m_centreFrame) - delta >= int(getModelsEndFrame())) { | 
| Chris@510 | 1499 	setCentreFrame(getModelsEndFrame(), e); | 
| Chris@127 | 1500     } else { | 
| Chris@510 | 1501 	setCentreFrame(m_centreFrame - delta, e); | 
| Chris@127 | 1502     } | 
| Chris@127 | 1503 } | 
| Chris@127 | 1504 | 
| Chris@127 | 1505 void | 
| Chris@797 | 1506 View::cancelClicked() | 
| Chris@797 | 1507 { | 
| Chris@797 | 1508     QPushButton *cancel = qobject_cast<QPushButton *>(sender()); | 
| Chris@797 | 1509     if (!cancel) return; | 
| Chris@797 | 1510 | 
| Chris@797 | 1511     for (ProgressMap::iterator i = m_progressBars.begin(); | 
| Chris@797 | 1512 	 i != m_progressBars.end(); ++i) { | 
| Chris@797 | 1513 | 
| Chris@797 | 1514         if (i->second.cancel == cancel) { | 
| Chris@797 | 1515 | 
| Chris@797 | 1516             Layer *layer = i->first; | 
| Chris@797 | 1517             Model *model = layer->getModel(); | 
| Chris@797 | 1518 | 
| Chris@797 | 1519             if (model) model->abandon(); | 
| Chris@797 | 1520         } | 
| Chris@797 | 1521     } | 
| Chris@797 | 1522 } | 
| Chris@797 | 1523 | 
| Chris@797 | 1524 void | 
| Chris@127 | 1525 View::checkProgress(void *object) | 
| Chris@127 | 1526 { | 
| Chris@127 | 1527     if (!m_showProgress) return; | 
| Chris@127 | 1528 | 
| Chris@127 | 1529     int ph = height(); | 
| Chris@127 | 1530 | 
| Chris@555 | 1531     for (ProgressMap::iterator i = m_progressBars.begin(); | 
| Chris@127 | 1532 	 i != m_progressBars.end(); ++i) { | 
| Chris@127 | 1533 | 
| Chris@555 | 1534         QProgressBar *pb = i->second.bar; | 
| Chris@797 | 1535         QPushButton *cancel = i->second.cancel; | 
| Chris@555 | 1536 | 
| Chris@127 | 1537 	if (i->first == object) { | 
| Chris@127 | 1538 | 
| Chris@555 | 1539             // The timer is used to test for stalls.  If the progress | 
| Chris@555 | 1540             // bar does not get updated for some length of time, the | 
| Chris@555 | 1541             // timer prompts it to go back into "indeterminate" mode | 
| Chris@555 | 1542             QTimer *timer = i->second.checkTimer; | 
| Chris@555 | 1543 | 
| Chris@127 | 1544 	    int completion = i->first->getCompletion(this); | 
| Chris@301 | 1545             QString text = i->first->getPropertyContainerName(); | 
| Chris@583 | 1546             QString error = i->first->getError(this); | 
| Chris@583 | 1547 | 
| Chris@583 | 1548             if (error != "" && error != m_lastError) { | 
| Chris@583 | 1549                 QMessageBox::critical(this, tr("Layer rendering error"), error); | 
| Chris@583 | 1550                 m_lastError = error; | 
| Chris@583 | 1551             } | 
| Chris@301 | 1552 | 
| Chris@387 | 1553             Model *model = i->first->getModel(); | 
| Chris@387 | 1554             RangeSummarisableTimeValueModel *wfm = | 
| Chris@387 | 1555                 dynamic_cast<RangeSummarisableTimeValueModel *>(model); | 
| Chris@387 | 1556 | 
| Chris@388 | 1557             if (completion > 0) { | 
| Chris@555 | 1558                 pb->setMaximum(100); // was 0, for indeterminate start | 
| Chris@388 | 1559             } | 
| Chris@388 | 1560 | 
| Chris@301 | 1561             if (completion >= 100) { | 
| Chris@301 | 1562 | 
| Chris@301 | 1563                 //!!! | 
| Chris@326 | 1564                 if (wfm || | 
| Chris@776 | 1565                     (model && | 
| Chris@776 | 1566                      (wfm = dynamic_cast<RangeSummarisableTimeValueModel *> | 
| Chris@776 | 1567                       (model->getSourceModel())))) { | 
| Chris@301 | 1568                     completion = wfm->getAlignmentCompletion(); | 
| Chris@587 | 1569 //                    SVDEBUG << "View::checkProgress: Alignment completion = " << completion << endl; | 
| Chris@301 | 1570                     if (completion < 100) { | 
| Chris@301 | 1571                         text = tr("Alignment"); | 
| Chris@301 | 1572                     } | 
| Chris@301 | 1573                 } | 
| Chris@387 | 1574 | 
| Chris@387 | 1575             } else if (wfm) { | 
| Chris@387 | 1576                 update(); // ensure duration &c gets updated | 
| Chris@301 | 1577             } | 
| Chris@127 | 1578 | 
| Chris@127 | 1579 	    if (completion >= 100) { | 
| Chris@127 | 1580 | 
| Chris@555 | 1581 		pb->hide(); | 
| Chris@797 | 1582                 cancel->hide(); | 
| Chris@555 | 1583                 timer->stop(); | 
| Chris@127 | 1584 | 
| Chris@127 | 1585 	    } else { | 
| Chris@127 | 1586 | 
| Chris@682 | 1587 //                cerr << "progress = " << completion << endl; | 
| Chris@555 | 1588 | 
| Chris@555 | 1589                 if (!pb->isVisible()) { | 
| Chris@555 | 1590                     i->second.lastCheck = 0; | 
| Chris@555 | 1591                     timer->setInterval(2000); | 
| Chris@555 | 1592                     timer->start(); | 
| Chris@555 | 1593                 } | 
| Chris@555 | 1594 | 
| Chris@797 | 1595                 cancel->move(0, ph - pb->height()/2 - 10); | 
| Chris@797 | 1596                 cancel->show(); | 
| Chris@797 | 1597 | 
| Chris@555 | 1598 		pb->setValue(completion); | 
| Chris@797 | 1599 		pb->move(20, ph - pb->height()); | 
| Chris@555 | 1600 | 
| Chris@555 | 1601 		pb->show(); | 
| Chris@555 | 1602 		pb->update(); | 
| Chris@555 | 1603 | 
| Chris@555 | 1604 		ph -= pb->height(); | 
| Chris@127 | 1605 	    } | 
| Chris@127 | 1606 	} else { | 
| Chris@555 | 1607 	    if (pb->isVisible()) { | 
| Chris@555 | 1608 		ph -= pb->height(); | 
| Chris@127 | 1609 	    } | 
| Chris@127 | 1610 	} | 
| Chris@127 | 1611     } | 
| Chris@127 | 1612 } | 
| Chris@127 | 1613 | 
| Chris@555 | 1614 void | 
| Chris@555 | 1615 View::progressCheckStalledTimerElapsed() | 
| Chris@555 | 1616 { | 
| Chris@555 | 1617     QObject *s = sender(); | 
| Chris@555 | 1618     QTimer *t = qobject_cast<QTimer *>(s); | 
| Chris@555 | 1619     if (!t) return; | 
| Chris@555 | 1620     for (ProgressMap::iterator i =  m_progressBars.begin(); | 
| Chris@555 | 1621          i != m_progressBars.end(); ++i) { | 
| Chris@555 | 1622         if (i->second.checkTimer == t) { | 
| Chris@555 | 1623             int value = i->second.bar->value(); | 
| Chris@555 | 1624             if (value > 0 && value == i->second.lastCheck) { | 
| Chris@555 | 1625                 i->second.bar->setMaximum(0); // indeterminate | 
| Chris@555 | 1626             } | 
| Chris@555 | 1627             i->second.lastCheck = value; | 
| Chris@555 | 1628             return; | 
| Chris@555 | 1629         } | 
| Chris@555 | 1630     } | 
| Chris@555 | 1631 } | 
| Chris@555 | 1632 | 
| Chris@384 | 1633 int | 
| Chris@384 | 1634 View::getProgressBarWidth() const | 
| Chris@384 | 1635 { | 
| Chris@384 | 1636     for (ProgressMap::const_iterator i = m_progressBars.begin(); | 
| Chris@384 | 1637 	 i != m_progressBars.end(); ++i) { | 
| Chris@555 | 1638         if (i->second.bar && i->second.bar->isVisible()) { | 
| Chris@555 | 1639             return i->second.bar->width(); | 
| Chris@555 | 1640         } | 
| Chris@384 | 1641     } | 
| Chris@384 | 1642 | 
| Chris@384 | 1643     return 0; | 
| Chris@384 | 1644 } | 
| Chris@384 | 1645 | 
| Chris@127 | 1646 void | 
| Chris@339 | 1647 View::setPaintFont(QPainter &paint) | 
| Chris@339 | 1648 { | 
| Chris@339 | 1649     QFont font(paint.font()); | 
| Chris@339 | 1650     font.setPointSize(Preferences::getInstance()->getViewFontSize()); | 
| Chris@339 | 1651     paint.setFont(font); | 
| Chris@339 | 1652 } | 
| Chris@339 | 1653 | 
| Chris@339 | 1654 void | 
| Chris@127 | 1655 View::paintEvent(QPaintEvent *e) | 
| Chris@127 | 1656 { | 
| Chris@127 | 1657 //    Profiler prof("View::paintEvent", false); | 
| Chris@800 | 1658 //    cerr << "View::paintEvent: centre frame is " << m_centreFrame << endl; | 
| matthiasm@785 | 1659 | 
| Chris@835 | 1660     if (m_layerStack.empty()) { | 
| Chris@127 | 1661 	QFrame::paintEvent(e); | 
| Chris@127 | 1662 	return; | 
| Chris@127 | 1663     } | 
| Chris@127 | 1664 | 
| Chris@127 | 1665     // ensure our constraints are met | 
| Chris@137 | 1666 | 
| Chris@137 | 1667 /*!!! Should we do this only if we have layers that can't support other | 
| Chris@137 | 1668   zoom levels? | 
| Chris@137 | 1669 | 
| Chris@127 | 1670     m_zoomLevel = getZoomConstraintBlockSize(m_zoomLevel, | 
| Chris@127 | 1671 					     ZoomConstraint::RoundUp); | 
| Chris@137 | 1672 */ | 
| Chris@127 | 1673 | 
| Chris@127 | 1674     QPainter paint; | 
| Chris@127 | 1675     bool repaintCache = false; | 
| Chris@127 | 1676     bool paintedCacheRect = false; | 
| Chris@127 | 1677 | 
| Chris@127 | 1678     QRect cacheRect(rect()); | 
| Chris@127 | 1679 | 
| Chris@127 | 1680     if (e) { | 
| Chris@127 | 1681 	cacheRect &= e->rect(); | 
| Chris@127 | 1682 #ifdef DEBUG_VIEW_WIDGET_PAINT | 
| Chris@682 | 1683 	cerr << "paint rect " << cacheRect.width() << "x" << cacheRect.height() | 
| Chris@682 | 1684 		  << ", my rect " << width() << "x" << height() << endl; | 
| Chris@127 | 1685 #endif | 
| Chris@127 | 1686     } | 
| Chris@127 | 1687 | 
| Chris@127 | 1688     QRect nonCacheRect(cacheRect); | 
| Chris@127 | 1689 | 
| Chris@127 | 1690     // If not all layers are scrollable, but some of the back layers | 
| Chris@127 | 1691     // are, we should store only those in the cache. | 
| Chris@127 | 1692 | 
| Chris@127 | 1693     bool layersChanged = false; | 
| Chris@127 | 1694     LayerList scrollables = getScrollableBackLayers(true, layersChanged); | 
| Chris@127 | 1695     LayerList nonScrollables = getNonScrollableFrontLayers(true, layersChanged); | 
| Chris@127 | 1696     bool selectionCacheable = nonScrollables.empty(); | 
| Chris@127 | 1697     bool haveSelections = m_manager && !m_manager->getSelections().empty(); | 
| Chris@127 | 1698 | 
| Chris@127 | 1699     // If all the non-scrollable layers are non-opaque, then we draw | 
| Chris@127 | 1700     // the selection rectangle behind them and cache it.  If any are | 
| Chris@127 | 1701     // opaque, however, we can't cache. | 
| Chris@127 | 1702     // | 
| Chris@127 | 1703     if (!selectionCacheable) { | 
| Chris@127 | 1704 	selectionCacheable = true; | 
| Chris@127 | 1705 	for (LayerList::const_iterator i = nonScrollables.begin(); | 
| Chris@127 | 1706 	     i != nonScrollables.end(); ++i) { | 
| Chris@127 | 1707 	    if ((*i)->isLayerOpaque()) { | 
| Chris@127 | 1708 		selectionCacheable = false; | 
| Chris@127 | 1709 		break; | 
| Chris@127 | 1710 	    } | 
| Chris@127 | 1711 	} | 
| Chris@127 | 1712     } | 
| Chris@127 | 1713 | 
| Chris@127 | 1714     if (selectionCacheable) { | 
| Chris@127 | 1715 	QPoint localPos; | 
| Chris@127 | 1716 	bool closeToLeft, closeToRight; | 
| Chris@127 | 1717 	if (shouldIlluminateLocalSelection(localPos, closeToLeft, closeToRight)) { | 
| Chris@127 | 1718 	    selectionCacheable = false; | 
| Chris@127 | 1719 	} | 
| Chris@127 | 1720     } | 
| Chris@127 | 1721 | 
| Chris@127 | 1722 #ifdef DEBUG_VIEW_WIDGET_PAINT | 
| Chris@682 | 1723     cerr << "View(" << this << ")::paintEvent: have " << scrollables.size() | 
| Chris@127 | 1724 	      << " scrollable back layers and " << nonScrollables.size() | 
| Chris@682 | 1725 	      << " non-scrollable front layers" << endl; | 
| Chris@682 | 1726     cerr << "haveSelections " << haveSelections << ", selectionCacheable " | 
| Chris@682 | 1727 	      << selectionCacheable << ", m_selectionCached " << m_selectionCached << endl; | 
| Chris@127 | 1728 #endif | 
| Chris@127 | 1729 | 
| Chris@127 | 1730     if (layersChanged || scrollables.empty() || | 
| Chris@127 | 1731 	(haveSelections && (selectionCacheable != m_selectionCached))) { | 
| Chris@127 | 1732 	delete m_cache; | 
| Chris@127 | 1733 	m_cache = 0; | 
| Chris@127 | 1734 	m_selectionCached = false; | 
| Chris@127 | 1735     } | 
| Chris@127 | 1736 | 
| Chris@127 | 1737     if (!scrollables.empty()) { | 
| Chris@244 | 1738 | 
| Chris@244 | 1739 #ifdef DEBUG_VIEW_WIDGET_PAINT | 
| Chris@682 | 1740         cerr << "View(" << this << "): cache " << m_cache << ", cache zoom " | 
| Chris@682 | 1741                   << m_cacheZoomLevel << ", zoom " << m_zoomLevel << endl; | 
| Chris@244 | 1742 #endif | 
| Chris@244 | 1743 | 
| Chris@127 | 1744 	if (!m_cache || | 
| Chris@127 | 1745 	    m_cacheZoomLevel != m_zoomLevel || | 
| Chris@127 | 1746 	    width() != m_cache->width() || | 
| Chris@127 | 1747 	    height() != m_cache->height()) { | 
| Chris@127 | 1748 | 
| Chris@127 | 1749 	    // cache is not valid | 
| Chris@127 | 1750 | 
| Chris@127 | 1751 	    if (cacheRect.width() < width()/10) { | 
| Chris@244 | 1752 		delete m_cache; | 
| Chris@244 | 1753                 m_cache = 0; | 
| Chris@127 | 1754 #ifdef DEBUG_VIEW_WIDGET_PAINT | 
| Chris@682 | 1755 		cerr << "View(" << this << ")::paintEvent: small repaint, not bothering to recreate cache" << endl; | 
| Chris@127 | 1756 #endif | 
| Chris@127 | 1757 	    } else { | 
| Chris@127 | 1758 		delete m_cache; | 
| Chris@127 | 1759 		m_cache = new QPixmap(width(), height()); | 
| Chris@127 | 1760 #ifdef DEBUG_VIEW_WIDGET_PAINT | 
| Chris@682 | 1761 		cerr << "View(" << this << ")::paintEvent: recreated cache" << endl; | 
| Chris@127 | 1762 #endif | 
| Chris@127 | 1763 		cacheRect = rect(); | 
| Chris@127 | 1764 		repaintCache = true; | 
| Chris@127 | 1765 	    } | 
| Chris@127 | 1766 | 
| Chris@127 | 1767 	} else if (m_cacheCentreFrame != m_centreFrame) { | 
| Chris@127 | 1768 | 
| Chris@806 | 1769 	    int dx = | 
| Chris@127 | 1770 		getXForFrame(m_cacheCentreFrame) - | 
| Chris@127 | 1771 		getXForFrame(m_centreFrame); | 
| Chris@127 | 1772 | 
| Chris@127 | 1773 	    if (dx > -width() && dx < width()) { | 
| Chris@548 | 1774 #ifdef PIXMAP_COPY_TO_SELF | 
| Chris@548 | 1775                 // This is not normally defined. Copying a pixmap to | 
| Chris@548 | 1776 		// itself doesn't work properly on Windows, Mac, or | 
| Chris@548 | 1777 		// X11 with the raster backend (it only works when | 
| Chris@548 | 1778 		// moving in one direction and then presumably only by | 
| Chris@548 | 1779 		// accident).  It does actually seem to be fine on X11 | 
| Chris@548 | 1780 		// with the native backend, but we prefer not to use | 
| Chris@548 | 1781 		// that anyway | 
| Chris@548 | 1782 		paint.begin(m_cache); | 
| Chris@548 | 1783 		paint.drawPixmap(dx, 0, *m_cache); | 
| Chris@548 | 1784 		paint.end(); | 
| Chris@548 | 1785 #else | 
| Chris@127 | 1786 		static QPixmap *tmpPixmap = 0; | 
| Chris@127 | 1787 		if (!tmpPixmap || | 
| Chris@127 | 1788 		    tmpPixmap->width() != width() || | 
| Chris@127 | 1789 		    tmpPixmap->height() != height()) { | 
| Chris@127 | 1790 		    delete tmpPixmap; | 
| Chris@127 | 1791 		    tmpPixmap = new QPixmap(width(), height()); | 
| Chris@127 | 1792 		} | 
| Chris@127 | 1793 		paint.begin(tmpPixmap); | 
| Chris@127 | 1794 		paint.drawPixmap(0, 0, *m_cache); | 
| Chris@127 | 1795 		paint.end(); | 
| Chris@127 | 1796 		paint.begin(m_cache); | 
| Chris@127 | 1797 		paint.drawPixmap(dx, 0, *tmpPixmap); | 
| Chris@127 | 1798 		paint.end(); | 
| Chris@127 | 1799 #endif | 
| Chris@127 | 1800 		if (dx < 0) { | 
| Chris@127 | 1801 		    cacheRect = QRect(width() + dx, 0, -dx, height()); | 
| Chris@127 | 1802 		} else { | 
| Chris@127 | 1803 		    cacheRect = QRect(0, 0, dx, height()); | 
| Chris@127 | 1804 		} | 
| Chris@127 | 1805 #ifdef DEBUG_VIEW_WIDGET_PAINT | 
| Chris@682 | 1806 		cerr << "View(" << this << ")::paintEvent: scrolled cache by " << dx << endl; | 
| Chris@127 | 1807 #endif | 
| Chris@127 | 1808 	    } else { | 
| Chris@127 | 1809 		cacheRect = rect(); | 
| Chris@127 | 1810 #ifdef DEBUG_VIEW_WIDGET_PAINT | 
| Chris@682 | 1811 		cerr << "View(" << this << ")::paintEvent: scrolling too far" << endl; | 
| Chris@127 | 1812 #endif | 
| Chris@127 | 1813 	    } | 
| Chris@127 | 1814 	    repaintCache = true; | 
| Chris@127 | 1815 | 
| Chris@127 | 1816 	} else { | 
| Chris@127 | 1817 #ifdef DEBUG_VIEW_WIDGET_PAINT | 
| Chris@682 | 1818 	    cerr << "View(" << this << ")::paintEvent: cache is good" << endl; | 
| Chris@127 | 1819 #endif | 
| Chris@127 | 1820 	    paint.begin(this); | 
| Chris@127 | 1821 	    paint.drawPixmap(cacheRect, *m_cache, cacheRect); | 
| Chris@127 | 1822 	    paint.end(); | 
| Chris@127 | 1823 	    QFrame::paintEvent(e); | 
| Chris@127 | 1824 	    paintedCacheRect = true; | 
| Chris@127 | 1825 	} | 
| Chris@127 | 1826 | 
| Chris@127 | 1827 	m_cacheCentreFrame = m_centreFrame; | 
| Chris@127 | 1828 	m_cacheZoomLevel = m_zoomLevel; | 
| Chris@127 | 1829     } | 
| Chris@127 | 1830 | 
| Chris@127 | 1831 #ifdef DEBUG_VIEW_WIDGET_PAINT | 
| Chris@682 | 1832 //    cerr << "View(" << this << ")::paintEvent: cacheRect " << cacheRect << ", nonCacheRect " << (nonCacheRect | cacheRect) << ", repaintCache " << repaintCache << ", paintedCacheRect " << paintedCacheRect << endl; | 
| Chris@127 | 1833 #endif | 
| Chris@127 | 1834 | 
| Chris@127 | 1835     // Scrollable (cacheable) items first | 
| Chris@127 | 1836 | 
| Chris@127 | 1837     if (!paintedCacheRect) { | 
| Chris@127 | 1838 | 
| Chris@127 | 1839 	if (repaintCache) paint.begin(m_cache); | 
| Chris@127 | 1840 	else paint.begin(this); | 
| Chris@339 | 1841         setPaintFont(paint); | 
| Chris@127 | 1842 	paint.setClipRect(cacheRect); | 
| Chris@287 | 1843 | 
| Chris@287 | 1844         paint.setPen(getBackground()); | 
| Chris@287 | 1845         paint.setBrush(getBackground()); | 
| Chris@127 | 1846 	paint.drawRect(cacheRect); | 
| Chris@127 | 1847 | 
| Chris@287 | 1848 	paint.setPen(getForeground()); | 
| Chris@127 | 1849 	paint.setBrush(Qt::NoBrush); | 
| Chris@127 | 1850 | 
| Chris@127 | 1851 	for (LayerList::iterator i = scrollables.begin(); i != scrollables.end(); ++i) { | 
| Chris@127 | 1852 	    paint.setRenderHint(QPainter::Antialiasing, false); | 
| Chris@127 | 1853 	    paint.save(); | 
| Chris@127 | 1854 	    (*i)->paint(this, paint, cacheRect); | 
| Chris@127 | 1855 	    paint.restore(); | 
| Chris@127 | 1856 	} | 
| Chris@127 | 1857 | 
| Chris@127 | 1858 	if (haveSelections && selectionCacheable) { | 
| Chris@127 | 1859 	    drawSelections(paint); | 
| Chris@127 | 1860 	    m_selectionCached = repaintCache; | 
| Chris@127 | 1861 	} | 
| Chris@127 | 1862 | 
| Chris@127 | 1863 	paint.end(); | 
| Chris@127 | 1864 | 
| Chris@127 | 1865 	if (repaintCache) { | 
| Chris@127 | 1866 	    cacheRect |= (e ? e->rect() : rect()); | 
| Chris@127 | 1867 	    paint.begin(this); | 
| Chris@127 | 1868 	    paint.drawPixmap(cacheRect, *m_cache, cacheRect); | 
| Chris@127 | 1869 	    paint.end(); | 
| Chris@127 | 1870 	} | 
| Chris@127 | 1871     } | 
| Chris@127 | 1872 | 
| Chris@127 | 1873     // Now non-cacheable items.  We always need to redraw the | 
| Chris@127 | 1874     // non-cacheable items across at least the area we drew of the | 
| Chris@127 | 1875     // cacheable items. | 
| Chris@127 | 1876 | 
| Chris@127 | 1877     nonCacheRect |= cacheRect; | 
| Chris@127 | 1878 | 
| Chris@127 | 1879     paint.begin(this); | 
| Chris@127 | 1880     paint.setClipRect(nonCacheRect); | 
| Chris@339 | 1881     setPaintFont(paint); | 
| Chris@127 | 1882     if (scrollables.empty()) { | 
| Chris@287 | 1883         paint.setPen(getBackground()); | 
| Chris@287 | 1884         paint.setBrush(getBackground()); | 
| Chris@127 | 1885 	paint.drawRect(nonCacheRect); | 
| Chris@127 | 1886     } | 
| Chris@127 | 1887 | 
| Chris@287 | 1888     paint.setPen(getForeground()); | 
| Chris@127 | 1889     paint.setBrush(Qt::NoBrush); | 
| Chris@127 | 1890 | 
| Chris@127 | 1891     for (LayerList::iterator i = nonScrollables.begin(); i != nonScrollables.end(); ++i) { | 
| Chris@127 | 1892 //        Profiler profiler2("View::paintEvent non-cacheable"); | 
| Chris@127 | 1893 	(*i)->paint(this, paint, nonCacheRect); | 
| Chris@127 | 1894     } | 
| Chris@127 | 1895 | 
| Chris@127 | 1896     paint.end(); | 
| Chris@127 | 1897 | 
| Chris@127 | 1898     paint.begin(this); | 
| Chris@339 | 1899     setPaintFont(paint); | 
| Chris@127 | 1900     if (e) paint.setClipRect(e->rect()); | 
| Chris@127 | 1901     if (!m_selectionCached) { | 
| Chris@127 | 1902 	drawSelections(paint); | 
| Chris@127 | 1903     } | 
| Chris@127 | 1904     paint.end(); | 
| Chris@127 | 1905 | 
| Chris@211 | 1906     bool showPlayPointer = true; | 
| Chris@211 | 1907     if (m_followPlay == PlaybackScrollContinuous) { | 
| Chris@211 | 1908         showPlayPointer = false; | 
| Chris@806 | 1909     } else if (m_playPointerFrame <= getStartFrame() || | 
| Chris@211 | 1910                m_playPointerFrame >= getEndFrame()) { | 
| Chris@211 | 1911         showPlayPointer = false; | 
| Chris@211 | 1912     } else if (m_manager && !m_manager->isPlaying()) { | 
| Chris@211 | 1913         if (m_playPointerFrame == getCentreFrame() && | 
| Chris@787 | 1914             m_manager->shouldShowCentreLine() && | 
| Chris@211 | 1915             m_followPlay != PlaybackIgnore) { | 
| Chris@787 | 1916             // Don't show the play pointer when it is redundant with | 
| Chris@787 | 1917             // the centre line | 
| Chris@211 | 1918             showPlayPointer = false; | 
| Chris@211 | 1919         } | 
| Chris@211 | 1920     } | 
| Chris@211 | 1921 | 
| Chris@211 | 1922     if (showPlayPointer) { | 
| Chris@127 | 1923 | 
| Chris@127 | 1924 	paint.begin(this); | 
| Chris@127 | 1925 | 
| Chris@211 | 1926         int playx = getXForFrame(m_playPointerFrame); | 
| Chris@211 | 1927 | 
| Chris@287 | 1928         paint.setPen(getForeground()); | 
| Chris@211 | 1929         paint.drawLine(playx - 1, 0, playx - 1, height() - 1); | 
| Chris@211 | 1930         paint.drawLine(playx + 1, 0, playx + 1, height() - 1); | 
| Chris@211 | 1931         paint.drawPoint(playx, 0); | 
| Chris@211 | 1932         paint.drawPoint(playx, height() - 1); | 
| Chris@287 | 1933         paint.setPen(getBackground()); | 
| Chris@211 | 1934         paint.drawLine(playx, 1, playx, height() - 2); | 
| Chris@127 | 1935 | 
| Chris@127 | 1936 	paint.end(); | 
| Chris@127 | 1937     } | 
| Chris@127 | 1938 | 
| Chris@127 | 1939     QFrame::paintEvent(e); | 
| Chris@127 | 1940 } | 
| Chris@127 | 1941 | 
| Chris@127 | 1942 void | 
| Chris@127 | 1943 View::drawSelections(QPainter &paint) | 
| Chris@127 | 1944 { | 
| Chris@217 | 1945     if (!hasTopLayerTimeXAxis()) return; | 
| Chris@217 | 1946 | 
| Chris@127 | 1947     MultiSelection::SelectionList selections; | 
| Chris@127 | 1948 | 
| Chris@127 | 1949     if (m_manager) { | 
| Chris@127 | 1950 	selections = m_manager->getSelections(); | 
| Chris@127 | 1951 	if (m_manager->haveInProgressSelection()) { | 
| Chris@127 | 1952 	    bool exclusive; | 
| Chris@127 | 1953 	    Selection inProgressSelection = | 
| Chris@127 | 1954 		m_manager->getInProgressSelection(exclusive); | 
| Chris@127 | 1955 	    if (exclusive) selections.clear(); | 
| Chris@127 | 1956 	    selections.insert(inProgressSelection); | 
| Chris@127 | 1957 	} | 
| Chris@127 | 1958     } | 
| Chris@127 | 1959 | 
| Chris@127 | 1960     paint.save(); | 
| Chris@183 | 1961 | 
| Chris@183 | 1962     bool translucent = !areLayerColoursSignificant(); | 
| Chris@183 | 1963 | 
| Chris@183 | 1964     if (translucent) { | 
| Chris@183 | 1965         paint.setBrush(QColor(150, 150, 255, 80)); | 
| Chris@183 | 1966     } else { | 
| Chris@183 | 1967         paint.setBrush(Qt::NoBrush); | 
| Chris@183 | 1968     } | 
| Chris@127 | 1969 | 
| Chris@127 | 1970     int sampleRate = getModelsSampleRate(); | 
| Chris@127 | 1971 | 
| Chris@127 | 1972     QPoint localPos; | 
| Chris@806 | 1973     int illuminateFrame = -1; | 
| Chris@127 | 1974     bool closeToLeft, closeToRight; | 
| Chris@127 | 1975 | 
| Chris@127 | 1976     if (shouldIlluminateLocalSelection(localPos, closeToLeft, closeToRight)) { | 
| Chris@127 | 1977 	illuminateFrame = getFrameForX(localPos.x()); | 
| Chris@127 | 1978     } | 
| Chris@127 | 1979 | 
| Chris@127 | 1980     const QFontMetrics &metrics = paint.fontMetrics(); | 
| Chris@127 | 1981 | 
| Chris@127 | 1982     for (MultiSelection::SelectionList::iterator i = selections.begin(); | 
| Chris@127 | 1983 	 i != selections.end(); ++i) { | 
| Chris@127 | 1984 | 
| Chris@333 | 1985 	int p0 = getXForFrame(alignFromReference(i->getStartFrame())); | 
| Chris@333 | 1986 	int p1 = getXForFrame(alignFromReference(i->getEndFrame())); | 
| Chris@127 | 1987 | 
| Chris@127 | 1988 	if (p1 < 0 || p0 > width()) continue; | 
| Chris@127 | 1989 | 
| Chris@127 | 1990 #ifdef DEBUG_VIEW_WIDGET_PAINT | 
| Chris@587 | 1991 	SVDEBUG << "View::drawSelections: " << p0 << ",-1 [" << (p1-p0) << "x" << (height()+1) << "]" << endl; | 
| Chris@127 | 1992 #endif | 
| Chris@127 | 1993 | 
| Chris@127 | 1994 	bool illuminateThis = | 
| Chris@127 | 1995 	    (illuminateFrame >= 0 && i->contains(illuminateFrame)); | 
| Chris@127 | 1996 | 
| Chris@127 | 1997 	paint.setPen(QColor(150, 150, 255)); | 
| Chris@183 | 1998 | 
| Chris@183 | 1999         if (translucent && shouldLabelSelections()) { | 
| Chris@183 | 2000             paint.drawRect(p0, -1, p1 - p0, height() + 1); | 
| Chris@183 | 2001         } else { | 
| Chris@183 | 2002             // Make the top & bottom lines of the box visible if we | 
| Chris@183 | 2003             // are lacking some of the other visual cues.  There's no | 
| Chris@183 | 2004             // particular logic to this, it's just a question of what | 
| Chris@183 | 2005             // I happen to think looks nice. | 
| Chris@183 | 2006             paint.drawRect(p0, 0, p1 - p0, height() - 1); | 
| Chris@183 | 2007         } | 
| Chris@127 | 2008 | 
| Chris@127 | 2009 	if (illuminateThis) { | 
| Chris@127 | 2010 	    paint.save(); | 
| Chris@287 | 2011             paint.setPen(QPen(getForeground(), 2)); | 
| Chris@127 | 2012 	    if (closeToLeft) { | 
| Chris@127 | 2013 		paint.drawLine(p0, 1, p1, 1); | 
| Chris@127 | 2014 		paint.drawLine(p0, 0, p0, height()); | 
| Chris@127 | 2015 		paint.drawLine(p0, height() - 1, p1, height() - 1); | 
| Chris@127 | 2016 	    } else if (closeToRight) { | 
| Chris@127 | 2017 		paint.drawLine(p0, 1, p1, 1); | 
| Chris@127 | 2018 		paint.drawLine(p1, 0, p1, height()); | 
| Chris@127 | 2019 		paint.drawLine(p0, height() - 1, p1, height() - 1); | 
| Chris@127 | 2020 	    } else { | 
| Chris@127 | 2021 		paint.setBrush(Qt::NoBrush); | 
| Chris@127 | 2022 		paint.drawRect(p0, 1, p1 - p0, height() - 2); | 
| Chris@127 | 2023 	    } | 
| Chris@127 | 2024 	    paint.restore(); | 
| Chris@127 | 2025 	} | 
| Chris@127 | 2026 | 
| Chris@127 | 2027 	if (sampleRate && shouldLabelSelections() && m_manager && | 
| Chris@189 | 2028             m_manager->shouldShowSelectionExtents()) { | 
| Chris@127 | 2029 | 
| Chris@127 | 2030 	    QString startText = QString("%1 / %2") | 
| Chris@127 | 2031 		.arg(QString::fromStdString | 
| Chris@127 | 2032 		     (RealTime::frame2RealTime | 
| Chris@127 | 2033 		      (i->getStartFrame(), sampleRate).toText(true))) | 
| Chris@127 | 2034 		.arg(i->getStartFrame()); | 
| Chris@127 | 2035 | 
| Chris@127 | 2036 	    QString endText = QString(" %1 / %2") | 
| Chris@127 | 2037 		.arg(QString::fromStdString | 
| Chris@127 | 2038 		     (RealTime::frame2RealTime | 
| Chris@127 | 2039 		      (i->getEndFrame(), sampleRate).toText(true))) | 
| Chris@127 | 2040 		.arg(i->getEndFrame()); | 
| Chris@127 | 2041 | 
| Chris@127 | 2042 	    QString durationText = QString("(%1 / %2) ") | 
| Chris@127 | 2043 		.arg(QString::fromStdString | 
| Chris@127 | 2044 		     (RealTime::frame2RealTime | 
| Chris@127 | 2045 		      (i->getEndFrame() - i->getStartFrame(), sampleRate) | 
| Chris@127 | 2046 		      .toText(true))) | 
| Chris@127 | 2047 		.arg(i->getEndFrame() - i->getStartFrame()); | 
| Chris@127 | 2048 | 
| Chris@127 | 2049 	    int sw = metrics.width(startText), | 
| Chris@127 | 2050 		ew = metrics.width(endText), | 
| Chris@127 | 2051 		dw = metrics.width(durationText); | 
| Chris@127 | 2052 | 
| Chris@127 | 2053 	    int sy = metrics.ascent() + metrics.height() + 4; | 
| Chris@127 | 2054 	    int ey = sy; | 
| Chris@127 | 2055 	    int dy = sy + metrics.height(); | 
| Chris@127 | 2056 | 
| Chris@127 | 2057 	    int sx = p0 + 2; | 
| Chris@127 | 2058 	    int ex = sx; | 
| Chris@127 | 2059 	    int dx = sx; | 
| Chris@127 | 2060 | 
| Chris@501 | 2061             bool durationBothEnds = true; | 
| Chris@501 | 2062 | 
| Chris@127 | 2063 	    if (sw + ew > (p1 - p0)) { | 
| Chris@127 | 2064 		ey += metrics.height(); | 
| Chris@127 | 2065 		dy += metrics.height(); | 
| Chris@501 | 2066                 durationBothEnds = false; | 
| Chris@127 | 2067 	    } | 
| Chris@127 | 2068 | 
| Chris@127 | 2069 	    if (ew < (p1 - p0)) { | 
| Chris@127 | 2070 		ex = p1 - 2 - ew; | 
| Chris@127 | 2071 	    } | 
| Chris@127 | 2072 | 
| Chris@127 | 2073 	    if (dw < (p1 - p0)) { | 
| Chris@127 | 2074 		dx = p1 - 2 - dw; | 
| Chris@127 | 2075 	    } | 
| Chris@127 | 2076 | 
| Chris@127 | 2077 	    paint.drawText(sx, sy, startText); | 
| Chris@127 | 2078 	    paint.drawText(ex, ey, endText); | 
| Chris@127 | 2079 	    paint.drawText(dx, dy, durationText); | 
| Chris@501 | 2080             if (durationBothEnds) { | 
| Chris@501 | 2081                 paint.drawText(sx, dy, durationText); | 
| Chris@501 | 2082             } | 
| Chris@127 | 2083 	} | 
| Chris@127 | 2084     } | 
| Chris@127 | 2085 | 
| Chris@127 | 2086     paint.restore(); | 
| Chris@127 | 2087 } | 
| Chris@127 | 2088 | 
| Chris@267 | 2089 void | 
| Chris@270 | 2090 View::drawMeasurementRect(QPainter &paint, const Layer *topLayer, QRect r, | 
| Chris@270 | 2091                           bool focus) const | 
| Chris@267 | 2092 { | 
| Chris@587 | 2093 //    SVDEBUG << "View::drawMeasurementRect(" << r.x() << "," << r.y() << " " | 
| Chris@585 | 2094 //              << r.width() << "x" << r.height() << ")" << endl; | 
| Chris@268 | 2095 | 
| Chris@267 | 2096     if (r.x() + r.width() < 0 || r.x() >= width()) return; | 
| Chris@267 | 2097 | 
| Chris@270 | 2098     if (r.width() != 0 || r.height() != 0) { | 
| Chris@270 | 2099         paint.save(); | 
| Chris@270 | 2100         if (focus) { | 
| Chris@270 | 2101             paint.setPen(Qt::NoPen); | 
| Chris@272 | 2102             QColor brushColour(Qt::black); | 
| Chris@272 | 2103             brushColour.setAlpha(hasLightBackground() ? 15 : 40); | 
| Chris@270 | 2104             paint.setBrush(brushColour); | 
| Chris@270 | 2105             if (r.x() > 0) { | 
| Chris@270 | 2106                 paint.drawRect(0, 0, r.x(), height()); | 
| Chris@270 | 2107             } | 
| Chris@270 | 2108             if (r.x() + r.width() < width()) { | 
| Chris@270 | 2109                 paint.drawRect(r.x() + r.width(), 0, width()-r.x()-r.width(), height()); | 
| Chris@270 | 2110             } | 
| Chris@270 | 2111             if (r.y() > 0) { | 
| Chris@270 | 2112                 paint.drawRect(r.x(), 0, r.width(), r.y()); | 
| Chris@270 | 2113             } | 
| Chris@270 | 2114             if (r.y() + r.height() < height()) { | 
| Chris@270 | 2115                 paint.drawRect(r.x(), r.y() + r.height(), r.width(), height()-r.y()-r.height()); | 
| Chris@270 | 2116             } | 
| Chris@270 | 2117             paint.setBrush(Qt::NoBrush); | 
| Chris@270 | 2118         } | 
| Chris@270 | 2119         paint.setPen(Qt::green); | 
| Chris@270 | 2120         paint.drawRect(r); | 
| Chris@270 | 2121         paint.restore(); | 
| Chris@270 | 2122     } else { | 
| Chris@270 | 2123         paint.save(); | 
| Chris@270 | 2124         paint.setPen(Qt::green); | 
| Chris@270 | 2125         paint.drawPoint(r.x(), r.y()); | 
| Chris@270 | 2126         paint.restore(); | 
| Chris@270 | 2127     } | 
| Chris@270 | 2128 | 
| Chris@270 | 2129     if (!focus) return; | 
| Chris@270 | 2130 | 
| Chris@278 | 2131     paint.save(); | 
| Chris@278 | 2132     QFont fn = paint.font(); | 
| Chris@278 | 2133     if (fn.pointSize() > 8) { | 
| Chris@278 | 2134         fn.setPointSize(fn.pointSize() - 1); | 
| Chris@278 | 2135         paint.setFont(fn); | 
| Chris@278 | 2136     } | 
| Chris@278 | 2137 | 
| Chris@267 | 2138     int fontHeight = paint.fontMetrics().height(); | 
| Chris@267 | 2139     int fontAscent = paint.fontMetrics().ascent(); | 
| Chris@267 | 2140 | 
| Chris@267 | 2141     float v0, v1; | 
| Chris@267 | 2142     QString u0, u1; | 
| Chris@267 | 2143     bool b0 = false, b1 = false; | 
| Chris@267 | 2144 | 
| Chris@267 | 2145     QString axs, ays, bxs, bys, dxs, dys; | 
| Chris@267 | 2146 | 
| Chris@267 | 2147     int axx, axy, bxx, bxy, dxx, dxy; | 
| Chris@267 | 2148     int aw = 0, bw = 0, dw = 0; | 
| Chris@267 | 2149 | 
| Chris@267 | 2150     int labelCount = 0; | 
| Chris@267 | 2151 | 
| Chris@362 | 2152     // top-left point, x-coord | 
| Chris@362 | 2153 | 
| Chris@267 | 2154     if ((b0 = topLayer->getXScaleValue(this, r.x(), v0, u0))) { | 
| Chris@267 | 2155         axs = QString("%1 %2").arg(v0).arg(u0); | 
| Chris@278 | 2156         if (u0 == "Hz" && Pitch::isFrequencyInMidiRange(v0)) { | 
| Chris@278 | 2157             axs = QString("%1 (%2)").arg(axs) | 
| Chris@278 | 2158                 .arg(Pitch::getPitchLabelForFrequency(v0)); | 
| Chris@278 | 2159         } | 
| Chris@267 | 2160         aw = paint.fontMetrics().width(axs); | 
| Chris@267 | 2161         ++labelCount; | 
| Chris@267 | 2162     } | 
| Chris@362 | 2163 | 
| Chris@362 | 2164     // bottom-right point, x-coord | 
| Chris@267 | 2165 | 
| Chris@267 | 2166     if (r.width() > 0) { | 
| Chris@267 | 2167         if ((b1 = topLayer->getXScaleValue(this, r.x() + r.width(), v1, u1))) { | 
| Chris@267 | 2168             bxs = QString("%1 %2").arg(v1).arg(u1); | 
| Chris@278 | 2169             if (u1 == "Hz" && Pitch::isFrequencyInMidiRange(v1)) { | 
| Chris@278 | 2170                 bxs = QString("%1 (%2)").arg(bxs) | 
| Chris@278 | 2171                     .arg(Pitch::getPitchLabelForFrequency(v1)); | 
| Chris@278 | 2172             } | 
| Chris@267 | 2173             bw = paint.fontMetrics().width(bxs); | 
| Chris@267 | 2174         } | 
| Chris@267 | 2175     } | 
| Chris@362 | 2176 | 
| Chris@362 | 2177     // dimension, width | 
| Chris@267 | 2178 | 
| Chris@283 | 2179     if (b0 && b1 && v1 != v0 && u0 == u1) { | 
| Chris@362 | 2180         dxs = QString("[%1 %2]").arg(fabs(v1 - v0)).arg(u1); | 
| Chris@267 | 2181         dw = paint.fontMetrics().width(dxs); | 
| Chris@267 | 2182     } | 
| Chris@267 | 2183 | 
| Chris@267 | 2184     b0 = false; | 
| Chris@267 | 2185     b1 = false; | 
| Chris@267 | 2186 | 
| Chris@362 | 2187     // top-left point, y-coord | 
| Chris@362 | 2188 | 
| Chris@267 | 2189     if ((b0 = topLayer->getYScaleValue(this, r.y(), v0, u0))) { | 
| Chris@267 | 2190         ays = QString("%1 %2").arg(v0).arg(u0); | 
| Chris@278 | 2191         if (u0 == "Hz" && Pitch::isFrequencyInMidiRange(v0)) { | 
| Chris@278 | 2192             ays = QString("%1 (%2)").arg(ays) | 
| Chris@278 | 2193                 .arg(Pitch::getPitchLabelForFrequency(v0)); | 
| Chris@278 | 2194         } | 
| Chris@267 | 2195         aw = std::max(aw, paint.fontMetrics().width(ays)); | 
| Chris@267 | 2196         ++labelCount; | 
| Chris@267 | 2197     } | 
| Chris@267 | 2198 | 
| Chris@362 | 2199     // bottom-right point, y-coord | 
| Chris@362 | 2200 | 
| Chris@267 | 2201     if (r.height() > 0) { | 
| Chris@267 | 2202         if ((b1 = topLayer->getYScaleValue(this, r.y() + r.height(), v1, u1))) { | 
| Chris@267 | 2203             bys = QString("%1 %2").arg(v1).arg(u1); | 
| Chris@278 | 2204             if (u1 == "Hz" && Pitch::isFrequencyInMidiRange(v1)) { | 
| Chris@278 | 2205                 bys = QString("%1 (%2)").arg(bys) | 
| Chris@278 | 2206                     .arg(Pitch::getPitchLabelForFrequency(v1)); | 
| Chris@278 | 2207             } | 
| Chris@267 | 2208             bw = std::max(bw, paint.fontMetrics().width(bys)); | 
| Chris@267 | 2209         } | 
| Chris@267 | 2210     } | 
| Chris@274 | 2211 | 
| Chris@274 | 2212     bool bd = false; | 
| Chris@274 | 2213     float dy = 0.f; | 
| Chris@274 | 2214     QString du; | 
| Chris@274 | 2215 | 
| Chris@362 | 2216     // dimension, height | 
| Chris@362 | 2217 | 
| Chris@274 | 2218     if ((bd = topLayer->getYScaleDifference(this, r.y(), r.y() + r.height(), | 
| Chris@283 | 2219                                             dy, du)) && | 
| Chris@283 | 2220         dy != 0) { | 
| Chris@274 | 2221         if (du != "") { | 
| Chris@362 | 2222             if (du == "Hz") { | 
| Chris@362 | 2223                 int semis; | 
| Chris@362 | 2224                 float cents; | 
| Chris@362 | 2225                 semis = Pitch::getPitchForFrequencyDifference(v0, v1, ¢s); | 
| Chris@362 | 2226                 dys = QString("[%1 %2 (%3)]") | 
| Chris@362 | 2227                     .arg(dy).arg(du) | 
| Chris@362 | 2228                     .arg(Pitch::getLabelForPitchRange(semis, cents)); | 
| Chris@362 | 2229             } else { | 
| Chris@362 | 2230                 dys = QString("[%1 %2]").arg(dy).arg(du); | 
| Chris@362 | 2231             } | 
| Chris@274 | 2232         } else { | 
| Chris@362 | 2233             dys = QString("[%1]").arg(dy); | 
| Chris@274 | 2234         } | 
| Chris@267 | 2235         dw = std::max(dw, paint.fontMetrics().width(dys)); | 
| Chris@267 | 2236     } | 
| Chris@267 | 2237 | 
| Chris@267 | 2238     int mw = r.width(); | 
| Chris@267 | 2239     int mh = r.height(); | 
| Chris@267 | 2240 | 
| Chris@267 | 2241     bool edgeLabelsInside = false; | 
| Chris@267 | 2242     bool sizeLabelsInside = false; | 
| Chris@267 | 2243 | 
| Chris@267 | 2244     if (mw < std::max(aw, std::max(bw, dw)) + 4) { | 
| Chris@267 | 2245         // defaults stand | 
| Chris@267 | 2246     } else if (mw < aw + bw + 4) { | 
| Chris@267 | 2247         if (mh > fontHeight * labelCount * 3 + 4) { | 
| Chris@267 | 2248             edgeLabelsInside = true; | 
| Chris@267 | 2249             sizeLabelsInside = true; | 
| Chris@267 | 2250         } else if (mh > fontHeight * labelCount * 2 + 4) { | 
| Chris@267 | 2251             edgeLabelsInside = true; | 
| Chris@267 | 2252         } | 
| Chris@267 | 2253     } else if (mw < aw + bw + dw + 4) { | 
| Chris@267 | 2254         if (mh > fontHeight * labelCount * 3 + 4) { | 
| Chris@267 | 2255             edgeLabelsInside = true; | 
| Chris@267 | 2256             sizeLabelsInside = true; | 
| Chris@267 | 2257         } else if (mh > fontHeight * labelCount + 4) { | 
| Chris@267 | 2258             edgeLabelsInside = true; | 
| Chris@267 | 2259         } | 
| Chris@267 | 2260     } else { | 
| Chris@267 | 2261         if (mh > fontHeight * labelCount + 4) { | 
| Chris@267 | 2262             edgeLabelsInside = true; | 
| Chris@267 | 2263             sizeLabelsInside = true; | 
| Chris@267 | 2264         } | 
| Chris@267 | 2265     } | 
| Chris@267 | 2266 | 
| Chris@267 | 2267     if (edgeLabelsInside) { | 
| Chris@267 | 2268 | 
| Chris@267 | 2269         axx = r.x() + 2; | 
| Chris@267 | 2270         axy = r.y() + fontAscent + 2; | 
| Chris@267 | 2271 | 
| Chris@267 | 2272         bxx = r.x() + r.width() - bw - 2; | 
| Chris@267 | 2273         bxy = r.y() + r.height() - (labelCount-1) * fontHeight - 2; | 
| Chris@267 | 2274 | 
| Chris@267 | 2275     } else { | 
| Chris@267 | 2276 | 
| Chris@267 | 2277         axx = r.x() - aw - 2; | 
| Chris@267 | 2278         axy = r.y() + fontAscent; | 
| Chris@267 | 2279 | 
| Chris@267 | 2280         bxx = r.x() + r.width() + 2; | 
| Chris@267 | 2281         bxy = r.y() + r.height() - (labelCount-1) * fontHeight; | 
| Chris@267 | 2282     } | 
| Chris@267 | 2283 | 
| Chris@267 | 2284     dxx = r.width()/2 + r.x() - dw/2; | 
| Chris@267 | 2285 | 
| Chris@267 | 2286     if (sizeLabelsInside) { | 
| Chris@267 | 2287 | 
| Chris@267 | 2288         dxy = r.height()/2 + r.y() - (labelCount * fontHeight)/2 + fontAscent; | 
| Chris@267 | 2289 | 
| Chris@267 | 2290     } else { | 
| Chris@267 | 2291 | 
| Chris@267 | 2292         dxy = r.y() + r.height() + fontAscent + 2; | 
| Chris@267 | 2293     } | 
| Chris@267 | 2294 | 
| Chris@267 | 2295     if (axs != "") { | 
| Chris@267 | 2296         drawVisibleText(paint, axx, axy, axs, OutlinedText); | 
| Chris@267 | 2297         axy += fontHeight; | 
| Chris@267 | 2298     } | 
| Chris@267 | 2299 | 
| Chris@267 | 2300     if (ays != "") { | 
| Chris@267 | 2301         drawVisibleText(paint, axx, axy, ays, OutlinedText); | 
| Chris@267 | 2302         axy += fontHeight; | 
| Chris@267 | 2303     } | 
| Chris@267 | 2304 | 
| Chris@267 | 2305     if (bxs != "") { | 
| Chris@267 | 2306         drawVisibleText(paint, bxx, bxy, bxs, OutlinedText); | 
| Chris@267 | 2307         bxy += fontHeight; | 
| Chris@267 | 2308     } | 
| Chris@267 | 2309 | 
| Chris@267 | 2310     if (bys != "") { | 
| Chris@267 | 2311         drawVisibleText(paint, bxx, bxy, bys, OutlinedText); | 
| Chris@267 | 2312         bxy += fontHeight; | 
| Chris@267 | 2313     } | 
| Chris@267 | 2314 | 
| Chris@267 | 2315     if (dxs != "") { | 
| Chris@267 | 2316         drawVisibleText(paint, dxx, dxy, dxs, OutlinedText); | 
| Chris@267 | 2317         dxy += fontHeight; | 
| Chris@267 | 2318     } | 
| Chris@267 | 2319 | 
| Chris@267 | 2320     if (dys != "") { | 
| Chris@267 | 2321         drawVisibleText(paint, dxx, dxy, dys, OutlinedText); | 
| Chris@267 | 2322         dxy += fontHeight; | 
| Chris@267 | 2323     } | 
| Chris@278 | 2324 | 
| Chris@278 | 2325     paint.restore(); | 
| Chris@267 | 2326 } | 
| Chris@267 | 2327 | 
| Chris@227 | 2328 bool | 
| Chris@806 | 2329 View::render(QPainter &paint, int xorigin, int f0, int f1) | 
| Chris@227 | 2330 { | 
| Chris@806 | 2331     int x0 = f0 / m_zoomLevel; | 
| Chris@806 | 2332     int x1 = f1 / m_zoomLevel; | 
| Chris@806 | 2333 | 
| Chris@806 | 2334     int w = x1 - x0; | 
| Chris@806 | 2335 | 
| Chris@806 | 2336     int origCentreFrame = m_centreFrame; | 
| Chris@227 | 2337 | 
| Chris@227 | 2338     bool someLayersIncomplete = false; | 
| Chris@227 | 2339 | 
| Chris@835 | 2340     for (LayerList::iterator i = m_layerStack.begin(); | 
| Chris@835 | 2341          i != m_layerStack.end(); ++i) { | 
| Chris@227 | 2342 | 
| Chris@227 | 2343         int c = (*i)->getCompletion(this); | 
| Chris@227 | 2344         if (c < 100) { | 
| Chris@227 | 2345             someLayersIncomplete = true; | 
| Chris@227 | 2346             break; | 
| Chris@227 | 2347         } | 
| Chris@227 | 2348     } | 
| Chris@227 | 2349 | 
| Chris@227 | 2350     if (someLayersIncomplete) { | 
| Chris@227 | 2351 | 
| Chris@227 | 2352         QProgressDialog progress(tr("Waiting for layers to be ready..."), | 
| Chris@227 | 2353                                  tr("Cancel"), 0, 100, this); | 
| Chris@227 | 2354 | 
| Chris@227 | 2355         int layerCompletion = 0; | 
| Chris@227 | 2356 | 
| Chris@227 | 2357         while (layerCompletion < 100) { | 
| Chris@227 | 2358 | 
| Chris@835 | 2359             for (LayerList::iterator i = m_layerStack.begin(); | 
| Chris@835 | 2360                  i != m_layerStack.end(); ++i) { | 
| Chris@227 | 2361 | 
| Chris@227 | 2362                 int c = (*i)->getCompletion(this); | 
| Chris@835 | 2363                 if (i == m_layerStack.begin() || c < layerCompletion) { | 
| Chris@227 | 2364                     layerCompletion = c; | 
| Chris@227 | 2365                 } | 
| Chris@227 | 2366             } | 
| Chris@227 | 2367 | 
| Chris@227 | 2368             if (layerCompletion >= 100) break; | 
| Chris@227 | 2369 | 
| Chris@227 | 2370             progress.setValue(layerCompletion); | 
| Chris@227 | 2371             qApp->processEvents(); | 
| Chris@227 | 2372             if (progress.wasCanceled()) { | 
| Chris@227 | 2373                 update(); | 
| Chris@227 | 2374                 return false; | 
| Chris@227 | 2375             } | 
| Chris@227 | 2376 | 
| Chris@227 | 2377             usleep(50000); | 
| Chris@227 | 2378         } | 
| Chris@227 | 2379     } | 
| Chris@227 | 2380 | 
| Chris@227 | 2381     QProgressDialog progress(tr("Rendering image..."), | 
| Chris@227 | 2382                              tr("Cancel"), 0, w / width(), this); | 
| Chris@227 | 2383 | 
| Chris@806 | 2384     for (int x = 0; x < w; x += width()) { | 
| Chris@227 | 2385 | 
| Chris@227 | 2386         progress.setValue(x / width()); | 
| Chris@227 | 2387         qApp->processEvents(); | 
| Chris@227 | 2388         if (progress.wasCanceled()) { | 
| Chris@227 | 2389             m_centreFrame = origCentreFrame; | 
| Chris@227 | 2390             update(); | 
| Chris@227 | 2391             return false; | 
| Chris@227 | 2392         } | 
| Chris@227 | 2393 | 
| Chris@229 | 2394         m_centreFrame = f0 + (x + width()/2) * m_zoomLevel; | 
| Chris@227 | 2395 | 
| Chris@227 | 2396         QRect chunk(0, 0, width(), height()); | 
| Chris@227 | 2397 | 
| Chris@287 | 2398         paint.setPen(getBackground()); | 
| Chris@287 | 2399         paint.setBrush(getBackground()); | 
| Chris@227 | 2400 | 
| Chris@229 | 2401 	paint.drawRect(QRect(xorigin + x, 0, width(), height())); | 
| Chris@227 | 2402 | 
| Chris@287 | 2403 	paint.setPen(getForeground()); | 
| Chris@227 | 2404 	paint.setBrush(Qt::NoBrush); | 
| Chris@227 | 2405 | 
| Chris@835 | 2406 	for (LayerList::iterator i = m_layerStack.begin(); | 
| Chris@835 | 2407              i != m_layerStack.end(); ++i) { | 
| dan@574 | 2408 		if(!((*i)->isLayerDormant(this))){ | 
| dan@574 | 2409 | 
| dan@574 | 2410 		    paint.setRenderHint(QPainter::Antialiasing, false); | 
| dan@574 | 2411 | 
| dan@574 | 2412 		    paint.save(); | 
| dan@574 | 2413 	            paint.translate(xorigin + x, 0); | 
| dan@574 | 2414 | 
| Chris@682 | 2415 	            cerr << "Centre frame now: " << m_centreFrame << " drawing to " << chunk.x() + x + xorigin << ", " << chunk.width() << endl; | 
| dan@574 | 2416 | 
| dan@574 | 2417 	            (*i)->setSynchronousPainting(true); | 
| dan@574 | 2418 | 
| dan@574 | 2419 		    (*i)->paint(this, paint, chunk); | 
| dan@574 | 2420 | 
| dan@574 | 2421 	            (*i)->setSynchronousPainting(false); | 
| dan@574 | 2422 | 
| dan@574 | 2423 		    paint.restore(); | 
| dan@574 | 2424 		} | 
| Chris@227 | 2425 	} | 
| Chris@227 | 2426     } | 
| Chris@227 | 2427 | 
| Chris@227 | 2428     m_centreFrame = origCentreFrame; | 
| Chris@227 | 2429     update(); | 
| Chris@227 | 2430     return true; | 
| Chris@227 | 2431 } | 
| Chris@227 | 2432 | 
| Chris@227 | 2433 QImage * | 
| Chris@227 | 2434 View::toNewImage() | 
| Chris@227 | 2435 { | 
| Chris@806 | 2436     int f0 = getModelsStartFrame(); | 
| Chris@806 | 2437     int f1 = getModelsEndFrame(); | 
| Chris@227 | 2438 | 
| Chris@229 | 2439     return toNewImage(f0, f1); | 
| Chris@229 | 2440 } | 
| Chris@229 | 2441 | 
| Chris@229 | 2442 QImage * | 
| Chris@806 | 2443 View::toNewImage(int f0, int f1) | 
| Chris@229 | 2444 { | 
| Chris@806 | 2445     int x0 = f0 / getZoomLevel(); | 
| Chris@806 | 2446     int x1 = f1 / getZoomLevel(); | 
| Chris@227 | 2447 | 
| Chris@227 | 2448     QImage *image = new QImage(x1 - x0, height(), QImage::Format_RGB32); | 
| Chris@227 | 2449 | 
| Chris@227 | 2450     QPainter *paint = new QPainter(image); | 
| Chris@229 | 2451     if (!render(*paint, 0, f0, f1)) { | 
| Chris@227 | 2452         delete paint; | 
| Chris@227 | 2453         delete image; | 
| Chris@227 | 2454         return 0; | 
| Chris@227 | 2455     } else { | 
| Chris@227 | 2456         delete paint; | 
| Chris@227 | 2457         return image; | 
| Chris@227 | 2458     } | 
| Chris@227 | 2459 } | 
| Chris@227 | 2460 | 
| Chris@229 | 2461 QSize | 
| Chris@229 | 2462 View::getImageSize() | 
| Chris@229 | 2463 { | 
| Chris@806 | 2464     int f0 = getModelsStartFrame(); | 
| Chris@806 | 2465     int f1 = getModelsEndFrame(); | 
| Chris@229 | 2466 | 
| Chris@229 | 2467     return getImageSize(f0, f1); | 
| Chris@229 | 2468 } | 
| Chris@229 | 2469 | 
| Chris@229 | 2470 QSize | 
| Chris@806 | 2471 View::getImageSize(int f0, int f1) | 
| Chris@229 | 2472 { | 
| Chris@806 | 2473     int x0 = f0 / getZoomLevel(); | 
| Chris@806 | 2474     int x1 = f1 / getZoomLevel(); | 
| Chris@229 | 2475 | 
| Chris@229 | 2476     return QSize(x1 - x0, height()); | 
| Chris@229 | 2477 } | 
| Chris@229 | 2478 | 
| Chris@316 | 2479 void | 
| Chris@316 | 2480 View::toXml(QTextStream &stream, | 
| Chris@316 | 2481             QString indent, QString extraAttributes) const | 
| Chris@127 | 2482 { | 
| Chris@316 | 2483     stream << indent; | 
| Chris@127 | 2484 | 
| Chris@316 | 2485     stream << QString("<view " | 
| Chris@316 | 2486                       "centre=\"%1\" " | 
| Chris@316 | 2487                       "zoom=\"%2\" " | 
| Chris@316 | 2488                       "followPan=\"%3\" " | 
| Chris@316 | 2489                       "followZoom=\"%4\" " | 
| Chris@316 | 2490                       "tracking=\"%5\" " | 
| Chris@316 | 2491                       " %6>\n") | 
| Chris@127 | 2492 	.arg(m_centreFrame) | 
| Chris@127 | 2493 	.arg(m_zoomLevel) | 
| Chris@127 | 2494 	.arg(m_followPan) | 
| Chris@127 | 2495 	.arg(m_followZoom) | 
| Chris@127 | 2496 	.arg(m_followPlay == PlaybackScrollContinuous ? "scroll" : | 
| Chris@815 | 2497 	     m_followPlay == PlaybackScrollPageWithCentre ? "page" : | 
| Chris@815 | 2498 	     m_followPlay == PlaybackScrollPage ? "daw" : | 
| Chris@815 | 2499              "ignore") | 
| Chris@127 | 2500 	.arg(extraAttributes); | 
| Chris@127 | 2501 | 
| Chris@838 | 2502     for (int i = 0; i < (int)m_fixedOrderLayers.size(); ++i) { | 
| Chris@838 | 2503         bool visible = !m_fixedOrderLayers[i]->isLayerDormant(this); | 
| Chris@838 | 2504         m_fixedOrderLayers[i]->toBriefXml(stream, indent + "  ", | 
| Chris@838 | 2505                                           QString("visible=\"%1\"") | 
| Chris@838 | 2506                                           .arg(visible ? "true" : "false")); | 
| Chris@127 | 2507     } | 
| Chris@127 | 2508 | 
| Chris@316 | 2509     stream << indent + "</view>\n"; | 
| Chris@127 | 2510 } | 
| Chris@127 | 2511 | 
| Chris@127 | 2512 ViewPropertyContainer::ViewPropertyContainer(View *v) : | 
| Chris@127 | 2513     m_v(v) | 
| Chris@127 | 2514 { | 
| Chris@728 | 2515 //    cerr << "ViewPropertyContainer: " << this << " is owned by View " << v << endl; | 
| Chris@127 | 2516     connect(m_v, SIGNAL(propertyChanged(PropertyContainer::PropertyName)), | 
| Chris@127 | 2517 	    this, SIGNAL(propertyChanged(PropertyContainer::PropertyName))); | 
| Chris@127 | 2518 } | 
| Chris@127 | 2519 | 
| Chris@728 | 2520 ViewPropertyContainer::~ViewPropertyContainer() | 
| Chris@728 | 2521 { | 
| Chris@728 | 2522 } |