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