annotate base/View.cpp @ 5:31c4ed2d5da6

* Hook up SV file i/o. You can now save and load sessions. Some problems -- gain is not reloaded correctly for waveforms, reloaded panes are not properly reconnected to the panner, and no doubt plenty of others.
author Chris Cannam
date Tue, 17 Jan 2006 17:45:55 +0000
parents 149bb02a41ba
children 214054a0d8b8
rev   line source
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/ViewManager.h"
Chris@0 12 #include "base/Layer.h"
Chris@0 13 #include "base/Model.h"
Chris@0 14 #include "base/ZoomConstraint.h"
Chris@0 15 #include "base/Profiler.h"
Chris@0 16
Chris@0 17 #include "layer/TimeRulerLayer.h" //!!! damn, shouldn't be including that here
Chris@0 18
Chris@0 19 #include <QPainter>
Chris@0 20 #include <QPaintEvent>
Chris@0 21 #include <QRect>
Chris@0 22 #include <QApplication>
Chris@0 23
Chris@0 24 #include <iostream>
Chris@0 25
Chris@0 26 //#define DEBUG_VIEW_WIDGET_PAINT 1
Chris@0 27
Chris@0 28 using std::cerr;
Chris@0 29 using std::endl;
Chris@0 30
Chris@0 31 View::View(QWidget *w, bool showProgress) :
Chris@0 32 QFrame(w),
Chris@0 33 m_centreFrame(0),
Chris@0 34 m_zoomLevel(1024),
Chris@0 35 m_newModel(true),
Chris@0 36 m_followPan(true),
Chris@0 37 m_followZoom(true),
Chris@0 38 m_followPlay(PlaybackScrollPage),
Chris@0 39 m_lightBackground(true),
Chris@0 40 m_showProgress(showProgress),
Chris@0 41 m_cache(0),
Chris@0 42 m_cacheCentreFrame(0),
Chris@0 43 m_cacheZoomLevel(1024),
Chris@0 44 m_deleting(false),
Chris@0 45 m_manager(0)
Chris@0 46 {
Chris@0 47 // QWidget::setAttribute(Qt::WA_PaintOnScreen);
Chris@0 48 }
Chris@0 49
Chris@0 50 View::~View()
Chris@0 51 {
Chris@0 52 m_deleting = true;
Chris@0 53
Chris@0 54 for (LayerList::iterator i = m_layers.begin(); i != m_layers.end(); ++i) {
Chris@0 55 delete *i;
Chris@0 56 }
Chris@0 57 }
Chris@0 58
Chris@0 59 PropertyContainer::PropertyList
Chris@0 60 View::getProperties() const
Chris@0 61 {
Chris@0 62 PropertyList list;
Chris@0 63 list.push_back(tr("Global Scroll"));
Chris@0 64 list.push_back(tr("Global Zoom"));
Chris@0 65 list.push_back(tr("Follow Playback"));
Chris@0 66 return list;
Chris@0 67 }
Chris@0 68
Chris@0 69 PropertyContainer::PropertyType
Chris@0 70 View::getPropertyType(const PropertyName &name) const
Chris@0 71 {
Chris@0 72 if (name == tr("Global Scroll")) return ToggleProperty;
Chris@0 73 if (name == tr("Global Zoom")) return ToggleProperty;
Chris@0 74 if (name == tr("Follow Playback")) return ValueProperty;
Chris@0 75 return InvalidProperty;
Chris@0 76 }
Chris@0 77
Chris@0 78 int
Chris@0 79 View::getPropertyRangeAndValue(const PropertyName &name,
Chris@0 80 int *min, int *max) const
Chris@0 81 {
Chris@0 82 if (name == tr("Global Scroll")) return m_followPan;
Chris@0 83 if (name == tr("Global Zoom")) return m_followZoom;
Chris@0 84 if (name == tr("Follow Playback")) { *min = 0; *max = 2; return int(m_followPlay); }
Chris@0 85 return PropertyContainer::getPropertyRangeAndValue(name, min, max);
Chris@0 86 }
Chris@0 87
Chris@0 88 QString
Chris@0 89 View::getPropertyValueLabel(const PropertyName &name,
Chris@0 90 int value) const
Chris@0 91 {
Chris@0 92 if (name == tr("Follow Playback")) {
Chris@0 93 switch (value) {
Chris@0 94 default:
Chris@0 95 case 0: return tr("Scroll");
Chris@0 96 case 1: return tr("Page");
Chris@0 97 case 2: return tr("Off");
Chris@0 98 }
Chris@0 99 }
Chris@0 100 return tr("<unknown>");
Chris@0 101 }
Chris@0 102
Chris@0 103 void
Chris@0 104 View::setProperty(const PropertyName &name, int value)
Chris@0 105 {
Chris@0 106 if (name == tr("Global Scroll")) {
Chris@0 107 setFollowGlobalPan(value != 0);
Chris@0 108 } else if (name == tr("Global Zoom")) {
Chris@0 109 setFollowGlobalZoom(value != 0);
Chris@0 110 } else if (name == tr("Follow Playback")) {
Chris@0 111 switch (value) {
Chris@0 112 default:
Chris@0 113 case 0: setPlaybackFollow(PlaybackScrollContinuous); break;
Chris@0 114 case 1: setPlaybackFollow(PlaybackScrollPage); break;
Chris@0 115 case 2: setPlaybackFollow(PlaybackIgnore); break;
Chris@0 116 }
Chris@0 117 }
Chris@0 118 }
Chris@0 119
Chris@0 120 size_t
Chris@0 121 View::getPropertyContainerCount() const
Chris@0 122 {
Chris@0 123 return m_layers.size() + 1; // the 1 is for me
Chris@0 124 }
Chris@0 125
Chris@0 126 const PropertyContainer *
Chris@0 127 View::getPropertyContainer(size_t i) const
Chris@0 128 {
Chris@0 129 return (const PropertyContainer *)(((View *)this)->
Chris@0 130 getPropertyContainer(i));
Chris@0 131 }
Chris@0 132
Chris@0 133 PropertyContainer *
Chris@0 134 View::getPropertyContainer(size_t i)
Chris@0 135 {
Chris@0 136 if (i == 0) return this;
Chris@0 137 return m_layers[i-1];
Chris@0 138 }
Chris@0 139
Chris@0 140 void
Chris@0 141 View::propertyContainerSelected(PropertyContainer *pc)
Chris@0 142 {
Chris@0 143 if (pc == this) return;
Chris@0 144
Chris@0 145 delete m_cache;
Chris@0 146 m_cache = 0;
Chris@0 147
Chris@0 148 Layer *selectedLayer = 0;
Chris@0 149
Chris@0 150 for (LayerList::iterator i = m_layers.begin(); i != m_layers.end(); ++i) {
Chris@0 151 if (*i == pc) {
Chris@0 152 selectedLayer = *i;
Chris@0 153 m_layers.erase(i);
Chris@0 154 break;
Chris@0 155 }
Chris@0 156 }
Chris@0 157
Chris@0 158 if (selectedLayer) {
Chris@0 159 m_layers.push_back(selectedLayer);
Chris@0 160 update();
Chris@0 161 }
Chris@0 162 }
Chris@0 163
Chris@0 164 long
Chris@0 165 View::getStartFrame() const
Chris@0 166 {
Chris@0 167 size_t w2 = (width() / 2) * m_zoomLevel;
Chris@0 168 size_t frame = m_centreFrame;
Chris@0 169 if (frame >= w2) {
Chris@0 170 frame -= w2;
Chris@0 171 return (frame / m_zoomLevel * m_zoomLevel);
Chris@0 172 } else {
Chris@0 173 frame = w2 - frame;
Chris@0 174 frame = frame / m_zoomLevel * m_zoomLevel;
Chris@0 175 return -(long)frame - m_zoomLevel;
Chris@0 176 }
Chris@0 177 }
Chris@0 178
Chris@0 179 size_t
Chris@0 180 View::getEndFrame() const
Chris@0 181 {
Chris@0 182 return getStartFrame() + (width() * m_zoomLevel) - 1;
Chris@0 183 }
Chris@0 184
Chris@0 185 void
Chris@0 186 View::setStartFrame(long f)
Chris@0 187 {
Chris@0 188 setCentreFrame(f + m_zoomLevel * (width() / 2));
Chris@0 189 }
Chris@0 190
Chris@0 191 void
Chris@0 192 View::setCentreFrame(size_t f, bool e)
Chris@0 193 {
Chris@0 194 if (m_centreFrame != f) {
Chris@0 195
Chris@0 196 int formerPixel = m_centreFrame / m_zoomLevel;
Chris@0 197
Chris@0 198 m_centreFrame = f;
Chris@0 199
Chris@0 200 int newPixel = m_centreFrame / m_zoomLevel;
Chris@0 201
Chris@0 202 if (newPixel != formerPixel) {
Chris@0 203
Chris@0 204 #ifdef DEBUG_VIEW_WIDGET_PAINT
Chris@0 205 std::cout << "View(" << this << ")::setCentreFrame: newPixel " << newPixel << ", formerPixel " << formerPixel << std::endl;
Chris@0 206 #endif
Chris@0 207 update();
Chris@0 208 }
Chris@0 209
Chris@0 210 if (e) emit centreFrameChanged(this, f, m_followPan);
Chris@0 211 }
Chris@0 212 }
Chris@0 213
Chris@0 214 void
Chris@0 215 View::setZoomLevel(size_t z)
Chris@0 216 {
Chris@0 217 if (m_zoomLevel != int(z)) {
Chris@0 218 m_zoomLevel = z;
Chris@0 219 emit zoomLevelChanged(this, z, m_followZoom);
Chris@0 220 update();
Chris@0 221 }
Chris@0 222 }
Chris@0 223
Chris@0 224 void
Chris@0 225 View::addLayer(Layer *layer)
Chris@0 226 {
Chris@0 227 delete m_cache;
Chris@0 228 m_cache = 0;
Chris@0 229
Chris@0 230 m_layers.push_back(layer);
Chris@0 231
Chris@0 232 m_progressBars[layer] = new LayerProgressBar(this);
Chris@0 233 m_progressBars[layer]->setMinimum(0);
Chris@0 234 m_progressBars[layer]->setMaximum(100);
Chris@0 235 m_progressBars[layer]->setMinimumWidth(80);
Chris@0 236 m_progressBars[layer]->hide();
Chris@0 237
Chris@0 238 connect(layer, SIGNAL(layerParametersChanged()),
Chris@0 239 this, SLOT(layerParametersChanged()));
Chris@0 240 connect(layer, SIGNAL(layerNameChanged()),
Chris@0 241 this, SLOT(layerNameChanged()));
Chris@0 242 connect(layer, SIGNAL(modelChanged()),
Chris@0 243 this, SLOT(modelChanged()));
Chris@0 244 connect(layer, SIGNAL(modelCompletionChanged()),
Chris@0 245 this, SLOT(modelCompletionChanged()));
Chris@0 246 connect(layer, SIGNAL(modelChanged(size_t, size_t)),
Chris@0 247 this, SLOT(modelChanged(size_t, size_t)));
Chris@0 248 connect(layer, SIGNAL(modelReplaced()),
Chris@0 249 this, SLOT(modelReplaced()));
Chris@0 250
Chris@0 251 m_newModel = true;
Chris@0 252 update();
Chris@0 253
Chris@0 254 emit propertyContainerAdded(layer);
Chris@0 255 }
Chris@0 256
Chris@0 257 void
Chris@0 258 View::removeLayer(Layer *layer)
Chris@0 259 {
Chris@0 260 if (m_deleting) {
Chris@0 261 return;
Chris@0 262 }
Chris@0 263
Chris@0 264 delete m_cache;
Chris@0 265 m_cache = 0;
Chris@0 266
Chris@0 267 for (LayerList::iterator i = m_layers.begin(); i != m_layers.end(); ++i) {
Chris@0 268 if (*i == layer) {
Chris@0 269 m_layers.erase(i);
Chris@0 270 if (m_progressBars.find(layer) != m_progressBars.end()) {
Chris@0 271 delete m_progressBars[layer];
Chris@0 272 m_progressBars.erase(layer);
Chris@0 273 }
Chris@0 274 break;
Chris@0 275 }
Chris@0 276 }
Chris@0 277
Chris@0 278 update();
Chris@0 279
Chris@0 280 emit propertyContainerRemoved(layer);
Chris@0 281 }
Chris@0 282
Chris@0 283 void
Chris@0 284 View::setViewManager(ViewManager *manager)
Chris@0 285 {
Chris@0 286 if (m_manager) {
Chris@0 287 m_manager->disconnect(this, SLOT(viewManagerCentreFrameChanged(void *, unsigned long, bool)));
Chris@0 288 m_manager->disconnect(this, SLOT(viewManagerZoomLevelChanged(void *, unsigned long, bool)));
Chris@0 289 disconnect(m_manager, SIGNAL(centreFrameChanged(void *, unsigned long, bool)));
Chris@0 290 disconnect(m_manager, SIGNAL(zoomLevelChanged(void *, unsigned long, bool)));
Chris@0 291 }
Chris@0 292
Chris@0 293 m_manager = manager;
Chris@0 294 if (m_followPan) setCentreFrame(m_manager->getGlobalCentreFrame(), false);
Chris@0 295 if (m_followZoom) setZoomLevel(m_manager->getGlobalZoom());
Chris@0 296
Chris@0 297 connect(m_manager, SIGNAL(centreFrameChanged(void *, unsigned long, bool)),
Chris@0 298 this, SLOT(viewManagerCentreFrameChanged(void *, unsigned long, bool)));
Chris@0 299 connect(m_manager, SIGNAL(playbackFrameChanged(unsigned long)),
Chris@0 300 this, SLOT(viewManagerPlaybackFrameChanged(unsigned long)));
Chris@0 301 connect(m_manager, SIGNAL(zoomLevelChanged(void *, unsigned long, bool)),
Chris@0 302 this, SLOT(viewManagerZoomLevelChanged(void *, unsigned long, bool)));
Chris@0 303
Chris@0 304 connect(this, SIGNAL(centreFrameChanged(void *, unsigned long, bool)),
Chris@0 305 m_manager, SIGNAL(centreFrameChanged(void *, unsigned long, bool)));
Chris@0 306 connect(this, SIGNAL(zoomLevelChanged(void *, unsigned long, bool)),
Chris@0 307 m_manager, SIGNAL(zoomLevelChanged(void *, unsigned long, bool)));
Chris@0 308 }
Chris@0 309
Chris@0 310 void
Chris@0 311 View::setFollowGlobalPan(bool f)
Chris@0 312 {
Chris@0 313 m_followPan = f;
Chris@0 314 emit propertyContainerPropertyChanged(this);
Chris@0 315 }
Chris@0 316
Chris@0 317 void
Chris@0 318 View::setFollowGlobalZoom(bool f)
Chris@0 319 {
Chris@0 320 m_followZoom = f;
Chris@0 321 emit propertyContainerPropertyChanged(this);
Chris@0 322 }
Chris@0 323
Chris@0 324 void
Chris@0 325 View::setPlaybackFollow(PlaybackFollowMode m)
Chris@0 326 {
Chris@0 327 m_followPlay = m;
Chris@0 328 emit propertyContainerPropertyChanged(this);
Chris@0 329 }
Chris@0 330
Chris@0 331 void
Chris@0 332 View::modelChanged()
Chris@0 333 {
Chris@0 334 QObject *obj = sender();
Chris@0 335
Chris@0 336 #ifdef DEBUG_VIEW_WIDGET_PAINT
Chris@1 337 std::cerr << "View(" << this << ")::modelChanged()" << std::endl;
Chris@0 338 #endif
Chris@0 339 delete m_cache;
Chris@0 340 m_cache = 0;
Chris@0 341
Chris@0 342 checkProgress(obj);
Chris@0 343
Chris@0 344 update();
Chris@0 345 }
Chris@0 346
Chris@0 347 void
Chris@0 348 View::modelChanged(size_t startFrame, size_t endFrame)
Chris@0 349 {
Chris@0 350 QObject *obj = sender();
Chris@0 351
Chris@0 352 long myStartFrame = getStartFrame();
Chris@0 353 size_t myEndFrame = getEndFrame();
Chris@0 354
Chris@1 355 #ifdef DEBUG_VIEW_WIDGET_PAINT
Chris@1 356 std::cerr << "View(" << this << ")::modelChanged(" << startFrame << "," << endFrame << ") [me " << myStartFrame << "," << myEndFrame << "]" << std::endl;
Chris@1 357 #endif
Chris@1 358
Chris@0 359 if (myStartFrame > 0 && endFrame < size_t(myStartFrame)) {
Chris@0 360 checkProgress(obj);
Chris@0 361 return;
Chris@0 362 }
Chris@0 363 if (startFrame > myEndFrame) {
Chris@0 364 checkProgress(obj);
Chris@0 365 return;
Chris@0 366 }
Chris@0 367
Chris@0 368 delete m_cache;
Chris@0 369 m_cache = 0;
Chris@0 370
Chris@0 371 if (long(startFrame) < myStartFrame) startFrame = myStartFrame;
Chris@0 372 if (endFrame > myEndFrame) endFrame = myEndFrame;
Chris@0 373
Chris@0 374 int x0 = (startFrame - myStartFrame) / m_zoomLevel;
Chris@0 375 int x1 = (endFrame - myStartFrame) / m_zoomLevel;
Chris@0 376 if (x1 < x0) return;
Chris@0 377
Chris@0 378 checkProgress(obj);
Chris@0 379
Chris@0 380 update(x0, 0, x1 - x0 + 1, height());
Chris@0 381 }
Chris@0 382
Chris@0 383 void
Chris@0 384 View::modelCompletionChanged()
Chris@0 385 {
Chris@0 386 QObject *obj = sender();
Chris@0 387 checkProgress(obj);
Chris@0 388 }
Chris@0 389
Chris@0 390 void
Chris@0 391 View::modelReplaced()
Chris@0 392 {
Chris@0 393 #ifdef DEBUG_VIEW_WIDGET_PAINT
Chris@1 394 std::cerr << "View(" << this << ")::modelReplaced()" << std::endl;
Chris@0 395 #endif
Chris@1 396 delete m_cache;
Chris@1 397 m_cache = 0;
Chris@1 398
Chris@0 399 m_newModel = true;
Chris@0 400 update();
Chris@0 401 }
Chris@0 402
Chris@0 403 void
Chris@0 404 View::layerParametersChanged()
Chris@0 405 {
Chris@0 406 Layer *layer = dynamic_cast<Layer *>(sender());
Chris@0 407
Chris@0 408 #ifdef DEBUG_VIEW_WIDGET_PAINT
Chris@0 409 std::cerr << "View::layerParametersChanged()" << std::endl;
Chris@0 410 #endif
Chris@0 411
Chris@0 412 delete m_cache;
Chris@0 413 m_cache = 0;
Chris@0 414 update();
Chris@0 415
Chris@0 416 if (layer) {
Chris@0 417 emit propertyContainerPropertyChanged(layer);
Chris@0 418 }
Chris@0 419 }
Chris@0 420
Chris@0 421 void
Chris@0 422 View::layerNameChanged()
Chris@0 423 {
Chris@0 424 Layer *layer = dynamic_cast<Layer *>(sender());
Chris@0 425 if (layer) emit propertyContainerNameChanged(layer);
Chris@0 426 }
Chris@0 427
Chris@0 428 void
Chris@0 429 View::viewManagerCentreFrameChanged(void *p, unsigned long f, bool locked)
Chris@0 430 {
Chris@0 431 if (m_followPan && p != this && locked) {
Chris@0 432 if (m_manager && (sender() == m_manager)) {
Chris@0 433 #ifdef DEBUG_VIEW_WIDGET_PAINT
Chris@0 434 std::cerr << this << ": manager frame changed " << f << " from " << p << std::endl;
Chris@0 435 #endif
Chris@0 436 setCentreFrame(f);
Chris@0 437 if (p == this) repaint();
Chris@0 438 }
Chris@0 439 }
Chris@0 440 }
Chris@0 441
Chris@0 442 void
Chris@0 443 View::viewManagerPlaybackFrameChanged(unsigned long f)
Chris@0 444 {
Chris@0 445 if (m_manager) {
Chris@0 446 if (sender() != m_manager) return;
Chris@0 447 }
Chris@0 448
Chris@0 449 if (m_playPointerFrame == f) return;
Chris@0 450 bool visible = (m_playPointerFrame / m_zoomLevel != f / m_zoomLevel);
Chris@0 451 size_t oldPlayPointerFrame = m_playPointerFrame;
Chris@0 452 m_playPointerFrame = f;
Chris@0 453 if (!visible) return;
Chris@0 454
Chris@0 455 switch (m_followPlay) {
Chris@0 456
Chris@0 457 case PlaybackScrollContinuous:
Chris@0 458 if (QApplication::mouseButtons() == Qt::NoButton) {
Chris@0 459 setCentreFrame(f, false);
Chris@0 460 }
Chris@0 461 break;
Chris@0 462
Chris@0 463 case PlaybackScrollPage:
Chris@0 464 {
Chris@0 465 long w = width() * getZoomLevel();
Chris@0 466 w -= w/5;
Chris@0 467 long sf = (f / w) * w - w/8;
Chris@0 468 #ifdef DEBUG_VIEW_WIDGET_PAINT
Chris@0 469 std::cerr << "PlaybackScrollPage: f = " << f << ", sf = " << sf << ", start frame "
Chris@0 470 << getStartFrame() << std::endl;
Chris@0 471 #endif
Chris@0 472 setCentreFrame(sf + width() * getZoomLevel() / 2, false);
Chris@0 473 int xold = (long(oldPlayPointerFrame) - getStartFrame()) / m_zoomLevel;
Chris@0 474 int xnew = (long(m_playPointerFrame) - getStartFrame()) / m_zoomLevel;
Chris@0 475 update(xold - 1, 0, 3, height());
Chris@0 476 update(xnew - 1, 0, 3, height());
Chris@0 477 break;
Chris@0 478 }
Chris@0 479
Chris@0 480 case PlaybackIgnore:
Chris@0 481 if (long(f) >= getStartFrame() && f < getEndFrame()) {
Chris@0 482 update();
Chris@0 483 }
Chris@0 484 break;
Chris@0 485 }
Chris@0 486 }
Chris@0 487
Chris@0 488 void
Chris@0 489 View::viewManagerZoomLevelChanged(void *p, unsigned long z, bool locked)
Chris@0 490 {
Chris@0 491 if (m_followZoom && p != this && locked) {
Chris@0 492 if (m_manager && (sender() == m_manager)) {
Chris@0 493 setZoomLevel(z);
Chris@0 494 if (p == this) repaint();
Chris@0 495 }
Chris@0 496 }
Chris@0 497 }
Chris@0 498
Chris@0 499 size_t
Chris@0 500 View::getModelsStartFrame() const
Chris@0 501 {
Chris@0 502 bool first = true;
Chris@0 503 size_t startFrame = 0;
Chris@0 504
Chris@0 505 for (LayerList::const_iterator i = m_layers.begin(); i != m_layers.end(); ++i) {
Chris@0 506
Chris@0 507 if ((*i)->getModel() && (*i)->getModel()->isOK()) {
Chris@0 508
Chris@0 509 size_t thisStartFrame = (*i)->getModel()->getStartFrame();
Chris@0 510
Chris@0 511 if (first || thisStartFrame < startFrame) {
Chris@0 512 startFrame = thisStartFrame;
Chris@0 513 }
Chris@0 514 first = false;
Chris@0 515 }
Chris@0 516 }
Chris@0 517 return startFrame;
Chris@0 518 }
Chris@0 519
Chris@0 520 size_t
Chris@0 521 View::getModelsEndFrame() const
Chris@0 522 {
Chris@0 523 bool first = true;
Chris@0 524 size_t endFrame = 0;
Chris@0 525
Chris@0 526 for (LayerList::const_iterator i = m_layers.begin(); i != m_layers.end(); ++i) {
Chris@0 527
Chris@0 528 if ((*i)->getModel() && (*i)->getModel()->isOK()) {
Chris@0 529
Chris@0 530 size_t thisEndFrame = (*i)->getModel()->getEndFrame();
Chris@0 531
Chris@0 532 if (first || thisEndFrame > endFrame) {
Chris@0 533 endFrame = thisEndFrame;
Chris@0 534 }
Chris@0 535 first = false;
Chris@0 536 }
Chris@0 537 }
Chris@0 538
Chris@0 539 if (first) return getModelsStartFrame();
Chris@0 540 return endFrame;
Chris@0 541 }
Chris@0 542
Chris@0 543 int
Chris@0 544 View::getModelsSampleRate() const
Chris@0 545 {
Chris@0 546 //!!! Just go for the first, for now. If we were supporting
Chris@0 547 // multiple samplerates, we'd probably want to do frame/time
Chris@0 548 // conversion in the model
Chris@0 549
Chris@0 550 for (LayerList::const_iterator i = m_layers.begin(); i != m_layers.end(); ++i) {
Chris@0 551 if ((*i)->getModel() && (*i)->getModel()->isOK()) {
Chris@0 552 return (*i)->getModel()->getSampleRate();
Chris@0 553 }
Chris@0 554 }
Chris@0 555 return 0;
Chris@0 556 }
Chris@0 557
Chris@0 558 bool
Chris@0 559 View::areLayersScrollable() const
Chris@0 560 {
Chris@0 561 // True iff all views are scrollable
Chris@0 562 for (LayerList::const_iterator i = m_layers.begin(); i != m_layers.end(); ++i) {
Chris@0 563 if (!(*i)->isLayerScrollable()) return false;
Chris@0 564 }
Chris@0 565 return true;
Chris@0 566 }
Chris@0 567
Chris@0 568 View::LayerList
Chris@0 569 View::getScrollableBackLayers(bool &changed) const
Chris@0 570 {
Chris@0 571 changed = false;
Chris@0 572
Chris@0 573 LayerList scrollables;
Chris@0 574 for (LayerList::const_iterator i = m_layers.begin(); i != m_layers.end(); ++i) {
Chris@0 575 if ((*i)->isLayerScrollable()) scrollables.push_back(*i);
Chris@0 576 else {
Chris@0 577 if (scrollables != m_lastScrollableBackLayers) {
Chris@0 578 m_lastScrollableBackLayers = scrollables;
Chris@0 579 changed = true;
Chris@0 580 }
Chris@0 581 return scrollables;
Chris@0 582 }
Chris@0 583 }
Chris@0 584
Chris@0 585 if (scrollables.size() == 1 &&
Chris@0 586 dynamic_cast<TimeRulerLayer *>(*scrollables.begin())) {
Chris@0 587
Chris@0 588 // If only the ruler is scrollable, it's not worth the bother
Chris@0 589 // -- it probably redraws as quickly as it refreshes from
Chris@0 590 // cache
Chris@0 591 scrollables.clear();
Chris@0 592 }
Chris@0 593
Chris@0 594 if (scrollables != m_lastScrollableBackLayers) {
Chris@0 595 m_lastScrollableBackLayers = scrollables;
Chris@0 596 changed = true;
Chris@0 597 }
Chris@0 598 return scrollables;
Chris@0 599 }
Chris@0 600
Chris@0 601 View::LayerList
Chris@0 602 View::getNonScrollableFrontLayers(bool &changed) const
Chris@0 603 {
Chris@0 604 changed = false;
Chris@0 605 LayerList scrollables = getScrollableBackLayers(changed);
Chris@0 606 LayerList nonScrollables;
Chris@0 607
Chris@0 608 // Everything in front of the first non-scrollable from the back
Chris@0 609 // should also be considered non-scrollable
Chris@0 610
Chris@0 611 size_t count = 0;
Chris@0 612 for (LayerList::const_iterator i = m_layers.begin(); i != m_layers.end(); ++i) {
Chris@0 613 if (count < scrollables.size()) {
Chris@0 614 ++count;
Chris@0 615 continue;
Chris@0 616 }
Chris@0 617 nonScrollables.push_back(*i);
Chris@0 618 }
Chris@0 619
Chris@0 620 if (nonScrollables != m_lastNonScrollableBackLayers) {
Chris@0 621 m_lastNonScrollableBackLayers = nonScrollables;
Chris@0 622 changed = true;
Chris@0 623 }
Chris@0 624
Chris@0 625 return nonScrollables;
Chris@0 626 }
Chris@0 627
Chris@0 628 size_t
Chris@0 629 View::getZoomConstraintBlockSize(size_t blockSize,
Chris@0 630 ZoomConstraint::RoundingDirection dir)
Chris@0 631 const
Chris@0 632 {
Chris@0 633 size_t candidate = blockSize;
Chris@0 634 bool haveCandidate = false;
Chris@0 635
Chris@0 636 for (LayerList::const_iterator i = m_layers.begin(); i != m_layers.end(); ++i) {
Chris@0 637
Chris@0 638 if ((*i)->getZoomConstraint()) {
Chris@0 639
Chris@0 640 size_t thisBlockSize =
Chris@0 641 (*i)->getZoomConstraint()->getNearestBlockSize
Chris@0 642 (blockSize, dir);
Chris@0 643
Chris@0 644 // Go for the block size that's furthest from the one
Chris@0 645 // passed in. Most of the time, that's what we want.
Chris@0 646 if (!haveCandidate ||
Chris@0 647 (thisBlockSize > blockSize && thisBlockSize > candidate) ||
Chris@0 648 (thisBlockSize < blockSize && thisBlockSize < candidate)) {
Chris@0 649 candidate = thisBlockSize;
Chris@0 650 haveCandidate = true;
Chris@0 651 }
Chris@0 652 }
Chris@0 653 }
Chris@0 654
Chris@0 655 return candidate;
Chris@0 656 }
Chris@0 657
Chris@0 658 void
Chris@0 659 View::zoom(bool in)
Chris@0 660 {
Chris@0 661 int newZoomLevel = m_zoomLevel;
Chris@0 662
Chris@0 663 if (in) {
Chris@0 664 newZoomLevel = getZoomConstraintBlockSize(newZoomLevel - 1,
Chris@0 665 ZoomConstraint::RoundDown);
Chris@0 666 } else {
Chris@0 667 newZoomLevel = getZoomConstraintBlockSize(newZoomLevel + 1,
Chris@0 668 ZoomConstraint::RoundUp);
Chris@0 669 }
Chris@0 670
Chris@0 671 if (newZoomLevel != m_zoomLevel) {
Chris@0 672 setZoomLevel(newZoomLevel);
Chris@0 673 }
Chris@0 674 }
Chris@0 675
Chris@0 676 void
Chris@0 677 View::checkProgress(void *object)
Chris@0 678 {
Chris@0 679 // std::cerr << "View::checkProgress(" << object << ")" << std::endl;
Chris@0 680
Chris@0 681 if (!m_showProgress) return;
Chris@0 682
Chris@0 683 int ph = height();
Chris@0 684
Chris@0 685 for (ProgressMap::const_iterator i = m_progressBars.begin();
Chris@0 686 i != m_progressBars.end(); ++i) {
Chris@0 687
Chris@0 688 if (i->first == object) {
Chris@0 689
Chris@0 690 int completion = i->first->getCompletion();
Chris@0 691
Chris@0 692 if (completion >= 100) {
Chris@0 693
Chris@0 694 i->second->hide();
Chris@0 695
Chris@0 696 } else {
Chris@0 697
Chris@0 698 i->second->setText(i->first->getPropertyContainerName());
Chris@0 699 i->second->setValue(completion);
Chris@0 700 i->second->move(0, ph - i->second->height());
Chris@0 701
Chris@0 702 i->second->show();
Chris@0 703 i->second->update();
Chris@0 704
Chris@0 705 ph -= i->second->height();
Chris@0 706 }
Chris@0 707 } else {
Chris@0 708 if (i->second->isVisible()) {
Chris@0 709 ph -= i->second->height();
Chris@0 710 }
Chris@0 711 }
Chris@0 712 }
Chris@0 713 }
Chris@0 714 /*!!!
Chris@0 715 void
Chris@0 716 View::identifyLocalFeatures(bool on, int x, int y)
Chris@0 717 {
Chris@0 718 for (LayerList::const_iterator i = m_layers.end(); i != m_layers.begin(); ) {
Chris@0 719 --i;
Chris@0 720 #ifdef DEBUG_VIEW_WIDGET_PAINT
Chris@0 721 std::cerr << "View::identifyLocalFeatures: calling on " << *i << std::endl;
Chris@0 722 #endif
Chris@0 723 if ((*i)->identifyLocalFeatures(on, x, y)) break;
Chris@0 724 }
Chris@0 725 }
Chris@0 726 */
Chris@0 727
Chris@0 728 void
Chris@0 729 View::paintEvent(QPaintEvent *e)
Chris@0 730 {
Chris@0 731 // Profiler prof("View::paintEvent", true);
Chris@0 732 // std::cerr << "View::paintEvent" << std::endl;
Chris@0 733
Chris@0 734 if (m_layers.empty()) {
Chris@0 735 QFrame::paintEvent(e);
Chris@0 736 return;
Chris@0 737 }
Chris@0 738
Chris@0 739 if (m_newModel) {
Chris@0 740 m_newModel = false;
Chris@0 741 }
Chris@0 742
Chris@0 743 // ensure our constraints are met
Chris@0 744 m_zoomLevel = getZoomConstraintBlockSize(m_zoomLevel,
Chris@0 745 ZoomConstraint::RoundUp);
Chris@0 746
Chris@0 747 QPainter paint;
Chris@0 748 bool repaintCache = false;
Chris@0 749 bool paintedCacheRect = false;
Chris@0 750
Chris@0 751 QRect cacheRect(rect());
Chris@0 752
Chris@0 753 if (e) {
Chris@0 754 cacheRect &= e->rect();
Chris@0 755 #ifdef DEBUG_VIEW_WIDGET_PAINT
Chris@0 756 std::cerr << "paint rect " << cacheRect.width() << "x" << cacheRect.height()
Chris@0 757 << ", my rect " << width() << "x" << height() << std::endl;
Chris@0 758 #endif
Chris@0 759 }
Chris@0 760
Chris@0 761 QRect nonCacheRect(cacheRect);
Chris@0 762
Chris@0 763 // If not all layers are scrollable, but some of the back layers
Chris@0 764 // are, we should store only those in the cache
Chris@0 765
Chris@0 766 bool layersChanged = false;
Chris@0 767 LayerList scrollables = getScrollableBackLayers(layersChanged);
Chris@0 768 LayerList nonScrollables = getNonScrollableFrontLayers(layersChanged);
Chris@0 769
Chris@0 770 #ifdef DEBUG_VIEW_WIDGET_PAINT
Chris@0 771 std::cerr << "View(" << this << ")::paintEvent: have " << scrollables.size()
Chris@0 772 << " scrollable back layers and " << nonScrollables.size()
Chris@0 773 << " non-scrollable front layers" << std::endl;
Chris@0 774 #endif
Chris@0 775
Chris@0 776 if (layersChanged || scrollables.empty()) {
Chris@0 777 delete m_cache;
Chris@0 778 m_cache = 0;
Chris@0 779 }
Chris@0 780
Chris@0 781 if (!scrollables.empty()) {
Chris@0 782 if (!m_cache ||
Chris@0 783 m_cacheZoomLevel != m_zoomLevel ||
Chris@0 784 width() != m_cache->width() ||
Chris@0 785 height() != m_cache->height()) {
Chris@0 786
Chris@0 787 // cache is not valid
Chris@0 788
Chris@0 789 if (cacheRect.width() < width()/10) {
Chris@0 790 #ifdef DEBUG_VIEW_WIDGET_PAINT
Chris@0 791 std::cerr << "View(" << this << ")::paintEvent: small repaint, not bothering to recreate cache" << std::endl;
Chris@0 792 #endif
Chris@0 793 } else {
Chris@0 794 delete m_cache;
Chris@0 795 m_cache = new QPixmap(width(), height());
Chris@0 796 #ifdef DEBUG_VIEW_WIDGET_PAINT
Chris@0 797 std::cerr << "View(" << this << ")::paintEvent: recreated cache" << std::endl;
Chris@0 798 #endif
Chris@0 799 cacheRect = rect();
Chris@0 800 repaintCache = true;
Chris@0 801 }
Chris@0 802
Chris@0 803 } else if (m_cacheCentreFrame != m_centreFrame) {
Chris@0 804
Chris@0 805 long dx = long(m_cacheCentreFrame / m_zoomLevel) -
Chris@0 806 long(m_centreFrame / m_zoomLevel);
Chris@0 807
Chris@0 808 if (dx > -width() && dx < width()) {
Chris@0 809 #if defined(Q_WS_WIN32) || defined(Q_WS_MAC)
Chris@0 810 // Copying a pixmap to itself doesn't work properly on Windows
Chris@0 811 // or Mac (it only works when moving in one direction)
Chris@0 812 static QPixmap *tmpPixmap = 0;
Chris@0 813 if (!tmpPixmap ||
Chris@0 814 tmpPixmap->width() != width() ||
Chris@0 815 tmpPixmap->height() != height()) {
Chris@0 816 delete tmpPixmap;
Chris@0 817 tmpPixmap = new QPixmap(width(), height());
Chris@0 818 }
Chris@0 819 paint.begin(tmpPixmap);
Chris@0 820 paint.drawPixmap(0, 0, *m_cache);
Chris@0 821 paint.end();
Chris@0 822 paint.begin(m_cache);
Chris@0 823 paint.drawPixmap(dx, 0, *tmpPixmap);
Chris@0 824 paint.end();
Chris@0 825 #else
Chris@0 826 // But it seems to be fine on X11
Chris@0 827 paint.begin(m_cache);
Chris@0 828 paint.drawPixmap(dx, 0, *m_cache);
Chris@0 829 paint.end();
Chris@0 830 #endif
Chris@0 831
Chris@0 832 if (dx < 0) {
Chris@0 833 cacheRect = QRect(width() + dx, 0, -dx, height());
Chris@0 834 } else {
Chris@0 835 cacheRect = QRect(0, 0, dx, height());
Chris@0 836 }
Chris@0 837 #ifdef DEBUG_VIEW_WIDGET_PAINT
Chris@0 838 std::cerr << "View(" << this << ")::paintEvent: scrolled cache by " << dx << std::endl;
Chris@0 839 #endif
Chris@0 840 } else {
Chris@0 841 cacheRect = rect();
Chris@0 842 #ifdef DEBUG_VIEW_WIDGET_PAINT
Chris@0 843 std::cerr << "View(" << this << ")::paintEvent: scrolling too far" << std::endl;
Chris@0 844 #endif
Chris@0 845 }
Chris@0 846 repaintCache = true;
Chris@0 847
Chris@0 848 } else {
Chris@0 849 #ifdef DEBUG_VIEW_WIDGET_PAINT
Chris@0 850 std::cerr << "View(" << this << ")::paintEvent: cache is good" << std::endl;
Chris@0 851 #endif
Chris@0 852 paint.begin(this);
Chris@0 853 paint.drawPixmap(cacheRect, *m_cache, cacheRect);
Chris@0 854 paint.end();
Chris@0 855 QFrame::paintEvent(e);
Chris@0 856 paintedCacheRect = true;
Chris@0 857 }
Chris@0 858
Chris@0 859 m_cacheCentreFrame = m_centreFrame;
Chris@0 860 m_cacheZoomLevel = m_zoomLevel;
Chris@0 861 }
Chris@0 862
Chris@0 863 #ifdef DEBUG_VIEW_WIDGET_PAINT
Chris@0 864 // std::cerr << "View(" << this << ")::paintEvent: cacheRect " << cacheRect << ", nonCacheRect " << (nonCacheRect | cacheRect) << ", repaintCache " << repaintCache << ", paintedCacheRect " << paintedCacheRect << std::endl;
Chris@0 865 #endif
Chris@0 866
Chris@0 867 // Scrollable (cacheable) items first
Chris@0 868
Chris@0 869 if (!paintedCacheRect) {
Chris@0 870
Chris@0 871 if (repaintCache) paint.begin(m_cache);
Chris@0 872 else paint.begin(this);
Chris@0 873
Chris@0 874 paint.setClipRect(cacheRect);
Chris@0 875
Chris@0 876 if (hasLightBackground()) {
Chris@0 877 paint.setPen(Qt::white);
Chris@0 878 paint.setBrush(Qt::white);
Chris@0 879 } else {
Chris@0 880 paint.setPen(Qt::black);
Chris@0 881 paint.setBrush(Qt::black);
Chris@0 882 }
Chris@0 883 paint.drawRect(cacheRect);
Chris@0 884
Chris@0 885 paint.setPen(Qt::black);
Chris@0 886 paint.setBrush(Qt::NoBrush);
Chris@0 887
Chris@0 888 for (LayerList::iterator i = scrollables.begin(); i != scrollables.end(); ++i) {
Chris@0 889 (*i)->paint(paint, cacheRect);
Chris@0 890 }
Chris@0 891
Chris@0 892 paint.end();
Chris@0 893
Chris@0 894 if (repaintCache) {
Chris@0 895 cacheRect |= (e ? e->rect() : rect());
Chris@0 896 paint.begin(this);
Chris@0 897 paint.drawPixmap(cacheRect, *m_cache, cacheRect);
Chris@0 898 paint.end();
Chris@0 899 }
Chris@0 900 }
Chris@0 901
Chris@0 902 // Now non-cacheable items. We always need to redraw the
Chris@0 903 // non-cacheable items across at least the area we drew of the
Chris@0 904 // cacheable items.
Chris@0 905
Chris@0 906 nonCacheRect |= cacheRect;
Chris@0 907
Chris@0 908 paint.begin(this);
Chris@0 909 paint.setClipRect(nonCacheRect);
Chris@0 910
Chris@0 911 if (scrollables.empty()) {
Chris@0 912 if (hasLightBackground()) {
Chris@0 913 paint.setPen(Qt::white);
Chris@0 914 paint.setBrush(Qt::white);
Chris@0 915 } else {
Chris@0 916 paint.setPen(Qt::black);
Chris@0 917 paint.setBrush(Qt::black);
Chris@0 918 }
Chris@0 919 paint.drawRect(nonCacheRect);
Chris@0 920 }
Chris@0 921
Chris@0 922 paint.setPen(Qt::black);
Chris@0 923 paint.setBrush(Qt::NoBrush);
Chris@0 924
Chris@0 925 for (LayerList::iterator i = nonScrollables.begin(); i != nonScrollables.end(); ++i) {
Chris@0 926 (*i)->paint(paint, nonCacheRect);
Chris@0 927 }
Chris@0 928
Chris@0 929 paint.end();
Chris@0 930
Chris@0 931 if (m_followPlay != PlaybackScrollContinuous) {
Chris@0 932
Chris@0 933 paint.begin(this);
Chris@0 934
Chris@0 935 if (long(m_playPointerFrame) > getStartFrame() &&
Chris@0 936 m_playPointerFrame < getEndFrame()) {
Chris@0 937
Chris@0 938 int playx = (long(m_playPointerFrame) - getStartFrame()) /
Chris@0 939 m_zoomLevel;
Chris@0 940
Chris@0 941 paint.setPen(Qt::black);
Chris@0 942 paint.drawLine(playx - 1, 0, playx - 1, height() - 1);
Chris@0 943 paint.drawLine(playx + 1, 0, playx + 1, height() - 1);
Chris@0 944 paint.drawPoint(playx, 0);
Chris@0 945 paint.drawPoint(playx, height() - 1);
Chris@0 946 paint.setPen(Qt::white);
Chris@0 947 paint.drawLine(playx, 1, playx, height() - 2);
Chris@0 948 }
Chris@0 949
Chris@0 950 paint.end();
Chris@0 951 }
Chris@0 952
Chris@0 953 QFrame::paintEvent(e);
Chris@0 954 }
Chris@0 955
Chris@3 956 QString
Chris@3 957 View::toXmlString(QString indent, QString extraAttributes) const
Chris@3 958 {
Chris@3 959 QString s;
Chris@3 960
Chris@3 961 s += indent;
Chris@3 962
Chris@3 963 s += QString("<view "
Chris@3 964 "centre=\"%1\" "
Chris@3 965 "zoom=\"%2\" "
Chris@3 966 "followPan=\"%3\" "
Chris@3 967 "followZoom=\"%4\" "
Chris@3 968 "tracking=\"%5\" "
Chris@3 969 "light=\"%6\" %7>\n")
Chris@3 970 .arg(m_centreFrame)
Chris@3 971 .arg(m_zoomLevel)
Chris@3 972 .arg(m_followPan)
Chris@3 973 .arg(m_followZoom)
Chris@3 974 .arg(m_followPlay == PlaybackScrollContinuous ? "scroll" :
Chris@3 975 m_followPlay == PlaybackScrollPage ? "page" : "ignore")
Chris@3 976 .arg(m_lightBackground)
Chris@3 977 .arg(extraAttributes);
Chris@3 978
Chris@3 979 for (size_t i = 0; i < m_layers.size(); ++i) {
Chris@3 980 s += m_layers[i]->toXmlString(indent + " ");
Chris@3 981 }
Chris@3 982
Chris@4 983 s += indent + "</view>\n";
Chris@3 984
Chris@3 985 return s;
Chris@3 986 }
Chris@3 987
Chris@3 988
Chris@0 989 #ifdef INCLUDE_MOCFILES
Chris@0 990 #include "View.moc.cpp"
Chris@0 991 #endif
Chris@0 992