| Chris@0 | 1 /* -*- c-basic-offset: 4 -*-  vi:set ts=8 sts=4 sw=4: */ | 
| Chris@0 | 2 | 
| Chris@0 | 3 /* | 
| Chris@0 | 4     A waveform viewer and audio annotation editor. | 
| Chris@1 | 5     Chris Cannam, Queen Mary University of London, 2005-2006 | 
| Chris@0 | 6 | 
| Chris@0 | 7     This is experimental software.  Not for distribution. | 
| Chris@0 | 8 */ | 
| Chris@0 | 9 | 
| Chris@0 | 10 #include "base/View.h" | 
| Chris@0 | 11 #include "base/Layer.h" | 
| Chris@0 | 12 #include "base/Model.h" | 
| Chris@0 | 13 #include "base/ZoomConstraint.h" | 
| Chris@0 | 14 #include "base/Profiler.h" | 
| Chris@0 | 15 | 
| Chris@0 | 16 #include "layer/TimeRulerLayer.h" //!!! damn, shouldn't be including that here | 
| Chris@0 | 17 | 
| Chris@0 | 18 #include <QPainter> | 
| Chris@0 | 19 #include <QPaintEvent> | 
| Chris@0 | 20 #include <QRect> | 
| Chris@0 | 21 #include <QApplication> | 
| Chris@0 | 22 | 
| Chris@0 | 23 #include <iostream> | 
| Chris@0 | 24 | 
| Chris@0 | 25 //#define DEBUG_VIEW_WIDGET_PAINT 1 | 
| Chris@0 | 26 | 
| Chris@0 | 27 using std::cerr; | 
| Chris@0 | 28 using std::endl; | 
| Chris@0 | 29 | 
| Chris@0 | 30 View::View(QWidget *w, bool showProgress) : | 
| Chris@0 | 31     QFrame(w), | 
| Chris@0 | 32     m_centreFrame(0), | 
| Chris@0 | 33     m_zoomLevel(1024), | 
| Chris@0 | 34     m_newModel(true), | 
| Chris@0 | 35     m_followPan(true), | 
| Chris@0 | 36     m_followZoom(true), | 
| Chris@0 | 37     m_followPlay(PlaybackScrollPage), | 
| Chris@0 | 38     m_lightBackground(true), | 
| Chris@0 | 39     m_showProgress(showProgress), | 
| Chris@0 | 40     m_cache(0), | 
| Chris@0 | 41     m_cacheCentreFrame(0), | 
| Chris@0 | 42     m_cacheZoomLevel(1024), | 
| Chris@9 | 43     m_selectionCached(false), | 
| Chris@0 | 44     m_deleting(false), | 
| Chris@8 | 45     m_haveSelectedLayer(false), | 
| Chris@0 | 46     m_manager(0) | 
| Chris@0 | 47 { | 
| Chris@0 | 48 //    QWidget::setAttribute(Qt::WA_PaintOnScreen); | 
| Chris@0 | 49 } | 
| Chris@0 | 50 | 
| Chris@0 | 51 View::~View() | 
| Chris@0 | 52 { | 
| Chris@0 | 53     m_deleting = true; | 
| Chris@0 | 54 | 
| Chris@0 | 55     for (LayerList::iterator i = m_layers.begin(); i != m_layers.end(); ++i) { | 
| Chris@0 | 56 	delete *i; | 
| Chris@0 | 57     } | 
| Chris@0 | 58 } | 
| Chris@0 | 59 | 
| Chris@0 | 60 PropertyContainer::PropertyList | 
| Chris@0 | 61 View::getProperties() const | 
| Chris@0 | 62 { | 
| Chris@0 | 63     PropertyList list; | 
| Chris@0 | 64     list.push_back(tr("Global Scroll")); | 
| Chris@0 | 65     list.push_back(tr("Global Zoom")); | 
| Chris@0 | 66     list.push_back(tr("Follow Playback")); | 
| Chris@0 | 67     return list; | 
| Chris@0 | 68 } | 
| Chris@0 | 69 | 
| Chris@0 | 70 PropertyContainer::PropertyType | 
| Chris@0 | 71 View::getPropertyType(const PropertyName &name) const | 
| Chris@0 | 72 { | 
| Chris@0 | 73     if (name == tr("Global Scroll")) return ToggleProperty; | 
| Chris@0 | 74     if (name == tr("Global Zoom")) return ToggleProperty; | 
| Chris@0 | 75     if (name == tr("Follow Playback")) return ValueProperty; | 
| Chris@0 | 76     return InvalidProperty; | 
| Chris@0 | 77 } | 
| Chris@0 | 78 | 
| Chris@0 | 79 int | 
| Chris@0 | 80 View::getPropertyRangeAndValue(const PropertyName &name, | 
| Chris@0 | 81 				       int *min, int *max) const | 
| Chris@0 | 82 { | 
| Chris@0 | 83     if (name == tr("Global Scroll")) return m_followPan; | 
| Chris@0 | 84     if (name == tr("Global Zoom")) return m_followZoom; | 
| Chris@0 | 85     if (name == tr("Follow Playback")) { *min = 0; *max = 2; return int(m_followPlay); } | 
| Chris@0 | 86     return PropertyContainer::getPropertyRangeAndValue(name, min, max); | 
| Chris@0 | 87 } | 
| Chris@0 | 88 | 
| Chris@0 | 89 QString | 
| Chris@0 | 90 View::getPropertyValueLabel(const PropertyName &name, | 
| Chris@0 | 91 				  int value) const | 
| Chris@0 | 92 { | 
| Chris@0 | 93     if (name == tr("Follow Playback")) { | 
| Chris@0 | 94 	switch (value) { | 
| Chris@0 | 95 	default: | 
| Chris@0 | 96 	case 0: return tr("Scroll"); | 
| Chris@0 | 97 	case 1: return tr("Page"); | 
| Chris@0 | 98 	case 2: return tr("Off"); | 
| Chris@0 | 99 	} | 
| Chris@0 | 100     } | 
| Chris@0 | 101     return tr("<unknown>"); | 
| Chris@0 | 102 } | 
| Chris@0 | 103 | 
| Chris@0 | 104 void | 
| Chris@0 | 105 View::setProperty(const PropertyName &name, int value) | 
| Chris@0 | 106 { | 
| Chris@0 | 107     if (name == tr("Global Scroll")) { | 
| Chris@0 | 108 	setFollowGlobalPan(value != 0); | 
| Chris@0 | 109     } else if (name == tr("Global Zoom")) { | 
| Chris@0 | 110 	setFollowGlobalZoom(value != 0); | 
| Chris@0 | 111     } else if (name == tr("Follow Playback")) { | 
| Chris@0 | 112 	switch (value) { | 
| Chris@0 | 113 	default: | 
| Chris@0 | 114 	case 0: setPlaybackFollow(PlaybackScrollContinuous); break; | 
| Chris@0 | 115 	case 1: setPlaybackFollow(PlaybackScrollPage); break; | 
| Chris@0 | 116 	case 2: setPlaybackFollow(PlaybackIgnore); break; | 
| Chris@0 | 117 	} | 
| Chris@0 | 118     } | 
| Chris@0 | 119 } | 
| Chris@0 | 120 | 
| Chris@0 | 121 size_t | 
| Chris@0 | 122 View::getPropertyContainerCount() const | 
| Chris@0 | 123 { | 
| Chris@0 | 124     return m_layers.size() + 1; // the 1 is for me | 
| Chris@0 | 125 } | 
| Chris@0 | 126 | 
| Chris@0 | 127 const PropertyContainer * | 
| Chris@0 | 128 View::getPropertyContainer(size_t i) const | 
| Chris@0 | 129 { | 
| Chris@0 | 130     return (const PropertyContainer *)(((View *)this)-> | 
| Chris@0 | 131 				       getPropertyContainer(i)); | 
| Chris@0 | 132 } | 
| Chris@0 | 133 | 
| Chris@0 | 134 PropertyContainer * | 
| Chris@0 | 135 View::getPropertyContainer(size_t i) | 
| Chris@0 | 136 { | 
| Chris@0 | 137     if (i == 0) return this; | 
| Chris@0 | 138     return m_layers[i-1]; | 
| Chris@0 | 139 } | 
| Chris@0 | 140 | 
| Chris@0 | 141 void | 
| Chris@0 | 142 View::propertyContainerSelected(PropertyContainer *pc) | 
| Chris@0 | 143 { | 
| Chris@8 | 144     if (pc == this) { | 
| Chris@8 | 145 	if (m_haveSelectedLayer) { | 
| Chris@8 | 146 	    m_haveSelectedLayer = false; | 
| Chris@8 | 147 	    update(); | 
| Chris@8 | 148 	} | 
| Chris@8 | 149 	return; | 
| Chris@8 | 150     } | 
| Chris@0 | 151 | 
| Chris@0 | 152     delete m_cache; | 
| Chris@0 | 153     m_cache = 0; | 
| Chris@0 | 154 | 
| Chris@0 | 155     Layer *selectedLayer = 0; | 
| Chris@0 | 156 | 
| Chris@0 | 157     for (LayerList::iterator i = m_layers.begin(); i != m_layers.end(); ++i) { | 
| Chris@0 | 158 	if (*i == pc) { | 
| Chris@0 | 159 	    selectedLayer = *i; | 
| Chris@0 | 160 	    m_layers.erase(i); | 
| Chris@0 | 161 	    break; | 
| Chris@0 | 162 	} | 
| Chris@0 | 163     } | 
| Chris@0 | 164 | 
| Chris@0 | 165     if (selectedLayer) { | 
| Chris@8 | 166 	m_haveSelectedLayer = true; | 
| Chris@0 | 167 	m_layers.push_back(selectedLayer); | 
| Chris@0 | 168 	update(); | 
| Chris@8 | 169     } else { | 
| Chris@8 | 170 	m_haveSelectedLayer = false; | 
| Chris@0 | 171     } | 
| Chris@0 | 172 } | 
| Chris@0 | 173 | 
| Chris@8 | 174 void | 
| Chris@8 | 175 View::toolModeChanged() | 
| Chris@8 | 176 { | 
| Chris@8 | 177     std::cerr << "View::toolModeChanged(" << m_manager->getToolMode() << ")" << std::endl; | 
| Chris@8 | 178 } | 
| Chris@8 | 179 | 
| Chris@0 | 180 long | 
| Chris@0 | 181 View::getStartFrame() const | 
| Chris@0 | 182 { | 
| Chris@0 | 183     size_t w2 = (width() / 2) * m_zoomLevel; | 
| Chris@0 | 184     size_t frame = m_centreFrame; | 
| Chris@0 | 185     if (frame >= w2) { | 
| Chris@0 | 186 	frame -= w2; | 
| Chris@0 | 187 	return (frame / m_zoomLevel * m_zoomLevel); | 
| Chris@0 | 188     } else { | 
| Chris@0 | 189 	frame = w2 - frame; | 
| Chris@0 | 190 	frame = frame / m_zoomLevel * m_zoomLevel; | 
| Chris@0 | 191 	return -(long)frame - m_zoomLevel; | 
| Chris@0 | 192     } | 
| Chris@0 | 193 } | 
| Chris@0 | 194 | 
| Chris@0 | 195 size_t | 
| Chris@0 | 196 View::getEndFrame() const | 
| Chris@0 | 197 { | 
| Chris@0 | 198     return getStartFrame() + (width() * m_zoomLevel) - 1; | 
| Chris@0 | 199 } | 
| Chris@0 | 200 | 
| Chris@0 | 201 void | 
| Chris@0 | 202 View::setStartFrame(long f) | 
| Chris@0 | 203 { | 
| Chris@0 | 204     setCentreFrame(f + m_zoomLevel * (width() / 2)); | 
| Chris@0 | 205 } | 
| Chris@0 | 206 | 
| Chris@10 | 207 bool | 
| Chris@0 | 208 View::setCentreFrame(size_t f, bool e) | 
| Chris@0 | 209 { | 
| Chris@10 | 210     bool changeVisible = false; | 
| Chris@10 | 211 | 
| Chris@0 | 212     if (m_centreFrame != f) { | 
| Chris@0 | 213 | 
| Chris@0 | 214 	int formerPixel = m_centreFrame / m_zoomLevel; | 
| Chris@0 | 215 | 
| Chris@0 | 216 	m_centreFrame = f; | 
| Chris@0 | 217 | 
| Chris@0 | 218 	int newPixel = m_centreFrame / m_zoomLevel; | 
| Chris@0 | 219 | 
| Chris@0 | 220 	if (newPixel != formerPixel) { | 
| Chris@0 | 221 | 
| Chris@0 | 222 #ifdef DEBUG_VIEW_WIDGET_PAINT | 
| Chris@0 | 223 	    std::cout << "View(" << this << ")::setCentreFrame: newPixel " << newPixel << ", formerPixel " << formerPixel << std::endl; | 
| Chris@0 | 224 #endif | 
| Chris@0 | 225 	    update(); | 
| Chris@10 | 226 | 
| Chris@10 | 227 	    changeVisible = true; | 
| Chris@0 | 228 	} | 
| Chris@0 | 229 | 
| Chris@0 | 230 	if (e) emit centreFrameChanged(this, f, m_followPan); | 
| Chris@0 | 231     } | 
| Chris@10 | 232 | 
| Chris@10 | 233     return changeVisible; | 
| Chris@0 | 234 } | 
| Chris@0 | 235 | 
| Chris@15 | 236 int | 
| Chris@15 | 237 View::getXForFrame(long frame) const | 
| Chris@15 | 238 { | 
| Chris@15 | 239     return (frame - getStartFrame()) / m_zoomLevel; | 
| Chris@15 | 240 } | 
| Chris@15 | 241 | 
| Chris@15 | 242 long | 
| Chris@15 | 243 View::getFrameForX(int x) const | 
| Chris@15 | 244 { | 
| Chris@15 | 245     return (long(x) * long(m_zoomLevel)) + getStartFrame(); | 
| Chris@15 | 246 } | 
| Chris@15 | 247 | 
| Chris@0 | 248 void | 
| Chris@0 | 249 View::setZoomLevel(size_t z) | 
| Chris@0 | 250 { | 
| Chris@0 | 251     if (m_zoomLevel != int(z)) { | 
| Chris@0 | 252 	m_zoomLevel = z; | 
| Chris@0 | 253 	emit zoomLevelChanged(this, z, m_followZoom); | 
| Chris@0 | 254 	update(); | 
| Chris@0 | 255     } | 
| Chris@0 | 256 } | 
| Chris@0 | 257 | 
| Chris@0 | 258 void | 
| Chris@0 | 259 View::addLayer(Layer *layer) | 
| Chris@0 | 260 { | 
| Chris@0 | 261     delete m_cache; | 
| Chris@0 | 262     m_cache = 0; | 
| Chris@0 | 263 | 
| Chris@0 | 264     m_layers.push_back(layer); | 
| Chris@0 | 265 | 
| Chris@0 | 266     m_progressBars[layer] = new LayerProgressBar(this); | 
| Chris@0 | 267     m_progressBars[layer]->setMinimum(0); | 
| Chris@0 | 268     m_progressBars[layer]->setMaximum(100); | 
| Chris@0 | 269     m_progressBars[layer]->setMinimumWidth(80); | 
| Chris@0 | 270     m_progressBars[layer]->hide(); | 
| Chris@0 | 271 | 
| Chris@0 | 272     connect(layer, SIGNAL(layerParametersChanged()), | 
| Chris@0 | 273 	    this,    SLOT(layerParametersChanged())); | 
| Chris@0 | 274     connect(layer, SIGNAL(layerNameChanged()), | 
| Chris@0 | 275 	    this,    SLOT(layerNameChanged())); | 
| Chris@0 | 276     connect(layer, SIGNAL(modelChanged()), | 
| Chris@0 | 277 	    this,    SLOT(modelChanged())); | 
| Chris@0 | 278     connect(layer, SIGNAL(modelCompletionChanged()), | 
| Chris@0 | 279 	    this,    SLOT(modelCompletionChanged())); | 
| Chris@0 | 280     connect(layer, SIGNAL(modelChanged(size_t, size_t)), | 
| Chris@0 | 281 	    this,    SLOT(modelChanged(size_t, size_t))); | 
| Chris@0 | 282     connect(layer, SIGNAL(modelReplaced()), | 
| Chris@0 | 283 	    this,    SLOT(modelReplaced())); | 
| Chris@0 | 284 | 
| Chris@0 | 285     m_newModel = true; | 
| Chris@0 | 286     update(); | 
| Chris@0 | 287 | 
| Chris@0 | 288     emit propertyContainerAdded(layer); | 
| Chris@0 | 289 } | 
| Chris@0 | 290 | 
| Chris@0 | 291 void | 
| Chris@0 | 292 View::removeLayer(Layer *layer) | 
| Chris@0 | 293 { | 
| Chris@0 | 294     if (m_deleting) { | 
| Chris@0 | 295 	return; | 
| Chris@0 | 296     } | 
| Chris@0 | 297 | 
| Chris@0 | 298     delete m_cache; | 
| Chris@0 | 299     m_cache = 0; | 
| Chris@0 | 300 | 
| Chris@0 | 301     for (LayerList::iterator i = m_layers.begin(); i != m_layers.end(); ++i) { | 
| Chris@0 | 302 	if (*i == layer) { | 
| Chris@0 | 303 	    m_layers.erase(i); | 
| Chris@0 | 304 	    if (m_progressBars.find(layer) != m_progressBars.end()) { | 
| Chris@0 | 305 		delete m_progressBars[layer]; | 
| Chris@0 | 306 		m_progressBars.erase(layer); | 
| Chris@0 | 307 	    } | 
| Chris@0 | 308 	    break; | 
| Chris@0 | 309 	} | 
| Chris@0 | 310     } | 
| Chris@0 | 311 | 
| Chris@0 | 312     update(); | 
| Chris@0 | 313 | 
| Chris@0 | 314     emit propertyContainerRemoved(layer); | 
| Chris@0 | 315 } | 
| Chris@0 | 316 | 
| Chris@8 | 317 Layer * | 
| Chris@8 | 318 View::getSelectedLayer() | 
| Chris@8 | 319 { | 
| Chris@8 | 320     if (m_haveSelectedLayer && !m_layers.empty()) { | 
| Chris@8 | 321 	return getLayer(getLayerCount() - 1); | 
| Chris@8 | 322     } else { | 
| Chris@8 | 323 	return 0; | 
| Chris@8 | 324     } | 
| Chris@8 | 325 } | 
| Chris@8 | 326 | 
| Chris@0 | 327 void | 
| Chris@0 | 328 View::setViewManager(ViewManager *manager) | 
| Chris@0 | 329 { | 
| Chris@0 | 330     if (m_manager) { | 
| Chris@0 | 331 	m_manager->disconnect(this, SLOT(viewManagerCentreFrameChanged(void *, unsigned long, bool))); | 
| Chris@0 | 332 	m_manager->disconnect(this, SLOT(viewManagerZoomLevelChanged(void *, unsigned long, bool))); | 
| Chris@0 | 333 	disconnect(m_manager, SIGNAL(centreFrameChanged(void *, unsigned long, bool))); | 
| Chris@0 | 334 	disconnect(m_manager, SIGNAL(zoomLevelChanged(void *, unsigned long, bool))); | 
| Chris@8 | 335 	disconnect(m_manager, SIGNAL(toolModeChanged())); | 
| Chris@8 | 336 	disconnect(m_manager, SIGNAL(selectionChanged())); | 
| Chris@9 | 337 	disconnect(m_manager, SIGNAL(inProgressSelectionChanged())); | 
| Chris@0 | 338     } | 
| Chris@0 | 339 | 
| Chris@0 | 340     m_manager = manager; | 
| Chris@0 | 341     if (m_followPan) setCentreFrame(m_manager->getGlobalCentreFrame(), false); | 
| Chris@0 | 342     if (m_followZoom) setZoomLevel(m_manager->getGlobalZoom()); | 
| Chris@0 | 343 | 
| Chris@0 | 344     connect(m_manager, SIGNAL(centreFrameChanged(void *, unsigned long, bool)), | 
| Chris@0 | 345 	    this, SLOT(viewManagerCentreFrameChanged(void *, unsigned long, bool))); | 
| Chris@0 | 346     connect(m_manager, SIGNAL(playbackFrameChanged(unsigned long)), | 
| Chris@0 | 347 	    this, SLOT(viewManagerPlaybackFrameChanged(unsigned long))); | 
| Chris@0 | 348     connect(m_manager, SIGNAL(zoomLevelChanged(void *, unsigned long, bool)), | 
| Chris@0 | 349 	    this, SLOT(viewManagerZoomLevelChanged(void *, unsigned long, bool))); | 
| Chris@8 | 350     connect(m_manager, SIGNAL(toolModeChanged()), | 
| Chris@8 | 351 	    this, SLOT(toolModeChanged())); | 
| Chris@8 | 352     connect(m_manager, SIGNAL(selectionChanged()), | 
| Chris@9 | 353 	    this, SLOT(selectionChanged())); | 
| Chris@9 | 354     connect(m_manager, SIGNAL(inProgressSelectionChanged()), | 
| Chris@9 | 355 	    this, SLOT(selectionChanged())); | 
| Chris@0 | 356 | 
| Chris@0 | 357     connect(this, SIGNAL(centreFrameChanged(void *, unsigned long, bool)), | 
| Chris@0 | 358 	    m_manager, SIGNAL(centreFrameChanged(void *, unsigned long, bool))); | 
| Chris@0 | 359     connect(this, SIGNAL(zoomLevelChanged(void *, unsigned long, bool)), | 
| Chris@0 | 360 	    m_manager, SIGNAL(zoomLevelChanged(void *, unsigned long, bool))); | 
| Chris@8 | 361 | 
| Chris@8 | 362     toolModeChanged(); | 
| Chris@0 | 363 } | 
| Chris@0 | 364 | 
| Chris@0 | 365 void | 
| Chris@0 | 366 View::setFollowGlobalPan(bool f) | 
| Chris@0 | 367 { | 
| Chris@0 | 368     m_followPan = f; | 
| Chris@0 | 369     emit propertyContainerPropertyChanged(this); | 
| Chris@0 | 370 } | 
| Chris@0 | 371 | 
| Chris@0 | 372 void | 
| Chris@0 | 373 View::setFollowGlobalZoom(bool f) | 
| Chris@0 | 374 { | 
| Chris@0 | 375     m_followZoom = f; | 
| Chris@0 | 376     emit propertyContainerPropertyChanged(this); | 
| Chris@0 | 377 } | 
| Chris@0 | 378 | 
| Chris@0 | 379 void | 
| Chris@0 | 380 View::setPlaybackFollow(PlaybackFollowMode m) | 
| Chris@0 | 381 { | 
| Chris@0 | 382     m_followPlay = m; | 
| Chris@0 | 383     emit propertyContainerPropertyChanged(this); | 
| Chris@0 | 384 } | 
| Chris@0 | 385 | 
| Chris@0 | 386 void | 
| Chris@0 | 387 View::modelChanged() | 
| Chris@0 | 388 { | 
| Chris@0 | 389     QObject *obj = sender(); | 
| Chris@0 | 390 | 
| Chris@0 | 391 #ifdef DEBUG_VIEW_WIDGET_PAINT | 
| Chris@1 | 392     std::cerr << "View(" << this << ")::modelChanged()" << std::endl; | 
| Chris@0 | 393 #endif | 
| Chris@0 | 394     delete m_cache; | 
| Chris@0 | 395     m_cache = 0; | 
| Chris@0 | 396 | 
| Chris@0 | 397     checkProgress(obj); | 
| Chris@0 | 398 | 
| Chris@0 | 399     update(); | 
| Chris@0 | 400 } | 
| Chris@0 | 401 | 
| Chris@0 | 402 void | 
| Chris@0 | 403 View::modelChanged(size_t startFrame, size_t endFrame) | 
| Chris@0 | 404 { | 
| Chris@0 | 405     QObject *obj = sender(); | 
| Chris@0 | 406 | 
| Chris@0 | 407     long myStartFrame = getStartFrame(); | 
| Chris@0 | 408     size_t myEndFrame = getEndFrame(); | 
| Chris@0 | 409 | 
| Chris@1 | 410 #ifdef DEBUG_VIEW_WIDGET_PAINT | 
| Chris@1 | 411     std::cerr << "View(" << this << ")::modelChanged(" << startFrame << "," << endFrame << ") [me " << myStartFrame << "," << myEndFrame << "]" << std::endl; | 
| Chris@1 | 412 #endif | 
| Chris@1 | 413 | 
| Chris@0 | 414     if (myStartFrame > 0 && endFrame < size_t(myStartFrame)) { | 
| Chris@0 | 415 	checkProgress(obj); | 
| Chris@0 | 416 	return; | 
| Chris@0 | 417     } | 
| Chris@0 | 418     if (startFrame > myEndFrame) { | 
| Chris@0 | 419 	checkProgress(obj); | 
| Chris@0 | 420 	return; | 
| Chris@0 | 421     } | 
| Chris@0 | 422 | 
| Chris@0 | 423     delete m_cache; | 
| Chris@0 | 424     m_cache = 0; | 
| Chris@0 | 425 | 
| Chris@0 | 426     if (long(startFrame) < myStartFrame) startFrame = myStartFrame; | 
| Chris@0 | 427     if (endFrame > myEndFrame) endFrame = myEndFrame; | 
| Chris@0 | 428 | 
| Chris@15 | 429     int x0 = getXForFrame(startFrame); | 
| Chris@15 | 430     int x1 = getXForFrame(endFrame + 1); | 
| Chris@15 | 431     if (x1 < x0) x1 = x0; | 
| Chris@0 | 432 | 
| Chris@0 | 433     checkProgress(obj); | 
| Chris@0 | 434 | 
| Chris@0 | 435     update(x0, 0, x1 - x0 + 1, height()); | 
| Chris@0 | 436 } | 
| Chris@0 | 437 | 
| Chris@0 | 438 void | 
| Chris@0 | 439 View::modelCompletionChanged() | 
| Chris@0 | 440 { | 
| Chris@0 | 441     QObject *obj = sender(); | 
| Chris@0 | 442     checkProgress(obj); | 
| Chris@0 | 443 } | 
| Chris@0 | 444 | 
| Chris@0 | 445 void | 
| Chris@0 | 446 View::modelReplaced() | 
| Chris@0 | 447 { | 
| Chris@0 | 448 #ifdef DEBUG_VIEW_WIDGET_PAINT | 
| Chris@1 | 449     std::cerr << "View(" << this << ")::modelReplaced()" << std::endl; | 
| Chris@0 | 450 #endif | 
| Chris@1 | 451     delete m_cache; | 
| Chris@1 | 452     m_cache = 0; | 
| Chris@1 | 453 | 
| Chris@0 | 454     m_newModel = true; | 
| Chris@0 | 455     update(); | 
| Chris@0 | 456 } | 
| Chris@0 | 457 | 
| Chris@0 | 458 void | 
| Chris@0 | 459 View::layerParametersChanged() | 
| Chris@0 | 460 { | 
| Chris@0 | 461     Layer *layer = dynamic_cast<Layer *>(sender()); | 
| Chris@0 | 462 | 
| Chris@0 | 463 #ifdef DEBUG_VIEW_WIDGET_PAINT | 
| Chris@0 | 464     std::cerr << "View::layerParametersChanged()" << std::endl; | 
| Chris@0 | 465 #endif | 
| Chris@0 | 466 | 
| Chris@0 | 467     delete m_cache; | 
| Chris@0 | 468     m_cache = 0; | 
| Chris@0 | 469     update(); | 
| Chris@0 | 470 | 
| Chris@0 | 471     if (layer) { | 
| Chris@0 | 472 	emit propertyContainerPropertyChanged(layer); | 
| Chris@0 | 473     } | 
| Chris@0 | 474 } | 
| Chris@0 | 475 | 
| Chris@0 | 476 void | 
| Chris@0 | 477 View::layerNameChanged() | 
| Chris@0 | 478 { | 
| Chris@0 | 479     Layer *layer = dynamic_cast<Layer *>(sender()); | 
| Chris@0 | 480     if (layer) emit propertyContainerNameChanged(layer); | 
| Chris@0 | 481 } | 
| Chris@0 | 482 | 
| Chris@0 | 483 void | 
| Chris@0 | 484 View::viewManagerCentreFrameChanged(void *p, unsigned long f, bool locked) | 
| Chris@0 | 485 { | 
| Chris@0 | 486     if (m_followPan && p != this && locked) { | 
| Chris@0 | 487 	if (m_manager && (sender() == m_manager)) { | 
| Chris@0 | 488 #ifdef DEBUG_VIEW_WIDGET_PAINT | 
| Chris@0 | 489 	    std::cerr << this << ": manager frame changed " << f << " from " << p << std::endl; | 
| Chris@0 | 490 #endif | 
| Chris@0 | 491 	    setCentreFrame(f); | 
| Chris@0 | 492 	    if (p == this) repaint(); | 
| Chris@0 | 493 	} | 
| Chris@0 | 494     } | 
| Chris@0 | 495 } | 
| Chris@0 | 496 | 
| Chris@0 | 497 void | 
| Chris@0 | 498 View::viewManagerPlaybackFrameChanged(unsigned long f) | 
| Chris@0 | 499 { | 
| Chris@0 | 500     if (m_manager) { | 
| Chris@0 | 501 	if (sender() != m_manager) return; | 
| Chris@0 | 502     } | 
| Chris@0 | 503 | 
| Chris@0 | 504     if (m_playPointerFrame == f) return; | 
| Chris@15 | 505     bool visible = (getXForFrame(m_playPointerFrame) != getXForFrame(f)); | 
| Chris@0 | 506     size_t oldPlayPointerFrame = m_playPointerFrame; | 
| Chris@0 | 507     m_playPointerFrame = f; | 
| Chris@0 | 508     if (!visible) return; | 
| Chris@0 | 509 | 
| Chris@0 | 510     switch (m_followPlay) { | 
| Chris@0 | 511 | 
| Chris@0 | 512     case PlaybackScrollContinuous: | 
| Chris@10 | 513 	if (QApplication::mouseButtons() == Qt::NoButton && | 
| Chris@10 | 514 	    QApplication::keyboardModifiers() == Qt::NoModifier) { | 
| Chris@0 | 515 	    setCentreFrame(f, false); | 
| Chris@0 | 516 	} | 
| Chris@0 | 517 	break; | 
| Chris@0 | 518 | 
| Chris@0 | 519     case PlaybackScrollPage: | 
| Chris@0 | 520     { | 
| Chris@15 | 521 	int xold = getXForFrame(oldPlayPointerFrame); | 
| Chris@10 | 522 	repaint(xold - 1, 0, 3, height()); | 
| Chris@10 | 523 | 
| Chris@15 | 524 	long w = getEndFrame() - getStartFrame(); | 
| Chris@0 | 525 	w -= w/5; | 
| Chris@0 | 526 	long sf = (f / w) * w - w/8; | 
| Chris@10 | 527 | 
| Chris@10 | 528 	if (m_manager && | 
| Chris@10 | 529 	    m_manager->isPlaying() && | 
| Chris@10 | 530 	    m_manager->getPlaySelectionMode()) { | 
| Chris@10 | 531 	    ViewManager::SelectionList selections = m_manager->getSelections(); | 
| Chris@10 | 532 	    if (!selections.empty()) { | 
| Chris@10 | 533 		size_t selectionStart = selections.begin()->getStartFrame(); | 
| Chris@10 | 534 		if (sf < long(selectionStart) - w / 10) { | 
| Chris@10 | 535 		    sf = long(selectionStart) - w / 10; | 
| Chris@10 | 536 		} | 
| Chris@10 | 537 	    } | 
| Chris@10 | 538 	} | 
| Chris@10 | 539 | 
| Chris@0 | 540 #ifdef DEBUG_VIEW_WIDGET_PAINT | 
| Chris@0 | 541 	std::cerr << "PlaybackScrollPage: f = " << f << ", sf = " << sf << ", start frame " | 
| Chris@0 | 542 		  << getStartFrame() << std::endl; | 
| Chris@0 | 543 #endif | 
| Chris@10 | 544 | 
| Chris@15 | 545 	// We don't consider scrolling unless the pointer is outside | 
| Chris@15 | 546 	// the clearly visible range already | 
| Chris@15 | 547 | 
| Chris@15 | 548 	int xnew = getXForFrame(m_playPointerFrame); | 
| Chris@15 | 549 | 
| Chris@15 | 550 	if (xnew < width()/8 || xnew > (width()*7)/8) { | 
| Chris@15 | 551 	    if (QApplication::mouseButtons() == Qt::NoButton && | 
| Chris@15 | 552 		QApplication::keyboardModifiers() == Qt::NoModifier) { | 
| Chris@15 | 553 		long offset = getFrameForX(width()/2) - getStartFrame(); | 
| Chris@15 | 554 		long newCentre = sf + offset; | 
| Chris@15 | 555 		bool changed = setCentreFrame(newCentre, false); | 
| Chris@15 | 556 		if (changed) { | 
| Chris@15 | 557 		    xold = getXForFrame(oldPlayPointerFrame); | 
| Chris@15 | 558 		    update(xold - 1, 0, 3, height()); | 
| Chris@15 | 559 		} | 
| Chris@10 | 560 	    } | 
| Chris@10 | 561 	} | 
| Chris@10 | 562 | 
| Chris@0 | 563 	update(xnew - 1, 0, 3, height()); | 
| Chris@10 | 564 | 
| Chris@0 | 565 	break; | 
| Chris@0 | 566     } | 
| Chris@0 | 567 | 
| Chris@0 | 568     case PlaybackIgnore: | 
| Chris@0 | 569 	if (long(f) >= getStartFrame() && f < getEndFrame()) { | 
| Chris@0 | 570 	    update(); | 
| Chris@0 | 571 	} | 
| Chris@0 | 572 	break; | 
| Chris@0 | 573     } | 
| Chris@0 | 574 } | 
| Chris@0 | 575 | 
| Chris@0 | 576 void | 
| Chris@0 | 577 View::viewManagerZoomLevelChanged(void *p, unsigned long z, bool locked) | 
| Chris@0 | 578 { | 
| Chris@0 | 579     if (m_followZoom && p != this && locked) { | 
| Chris@0 | 580 	if (m_manager && (sender() == m_manager)) { | 
| Chris@0 | 581 	    setZoomLevel(z); | 
| Chris@0 | 582 	    if (p == this) repaint(); | 
| Chris@0 | 583 	} | 
| Chris@0 | 584     } | 
| Chris@0 | 585 } | 
| Chris@0 | 586 | 
| Chris@9 | 587 void | 
| Chris@9 | 588 View::selectionChanged() | 
| Chris@9 | 589 { | 
| Chris@9 | 590     if (m_selectionCached) { | 
| Chris@9 | 591 	delete m_cache; | 
| Chris@9 | 592 	m_cache = 0; | 
| Chris@9 | 593 	m_selectionCached = false; | 
| Chris@9 | 594     } | 
| Chris@9 | 595     update(); | 
| Chris@9 | 596 } | 
| Chris@9 | 597 | 
| Chris@0 | 598 size_t | 
| Chris@0 | 599 View::getModelsStartFrame() const | 
| Chris@0 | 600 { | 
| Chris@0 | 601     bool first = true; | 
| Chris@0 | 602     size_t startFrame = 0; | 
| Chris@0 | 603 | 
| Chris@0 | 604     for (LayerList::const_iterator i = m_layers.begin(); i != m_layers.end(); ++i) { | 
| Chris@0 | 605 | 
| Chris@0 | 606 	if ((*i)->getModel() && (*i)->getModel()->isOK()) { | 
| Chris@0 | 607 | 
| Chris@0 | 608 	    size_t thisStartFrame = (*i)->getModel()->getStartFrame(); | 
| Chris@0 | 609 | 
| Chris@0 | 610 	    if (first || thisStartFrame < startFrame) { | 
| Chris@0 | 611 		startFrame = thisStartFrame; | 
| Chris@0 | 612 	    } | 
| Chris@0 | 613 	    first = false; | 
| Chris@0 | 614 	} | 
| Chris@0 | 615     } | 
| Chris@0 | 616     return startFrame; | 
| Chris@0 | 617 } | 
| Chris@0 | 618 | 
| Chris@0 | 619 size_t | 
| Chris@0 | 620 View::getModelsEndFrame() const | 
| Chris@0 | 621 { | 
| Chris@0 | 622     bool first = true; | 
| Chris@0 | 623     size_t endFrame = 0; | 
| Chris@0 | 624 | 
| Chris@0 | 625     for (LayerList::const_iterator i = m_layers.begin(); i != m_layers.end(); ++i) { | 
| Chris@0 | 626 | 
| Chris@0 | 627 	if ((*i)->getModel() && (*i)->getModel()->isOK()) { | 
| Chris@0 | 628 | 
| Chris@0 | 629 	    size_t thisEndFrame = (*i)->getModel()->getEndFrame(); | 
| Chris@0 | 630 | 
| Chris@0 | 631 	    if (first || thisEndFrame > endFrame) { | 
| Chris@0 | 632 		endFrame = thisEndFrame; | 
| Chris@0 | 633 	    } | 
| Chris@0 | 634 	    first = false; | 
| Chris@0 | 635 	} | 
| Chris@0 | 636     } | 
| Chris@0 | 637 | 
| Chris@0 | 638     if (first) return getModelsStartFrame(); | 
| Chris@0 | 639     return endFrame; | 
| Chris@0 | 640 } | 
| Chris@0 | 641 | 
| Chris@0 | 642 int | 
| Chris@0 | 643 View::getModelsSampleRate() const | 
| Chris@0 | 644 { | 
| Chris@0 | 645     //!!! Just go for the first, for now.  If we were supporting | 
| Chris@0 | 646     // multiple samplerates, we'd probably want to do frame/time | 
| Chris@0 | 647     // conversion in the model | 
| Chris@0 | 648 | 
| Chris@0 | 649     for (LayerList::const_iterator i = m_layers.begin(); i != m_layers.end(); ++i) { | 
| Chris@0 | 650 	if ((*i)->getModel() && (*i)->getModel()->isOK()) { | 
| Chris@0 | 651 	    return (*i)->getModel()->getSampleRate(); | 
| Chris@0 | 652 	} | 
| Chris@0 | 653     } | 
| Chris@0 | 654     return 0; | 
| Chris@0 | 655 } | 
| Chris@0 | 656 | 
| Chris@0 | 657 bool | 
| Chris@0 | 658 View::areLayersScrollable() const | 
| Chris@0 | 659 { | 
| Chris@0 | 660     // True iff all views are scrollable | 
| Chris@0 | 661     for (LayerList::const_iterator i = m_layers.begin(); i != m_layers.end(); ++i) { | 
| Chris@0 | 662 	if (!(*i)->isLayerScrollable()) return false; | 
| Chris@0 | 663     } | 
| Chris@0 | 664     return true; | 
| Chris@0 | 665 } | 
| Chris@0 | 666 | 
| Chris@0 | 667 View::LayerList | 
| Chris@0 | 668 View::getScrollableBackLayers(bool &changed) const | 
| Chris@0 | 669 { | 
| Chris@0 | 670     changed = false; | 
| Chris@0 | 671 | 
| Chris@0 | 672     LayerList scrollables; | 
| Chris@0 | 673     for (LayerList::const_iterator i = m_layers.begin(); i != m_layers.end(); ++i) { | 
| Chris@0 | 674 	if ((*i)->isLayerScrollable()) scrollables.push_back(*i); | 
| Chris@0 | 675 	else { | 
| Chris@0 | 676 	    if (scrollables != m_lastScrollableBackLayers) { | 
| Chris@0 | 677 		m_lastScrollableBackLayers = scrollables; | 
| Chris@0 | 678 		changed = true; | 
| Chris@0 | 679 	    } | 
| Chris@0 | 680 	    return scrollables; | 
| Chris@0 | 681 	} | 
| Chris@0 | 682     } | 
| Chris@0 | 683 | 
| Chris@0 | 684     if (scrollables.size() == 1 && | 
| Chris@0 | 685 	dynamic_cast<TimeRulerLayer *>(*scrollables.begin())) { | 
| Chris@0 | 686 | 
| Chris@0 | 687 	// If only the ruler is scrollable, it's not worth the bother | 
| Chris@0 | 688 	// -- it probably redraws as quickly as it refreshes from | 
| Chris@0 | 689 	// cache | 
| Chris@0 | 690 	scrollables.clear(); | 
| Chris@0 | 691     } | 
| Chris@0 | 692 | 
| Chris@0 | 693     if (scrollables != m_lastScrollableBackLayers) { | 
| Chris@0 | 694 	m_lastScrollableBackLayers = scrollables; | 
| Chris@0 | 695 	changed = true; | 
| Chris@0 | 696     } | 
| Chris@0 | 697     return scrollables; | 
| Chris@0 | 698 } | 
| Chris@0 | 699 | 
| Chris@0 | 700 View::LayerList | 
| Chris@0 | 701 View::getNonScrollableFrontLayers(bool &changed) const | 
| Chris@0 | 702 { | 
| Chris@0 | 703     changed = false; | 
| Chris@0 | 704     LayerList scrollables = getScrollableBackLayers(changed); | 
| Chris@0 | 705     LayerList nonScrollables; | 
| Chris@0 | 706 | 
| Chris@0 | 707     // Everything in front of the first non-scrollable from the back | 
| Chris@0 | 708     // should also be considered non-scrollable | 
| Chris@0 | 709 | 
| Chris@0 | 710     size_t count = 0; | 
| Chris@0 | 711     for (LayerList::const_iterator i = m_layers.begin(); i != m_layers.end(); ++i) { | 
| Chris@0 | 712 	if (count < scrollables.size()) { | 
| Chris@0 | 713 	    ++count; | 
| Chris@0 | 714 	    continue; | 
| Chris@0 | 715 	} | 
| Chris@0 | 716 	nonScrollables.push_back(*i); | 
| Chris@0 | 717     } | 
| Chris@0 | 718 | 
| Chris@0 | 719     if (nonScrollables != m_lastNonScrollableBackLayers) { | 
| Chris@0 | 720 	m_lastNonScrollableBackLayers = nonScrollables; | 
| Chris@0 | 721 	changed = true; | 
| Chris@0 | 722     } | 
| Chris@0 | 723 | 
| Chris@0 | 724     return nonScrollables; | 
| Chris@0 | 725 } | 
| Chris@0 | 726 | 
| Chris@0 | 727 size_t | 
| Chris@0 | 728 View::getZoomConstraintBlockSize(size_t blockSize, | 
| Chris@0 | 729 				 ZoomConstraint::RoundingDirection dir) | 
| Chris@0 | 730     const | 
| Chris@0 | 731 { | 
| Chris@0 | 732     size_t candidate = blockSize; | 
| Chris@0 | 733     bool haveCandidate = false; | 
| Chris@0 | 734 | 
| Chris@0 | 735     for (LayerList::const_iterator i = m_layers.begin(); i != m_layers.end(); ++i) { | 
| Chris@0 | 736 | 
| Chris@0 | 737 	if ((*i)->getZoomConstraint()) { | 
| Chris@0 | 738 | 
| Chris@0 | 739 	    size_t thisBlockSize = | 
| Chris@0 | 740 		(*i)->getZoomConstraint()->getNearestBlockSize | 
| Chris@0 | 741 		(blockSize, dir); | 
| Chris@0 | 742 | 
| Chris@0 | 743 	    // Go for the block size that's furthest from the one | 
| Chris@0 | 744 	    // passed in.  Most of the time, that's what we want. | 
| Chris@0 | 745 	    if (!haveCandidate || | 
| Chris@0 | 746 		(thisBlockSize > blockSize && thisBlockSize > candidate) || | 
| Chris@0 | 747 		(thisBlockSize < blockSize && thisBlockSize < candidate)) { | 
| Chris@0 | 748 		candidate = thisBlockSize; | 
| Chris@0 | 749 		haveCandidate = true; | 
| Chris@0 | 750 	    } | 
| Chris@0 | 751 	} | 
| Chris@0 | 752     } | 
| Chris@0 | 753 | 
| Chris@0 | 754     return candidate; | 
| Chris@0 | 755 } | 
| Chris@0 | 756 | 
| Chris@0 | 757 void | 
| Chris@0 | 758 View::zoom(bool in) | 
| Chris@0 | 759 { | 
| Chris@0 | 760     int newZoomLevel = m_zoomLevel; | 
| Chris@0 | 761 | 
| Chris@0 | 762     if (in) { | 
| Chris@0 | 763 	newZoomLevel = getZoomConstraintBlockSize(newZoomLevel - 1, | 
| Chris@0 | 764 						  ZoomConstraint::RoundDown); | 
| Chris@0 | 765     } else { | 
| Chris@0 | 766 	newZoomLevel = getZoomConstraintBlockSize(newZoomLevel + 1, | 
| Chris@0 | 767 						  ZoomConstraint::RoundUp); | 
| Chris@0 | 768     } | 
| Chris@0 | 769 | 
| Chris@0 | 770     if (newZoomLevel != m_zoomLevel) { | 
| Chris@0 | 771 	setZoomLevel(newZoomLevel); | 
| Chris@0 | 772     } | 
| Chris@0 | 773 } | 
| Chris@0 | 774 | 
| Chris@0 | 775 void | 
| Chris@12 | 776 View::scroll(bool right, bool lots) | 
| Chris@12 | 777 { | 
| Chris@12 | 778     long delta; | 
| Chris@12 | 779     if (lots) { | 
| Chris@15 | 780 	delta = (getEndFrame() - getStartFrame()) / 2; | 
| Chris@12 | 781     } else { | 
| Chris@15 | 782 	delta = (getEndFrame() - getStartFrame()) / 20; | 
| Chris@12 | 783     } | 
| Chris@12 | 784     if (right) delta = -delta; | 
| Chris@12 | 785 | 
| Chris@12 | 786     if (int(m_centreFrame) < delta) { | 
| Chris@12 | 787 	setCentreFrame(0); | 
| Chris@12 | 788     } else if (int(m_centreFrame) - delta >= int(getModelsEndFrame())) { | 
| Chris@12 | 789 	setCentreFrame(getModelsEndFrame()); | 
| Chris@12 | 790     } else { | 
| Chris@12 | 791 	setCentreFrame(m_centreFrame - delta); | 
| Chris@12 | 792     } | 
| Chris@12 | 793 } | 
| Chris@12 | 794 | 
| Chris@12 | 795 void | 
| Chris@0 | 796 View::checkProgress(void *object) | 
| Chris@0 | 797 { | 
| Chris@0 | 798 //    std::cerr << "View::checkProgress(" << object << ")" << std::endl; | 
| Chris@0 | 799 | 
| Chris@0 | 800     if (!m_showProgress) return; | 
| Chris@0 | 801 | 
| Chris@0 | 802     int ph = height(); | 
| Chris@0 | 803 | 
| Chris@0 | 804     for (ProgressMap::const_iterator i = m_progressBars.begin(); | 
| Chris@0 | 805 	 i != m_progressBars.end(); ++i) { | 
| Chris@0 | 806 | 
| Chris@0 | 807 	if (i->first == object) { | 
| Chris@0 | 808 | 
| Chris@0 | 809 	    int completion = i->first->getCompletion(); | 
| Chris@0 | 810 | 
| Chris@0 | 811 	    if (completion >= 100) { | 
| Chris@0 | 812 | 
| Chris@0 | 813 		i->second->hide(); | 
| Chris@0 | 814 | 
| Chris@0 | 815 	    } else { | 
| Chris@0 | 816 | 
| Chris@0 | 817 		i->second->setText(i->first->getPropertyContainerName()); | 
| Chris@0 | 818 		i->second->setValue(completion); | 
| Chris@0 | 819 		i->second->move(0, ph - i->second->height()); | 
| Chris@0 | 820 | 
| Chris@0 | 821 		i->second->show(); | 
| Chris@0 | 822 		i->second->update(); | 
| Chris@0 | 823 | 
| Chris@0 | 824 		ph -= i->second->height(); | 
| Chris@0 | 825 	    } | 
| Chris@0 | 826 	} else { | 
| Chris@0 | 827 	    if (i->second->isVisible()) { | 
| Chris@0 | 828 		ph -= i->second->height(); | 
| Chris@0 | 829 	    } | 
| Chris@0 | 830 	} | 
| Chris@0 | 831     } | 
| Chris@0 | 832 } | 
| Chris@0 | 833 /*!!! | 
| Chris@0 | 834 void | 
| Chris@0 | 835 View::identifyLocalFeatures(bool on, int x, int y) | 
| Chris@0 | 836 { | 
| Chris@0 | 837     for (LayerList::const_iterator i = m_layers.end(); i != m_layers.begin(); ) { | 
| Chris@0 | 838 	--i; | 
| Chris@0 | 839 #ifdef DEBUG_VIEW_WIDGET_PAINT | 
| Chris@0 | 840 	std::cerr << "View::identifyLocalFeatures: calling on " << *i << std::endl; | 
| Chris@0 | 841 #endif | 
| Chris@0 | 842 	if ((*i)->identifyLocalFeatures(on, x, y)) break; | 
| Chris@0 | 843     } | 
| Chris@0 | 844 } | 
| Chris@0 | 845 */ | 
| Chris@0 | 846 | 
| Chris@0 | 847 void | 
| Chris@0 | 848 View::paintEvent(QPaintEvent *e) | 
| Chris@0 | 849 { | 
| Chris@0 | 850 //    Profiler prof("View::paintEvent", true); | 
| Chris@0 | 851 //    std::cerr << "View::paintEvent" << std::endl; | 
| Chris@0 | 852 | 
| Chris@0 | 853     if (m_layers.empty()) { | 
| Chris@0 | 854 	QFrame::paintEvent(e); | 
| Chris@0 | 855 	return; | 
| Chris@0 | 856     } | 
| Chris@0 | 857 | 
| Chris@0 | 858     if (m_newModel) { | 
| Chris@0 | 859 	m_newModel = false; | 
| Chris@0 | 860     } | 
| Chris@0 | 861 | 
| Chris@0 | 862     // ensure our constraints are met | 
| Chris@0 | 863     m_zoomLevel = getZoomConstraintBlockSize(m_zoomLevel, | 
| Chris@0 | 864 					     ZoomConstraint::RoundUp); | 
| Chris@0 | 865 | 
| Chris@0 | 866     QPainter paint; | 
| Chris@0 | 867     bool repaintCache = false; | 
| Chris@0 | 868     bool paintedCacheRect = false; | 
| Chris@0 | 869 | 
| Chris@0 | 870     QRect cacheRect(rect()); | 
| Chris@0 | 871 | 
| Chris@0 | 872     if (e) { | 
| Chris@0 | 873 	cacheRect &= e->rect(); | 
| Chris@0 | 874 #ifdef DEBUG_VIEW_WIDGET_PAINT | 
| Chris@0 | 875 	std::cerr << "paint rect " << cacheRect.width() << "x" << cacheRect.height() | 
| Chris@0 | 876 		  << ", my rect " << width() << "x" << height() << std::endl; | 
| Chris@0 | 877 #endif | 
| Chris@0 | 878     } | 
| Chris@0 | 879 | 
| Chris@0 | 880     QRect nonCacheRect(cacheRect); | 
| Chris@0 | 881 | 
| Chris@0 | 882     // If not all layers are scrollable, but some of the back layers | 
| Chris@0 | 883     // are, we should store only those in the cache | 
| Chris@0 | 884 | 
| Chris@0 | 885     bool layersChanged = false; | 
| Chris@0 | 886     LayerList scrollables = getScrollableBackLayers(layersChanged); | 
| Chris@0 | 887     LayerList nonScrollables = getNonScrollableFrontLayers(layersChanged); | 
| Chris@9 | 888     bool selectionCacheable = nonScrollables.empty(); | 
| Chris@9 | 889     bool haveSelections = m_manager && !m_manager->getSelections().empty(); | 
| Chris@9 | 890     bool selectionDrawn = false; | 
| Chris@0 | 891 | 
| Chris@10 | 892     if (!selectionCacheable) { | 
| Chris@10 | 893 	selectionCacheable = true; | 
| Chris@10 | 894 	for (LayerList::const_iterator i = nonScrollables.begin(); | 
| Chris@10 | 895 	     i != nonScrollables.end(); ++i) { | 
| Chris@10 | 896 	    if ((*i)->isLayerOpaque()) { | 
| Chris@10 | 897 		selectionCacheable = false; | 
| Chris@10 | 898 		break; | 
| Chris@10 | 899 	    } | 
| Chris@10 | 900 	} | 
| Chris@10 | 901     } | 
| Chris@10 | 902 | 
| Chris@0 | 903 #ifdef DEBUG_VIEW_WIDGET_PAINT | 
| Chris@0 | 904     std::cerr << "View(" << this << ")::paintEvent: have " << scrollables.size() | 
| Chris@0 | 905 	      << " scrollable back layers and " << nonScrollables.size() | 
| Chris@0 | 906 	      << " non-scrollable front layers" << std::endl; | 
| Chris@9 | 907     std::cerr << "haveSelections " << haveSelections << ", selectionCacheable " | 
| Chris@9 | 908 	      << selectionCacheable << ", m_selectionCached " << m_selectionCached << std::endl; | 
| Chris@0 | 909 #endif | 
| Chris@0 | 910 | 
| Chris@9 | 911     if (layersChanged || scrollables.empty() || | 
| Chris@9 | 912 	(haveSelections && (selectionCacheable != m_selectionCached))) { | 
| Chris@0 | 913 	delete m_cache; | 
| Chris@0 | 914 	m_cache = 0; | 
| Chris@9 | 915 	m_selectionCached = false; | 
| Chris@0 | 916     } | 
| Chris@0 | 917 | 
| Chris@0 | 918     if (!scrollables.empty()) { | 
| Chris@0 | 919 	if (!m_cache || | 
| Chris@0 | 920 	    m_cacheZoomLevel != m_zoomLevel || | 
| Chris@0 | 921 	    width() != m_cache->width() || | 
| Chris@0 | 922 	    height() != m_cache->height()) { | 
| Chris@0 | 923 | 
| Chris@0 | 924 	    // cache is not valid | 
| Chris@0 | 925 | 
| Chris@0 | 926 	    if (cacheRect.width() < width()/10) { | 
| Chris@0 | 927 #ifdef DEBUG_VIEW_WIDGET_PAINT | 
| Chris@0 | 928 		std::cerr << "View(" << this << ")::paintEvent: small repaint, not bothering to recreate cache" << std::endl; | 
| Chris@0 | 929 #endif | 
| Chris@0 | 930 	    } else { | 
| Chris@0 | 931 		delete m_cache; | 
| Chris@0 | 932 		m_cache = new QPixmap(width(), height()); | 
| Chris@0 | 933 #ifdef DEBUG_VIEW_WIDGET_PAINT | 
| Chris@0 | 934 		std::cerr << "View(" << this << ")::paintEvent: recreated cache" << std::endl; | 
| Chris@0 | 935 #endif | 
| Chris@0 | 936 		cacheRect = rect(); | 
| Chris@0 | 937 		repaintCache = true; | 
| Chris@0 | 938 	    } | 
| Chris@0 | 939 | 
| Chris@0 | 940 	} else if (m_cacheCentreFrame != m_centreFrame) { | 
| Chris@0 | 941 | 
| Chris@15 | 942 	    long dx = | 
| Chris@15 | 943 		getXForFrame(m_cacheCentreFrame) - | 
| Chris@15 | 944 		getXForFrame(m_centreFrame); | 
| Chris@0 | 945 | 
| Chris@0 | 946 	    if (dx > -width() && dx < width()) { | 
| Chris@0 | 947 #if defined(Q_WS_WIN32) || defined(Q_WS_MAC) | 
| Chris@0 | 948 		// Copying a pixmap to itself doesn't work properly on Windows | 
| Chris@0 | 949 		// or Mac (it only works when moving in one direction) | 
| Chris@0 | 950 		static QPixmap *tmpPixmap = 0; | 
| Chris@0 | 951 		if (!tmpPixmap || | 
| Chris@0 | 952 		    tmpPixmap->width() != width() || | 
| Chris@0 | 953 		    tmpPixmap->height() != height()) { | 
| Chris@0 | 954 		    delete tmpPixmap; | 
| Chris@0 | 955 		    tmpPixmap = new QPixmap(width(), height()); | 
| Chris@0 | 956 		} | 
| Chris@0 | 957 		paint.begin(tmpPixmap); | 
| Chris@0 | 958 		paint.drawPixmap(0, 0, *m_cache); | 
| Chris@0 | 959 		paint.end(); | 
| Chris@0 | 960 		paint.begin(m_cache); | 
| Chris@0 | 961 		paint.drawPixmap(dx, 0, *tmpPixmap); | 
| Chris@0 | 962 		paint.end(); | 
| Chris@0 | 963 #else | 
| Chris@0 | 964 		// But it seems to be fine on X11 | 
| Chris@0 | 965 		paint.begin(m_cache); | 
| Chris@0 | 966 		paint.drawPixmap(dx, 0, *m_cache); | 
| Chris@0 | 967 		paint.end(); | 
| Chris@0 | 968 #endif | 
| Chris@0 | 969 | 
| Chris@0 | 970 		if (dx < 0) { | 
| Chris@0 | 971 		    cacheRect = QRect(width() + dx, 0, -dx, height()); | 
| Chris@0 | 972 		} else { | 
| Chris@0 | 973 		    cacheRect = QRect(0, 0, dx, height()); | 
| Chris@0 | 974 		} | 
| Chris@0 | 975 #ifdef DEBUG_VIEW_WIDGET_PAINT | 
| Chris@0 | 976 		std::cerr << "View(" << this << ")::paintEvent: scrolled cache by " << dx << std::endl; | 
| Chris@0 | 977 #endif | 
| Chris@0 | 978 	    } else { | 
| Chris@0 | 979 		cacheRect = rect(); | 
| Chris@0 | 980 #ifdef DEBUG_VIEW_WIDGET_PAINT | 
| Chris@0 | 981 		std::cerr << "View(" << this << ")::paintEvent: scrolling too far" << std::endl; | 
| Chris@0 | 982 #endif | 
| Chris@0 | 983 	    } | 
| Chris@0 | 984 	    repaintCache = true; | 
| Chris@0 | 985 | 
| Chris@0 | 986 	} else { | 
| Chris@0 | 987 #ifdef DEBUG_VIEW_WIDGET_PAINT | 
| Chris@0 | 988 	    std::cerr << "View(" << this << ")::paintEvent: cache is good" << std::endl; | 
| Chris@0 | 989 #endif | 
| Chris@0 | 990 	    paint.begin(this); | 
| Chris@0 | 991 	    paint.drawPixmap(cacheRect, *m_cache, cacheRect); | 
| Chris@0 | 992 	    paint.end(); | 
| Chris@0 | 993 	    QFrame::paintEvent(e); | 
| Chris@0 | 994 	    paintedCacheRect = true; | 
| Chris@0 | 995 	} | 
| Chris@0 | 996 | 
| Chris@0 | 997 	m_cacheCentreFrame = m_centreFrame; | 
| Chris@0 | 998 	m_cacheZoomLevel = m_zoomLevel; | 
| Chris@0 | 999     } | 
| Chris@0 | 1000 | 
| Chris@0 | 1001 #ifdef DEBUG_VIEW_WIDGET_PAINT | 
| Chris@0 | 1002 //    std::cerr << "View(" << this << ")::paintEvent: cacheRect " << cacheRect << ", nonCacheRect " << (nonCacheRect | cacheRect) << ", repaintCache " << repaintCache << ", paintedCacheRect " << paintedCacheRect << std::endl; | 
| Chris@0 | 1003 #endif | 
| Chris@0 | 1004 | 
| Chris@0 | 1005     // Scrollable (cacheable) items first | 
| Chris@0 | 1006 | 
| Chris@0 | 1007     if (!paintedCacheRect) { | 
| Chris@0 | 1008 | 
| Chris@0 | 1009 	if (repaintCache) paint.begin(m_cache); | 
| Chris@0 | 1010 	else paint.begin(this); | 
| Chris@0 | 1011 | 
| Chris@0 | 1012 	paint.setClipRect(cacheRect); | 
| Chris@0 | 1013 | 
| Chris@0 | 1014 	if (hasLightBackground()) { | 
| Chris@0 | 1015 	    paint.setPen(Qt::white); | 
| Chris@0 | 1016 	    paint.setBrush(Qt::white); | 
| Chris@0 | 1017 	} else { | 
| Chris@0 | 1018 	    paint.setPen(Qt::black); | 
| Chris@0 | 1019 	    paint.setBrush(Qt::black); | 
| Chris@0 | 1020 	} | 
| Chris@0 | 1021 	paint.drawRect(cacheRect); | 
| Chris@0 | 1022 | 
| Chris@0 | 1023 	paint.setPen(Qt::black); | 
| Chris@0 | 1024 	paint.setBrush(Qt::NoBrush); | 
| Chris@0 | 1025 | 
| Chris@0 | 1026 	for (LayerList::iterator i = scrollables.begin(); i != scrollables.end(); ++i) { | 
| Chris@8 | 1027 	    paint.setRenderHint(QPainter::Antialiasing, false); | 
| Chris@8 | 1028 	    paint.save(); | 
| Chris@0 | 1029 	    (*i)->paint(paint, cacheRect); | 
| Chris@8 | 1030 	    paint.restore(); | 
| Chris@0 | 1031 	} | 
| Chris@9 | 1032 | 
| Chris@9 | 1033 	if (haveSelections && selectionCacheable) { | 
| Chris@9 | 1034 	    drawSelections(paint); | 
| Chris@9 | 1035 	    m_selectionCached = repaintCache; | 
| Chris@9 | 1036 	    selectionDrawn = true; | 
| Chris@9 | 1037 	} | 
| Chris@0 | 1038 | 
| Chris@0 | 1039 	paint.end(); | 
| Chris@0 | 1040 | 
| Chris@0 | 1041 	if (repaintCache) { | 
| Chris@0 | 1042 	    cacheRect |= (e ? e->rect() : rect()); | 
| Chris@0 | 1043 	    paint.begin(this); | 
| Chris@0 | 1044 	    paint.drawPixmap(cacheRect, *m_cache, cacheRect); | 
| Chris@0 | 1045 	    paint.end(); | 
| Chris@0 | 1046 	} | 
| Chris@0 | 1047     } | 
| Chris@0 | 1048 | 
| Chris@0 | 1049     // Now non-cacheable items.  We always need to redraw the | 
| Chris@0 | 1050     // non-cacheable items across at least the area we drew of the | 
| Chris@0 | 1051     // cacheable items. | 
| Chris@0 | 1052 | 
| Chris@0 | 1053     nonCacheRect |= cacheRect; | 
| Chris@0 | 1054 | 
| Chris@0 | 1055     paint.begin(this); | 
| Chris@0 | 1056     paint.setClipRect(nonCacheRect); | 
| Chris@0 | 1057 | 
| Chris@0 | 1058     if (scrollables.empty()) { | 
| Chris@0 | 1059 	if (hasLightBackground()) { | 
| Chris@0 | 1060 	    paint.setPen(Qt::white); | 
| Chris@0 | 1061 	    paint.setBrush(Qt::white); | 
| Chris@0 | 1062 	} else { | 
| Chris@0 | 1063 	    paint.setPen(Qt::black); | 
| Chris@0 | 1064 	    paint.setBrush(Qt::black); | 
| Chris@0 | 1065 	} | 
| Chris@0 | 1066 	paint.drawRect(nonCacheRect); | 
| Chris@0 | 1067     } | 
| Chris@0 | 1068 | 
| Chris@0 | 1069     paint.setPen(Qt::black); | 
| Chris@0 | 1070     paint.setBrush(Qt::NoBrush); | 
| Chris@0 | 1071 | 
| Chris@0 | 1072     for (LayerList::iterator i = nonScrollables.begin(); i != nonScrollables.end(); ++i) { | 
| Chris@0 | 1073 	(*i)->paint(paint, nonCacheRect); | 
| Chris@0 | 1074     } | 
| Chris@0 | 1075 | 
| Chris@9 | 1076     paint.end(); | 
| Chris@8 | 1077 | 
| Chris@9 | 1078     paint.begin(this); | 
| Chris@9 | 1079     if (e) paint.setClipRect(e->rect()); | 
| Chris@9 | 1080     if (!m_selectionCached) { | 
| Chris@9 | 1081 	drawSelections(paint); | 
| Chris@8 | 1082     } | 
| Chris@0 | 1083     paint.end(); | 
| Chris@0 | 1084 | 
| Chris@0 | 1085     if (m_followPlay != PlaybackScrollContinuous) { | 
| Chris@0 | 1086 | 
| Chris@0 | 1087 	paint.begin(this); | 
| Chris@0 | 1088 | 
| Chris@0 | 1089 	if (long(m_playPointerFrame) > getStartFrame() && | 
| Chris@0 | 1090 	    m_playPointerFrame < getEndFrame()) { | 
| Chris@0 | 1091 | 
| Chris@15 | 1092 	    int playx = getXForFrame(m_playPointerFrame); | 
| Chris@0 | 1093 | 
| Chris@0 | 1094 	    paint.setPen(Qt::black); | 
| Chris@0 | 1095 	    paint.drawLine(playx - 1, 0, playx - 1, height() - 1); | 
| Chris@0 | 1096 	    paint.drawLine(playx + 1, 0, playx + 1, height() - 1); | 
| Chris@0 | 1097 	    paint.drawPoint(playx, 0); | 
| Chris@0 | 1098 	    paint.drawPoint(playx, height() - 1); | 
| Chris@0 | 1099 	    paint.setPen(Qt::white); | 
| Chris@0 | 1100 	    paint.drawLine(playx, 1, playx, height() - 2); | 
| Chris@0 | 1101 	} | 
| Chris@0 | 1102 | 
| Chris@0 | 1103 	paint.end(); | 
| Chris@0 | 1104     } | 
| Chris@0 | 1105 | 
| Chris@0 | 1106     QFrame::paintEvent(e); | 
| Chris@0 | 1107 } | 
| Chris@0 | 1108 | 
| Chris@9 | 1109 void | 
| Chris@9 | 1110 View::drawSelections(QPainter &paint) | 
| Chris@9 | 1111 { | 
| Chris@9 | 1112     ViewManager::SelectionList selections; | 
| Chris@9 | 1113 | 
| Chris@9 | 1114     if (m_manager) { | 
| Chris@9 | 1115 	selections = m_manager->getSelections(); | 
| Chris@9 | 1116 	if (m_manager->haveInProgressSelection()) { | 
| Chris@9 | 1117 	    bool exclusive; | 
| Chris@9 | 1118 	    Selection inProgressSelection = | 
| Chris@9 | 1119 		m_manager->getInProgressSelection(exclusive); | 
| Chris@9 | 1120 	    if (exclusive) selections.clear(); | 
| Chris@9 | 1121 	    selections.insert(inProgressSelection); | 
| Chris@9 | 1122 	} | 
| Chris@9 | 1123     } | 
| Chris@9 | 1124 | 
| Chris@9 | 1125     paint.save(); | 
| Chris@9 | 1126     paint.setPen(QColor(150, 150, 255)); | 
| Chris@9 | 1127     paint.setBrush(QColor(150, 150, 255, 80)); | 
| Chris@9 | 1128 | 
| Chris@10 | 1129     int sampleRate = getModelsSampleRate(); | 
| Chris@10 | 1130 | 
| Chris@10 | 1131     const QFontMetrics &metrics = paint.fontMetrics(); | 
| Chris@10 | 1132 | 
| Chris@9 | 1133     for (ViewManager::SelectionList::iterator i = selections.begin(); | 
| Chris@9 | 1134 	 i != selections.end(); ++i) { | 
| Chris@9 | 1135 | 
| Chris@15 | 1136 	int p0 = getXForFrame(i->getStartFrame()); | 
| Chris@15 | 1137 	int p1 = getXForFrame(i->getEndFrame()); | 
| Chris@9 | 1138 | 
| Chris@10 | 1139 	if (p1 < 0 || p0 > width()) continue; | 
| Chris@9 | 1140 | 
| Chris@9 | 1141 #ifdef DEBUG_VIEW_WIDGET_PAINT | 
| Chris@9 | 1142 	std::cerr << "View::drawSelections: " << p0 << ",-1 [" << (p1-p0) << "x" << (height()+1) << "]" << std::endl; | 
| Chris@9 | 1143 #endif | 
| Chris@9 | 1144 | 
| Chris@9 | 1145 	paint.drawRect(p0, -1, p1 - p0, height() + 1); | 
| Chris@10 | 1146 | 
| Chris@10 | 1147 	if (sampleRate && shouldLabelSelections()) { | 
| Chris@10 | 1148 | 
| Chris@10 | 1149 	    QString startText = QString("%1 / %2") | 
| Chris@10 | 1150 		.arg(QString::fromStdString | 
| Chris@10 | 1151 		     (RealTime::frame2RealTime | 
| Chris@10 | 1152 		      (i->getStartFrame(), sampleRate).toText(true))) | 
| Chris@10 | 1153 		.arg(i->getStartFrame()); | 
| Chris@10 | 1154 | 
| Chris@10 | 1155 	    QString endText = QString(" %1 / %2") | 
| Chris@10 | 1156 		.arg(QString::fromStdString | 
| Chris@10 | 1157 		     (RealTime::frame2RealTime | 
| Chris@10 | 1158 		      (i->getEndFrame(), sampleRate).toText(true))) | 
| Chris@10 | 1159 		.arg(i->getEndFrame()); | 
| Chris@10 | 1160 | 
| Chris@10 | 1161 	    QString durationText = QString("(%1 / %2) ") | 
| Chris@10 | 1162 		.arg(QString::fromStdString | 
| Chris@10 | 1163 		     (RealTime::frame2RealTime | 
| Chris@10 | 1164 		      (i->getEndFrame() - i->getStartFrame(), sampleRate) | 
| Chris@10 | 1165 		      .toText(true))) | 
| Chris@10 | 1166 		.arg(i->getEndFrame() - i->getStartFrame()); | 
| Chris@10 | 1167 | 
| Chris@10 | 1168 	    int sw = metrics.width(startText), | 
| Chris@10 | 1169 		ew = metrics.width(endText), | 
| Chris@10 | 1170 		dw = metrics.width(durationText); | 
| Chris@10 | 1171 | 
| Chris@10 | 1172 	    int sy = metrics.ascent() + metrics.height() + 4; | 
| Chris@10 | 1173 	    int ey = sy; | 
| Chris@10 | 1174 	    int dy = sy + metrics.height(); | 
| Chris@10 | 1175 | 
| Chris@10 | 1176 	    int sx = p0 + 2; | 
| Chris@10 | 1177 	    int ex = sx; | 
| Chris@10 | 1178 	    int dx = sx; | 
| Chris@10 | 1179 | 
| Chris@10 | 1180 	    if (sw + ew > (p1 - p0)) { | 
| Chris@10 | 1181 		ey += metrics.height(); | 
| Chris@10 | 1182 		dy += metrics.height(); | 
| Chris@10 | 1183 	    } | 
| Chris@10 | 1184 | 
| Chris@10 | 1185 	    if (ew < (p1 - p0)) { | 
| Chris@10 | 1186 		ex = p1 - 2 - ew; | 
| Chris@10 | 1187 	    } | 
| Chris@10 | 1188 | 
| Chris@10 | 1189 	    if (dw < (p1 - p0)) { | 
| Chris@10 | 1190 		dx = p1 - 2 - dw; | 
| Chris@10 | 1191 	    } | 
| Chris@10 | 1192 | 
| Chris@10 | 1193 	    paint.drawText(sx, sy, startText); | 
| Chris@10 | 1194 	    paint.drawText(ex, ey, endText); | 
| Chris@10 | 1195 	    paint.drawText(dx, dy, durationText); | 
| Chris@10 | 1196 	} | 
| Chris@9 | 1197     } | 
| Chris@9 | 1198 | 
| Chris@9 | 1199     paint.restore(); | 
| Chris@9 | 1200 } | 
| Chris@9 | 1201 | 
| Chris@3 | 1202 QString | 
| Chris@3 | 1203 View::toXmlString(QString indent, QString extraAttributes) const | 
| Chris@3 | 1204 { | 
| Chris@3 | 1205     QString s; | 
| Chris@3 | 1206 | 
| Chris@3 | 1207     s += indent; | 
| Chris@3 | 1208 | 
| Chris@3 | 1209     s += QString("<view " | 
| Chris@3 | 1210 		 "centre=\"%1\" " | 
| Chris@3 | 1211 		 "zoom=\"%2\" " | 
| Chris@3 | 1212 		 "followPan=\"%3\" " | 
| Chris@3 | 1213 		 "followZoom=\"%4\" " | 
| Chris@3 | 1214 		 "tracking=\"%5\" " | 
| Chris@3 | 1215 		 "light=\"%6\" %7>\n") | 
| Chris@3 | 1216 	.arg(m_centreFrame) | 
| Chris@3 | 1217 	.arg(m_zoomLevel) | 
| Chris@3 | 1218 	.arg(m_followPan) | 
| Chris@3 | 1219 	.arg(m_followZoom) | 
| Chris@3 | 1220 	.arg(m_followPlay == PlaybackScrollContinuous ? "scroll" : | 
| Chris@3 | 1221 	     m_followPlay == PlaybackScrollPage ? "page" : "ignore") | 
| Chris@3 | 1222 	.arg(m_lightBackground) | 
| Chris@3 | 1223 	.arg(extraAttributes); | 
| Chris@3 | 1224 | 
| Chris@3 | 1225     for (size_t i = 0; i < m_layers.size(); ++i) { | 
| Chris@3 | 1226 	s += m_layers[i]->toXmlString(indent + "  "); | 
| Chris@3 | 1227     } | 
| Chris@3 | 1228 | 
| Chris@4 | 1229     s += indent + "</view>\n"; | 
| Chris@3 | 1230 | 
| Chris@3 | 1231     return s; | 
| Chris@3 | 1232 } | 
| Chris@3 | 1233 | 
| Chris@3 | 1234 | 
| Chris@0 | 1235 #ifdef INCLUDE_MOCFILES | 
| Chris@0 | 1236 #include "View.moc.cpp" | 
| Chris@0 | 1237 #endif | 
| Chris@0 | 1238 |