annotate base/View.cpp @ 76:af2725b5d6fe

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