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