annotate view/View.cpp @ 162:f32212631b9c

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