annotate view/View.cpp @ 150:b1a3a9400284

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