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