annotate view/View.cpp @ 183:5f86ae638b04

* Omit translucent blue fill for selection rectangles that are superimposed over layers that use colours for meaningful purposes such as the spectrogram (CHARM change request)
author Chris Cannam
date Fri, 24 Nov 2006 16:56:15 +0000
parents 29f01de27db4
children 8dd247c4c5f1
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@180 774 setCentreFrame(f, true); //!!!
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@183 1030 bool
Chris@183 1031 View::areLayerColoursSignificant() const
Chris@183 1032 {
Chris@183 1033 for (LayerList::const_iterator i = m_layers.begin(); i != m_layers.end(); ++i) {
Chris@183 1034 if ((*i)->isLayerColourSignificant()) return true;
Chris@183 1035 if ((*i)->isLayerOpaque()) break;
Chris@183 1036 }
Chris@183 1037 return false;
Chris@183 1038 }
Chris@183 1039
Chris@127 1040 void
Chris@127 1041 View::zoom(bool in)
Chris@127 1042 {
Chris@127 1043 int newZoomLevel = m_zoomLevel;
Chris@127 1044
Chris@127 1045 if (in) {
Chris@127 1046 newZoomLevel = getZoomConstraintBlockSize(newZoomLevel - 1,
Chris@127 1047 ZoomConstraint::RoundDown);
Chris@127 1048 } else {
Chris@127 1049 newZoomLevel = getZoomConstraintBlockSize(newZoomLevel + 1,
Chris@127 1050 ZoomConstraint::RoundUp);
Chris@127 1051 }
Chris@127 1052
Chris@127 1053 if (newZoomLevel != m_zoomLevel) {
Chris@127 1054 setZoomLevel(newZoomLevel);
Chris@127 1055 }
Chris@127 1056 }
Chris@127 1057
Chris@127 1058 void
Chris@127 1059 View::scroll(bool right, bool lots)
Chris@127 1060 {
Chris@127 1061 long delta;
Chris@127 1062 if (lots) {
Chris@127 1063 delta = (getEndFrame() - getStartFrame()) / 2;
Chris@127 1064 } else {
Chris@127 1065 delta = (getEndFrame() - getStartFrame()) / 20;
Chris@127 1066 }
Chris@127 1067 if (right) delta = -delta;
Chris@127 1068
Chris@127 1069 if (int(m_centreFrame) < delta) {
Chris@127 1070 setCentreFrame(0);
Chris@127 1071 } else if (int(m_centreFrame) - delta >= int(getModelsEndFrame())) {
Chris@127 1072 setCentreFrame(getModelsEndFrame());
Chris@127 1073 } else {
Chris@127 1074 setCentreFrame(m_centreFrame - delta);
Chris@127 1075 }
Chris@127 1076 }
Chris@127 1077
Chris@127 1078 void
Chris@127 1079 View::checkProgress(void *object)
Chris@127 1080 {
Chris@127 1081 if (!m_showProgress) return;
Chris@127 1082
Chris@127 1083 int ph = height();
Chris@127 1084
Chris@127 1085 for (ProgressMap::const_iterator i = m_progressBars.begin();
Chris@127 1086 i != m_progressBars.end(); ++i) {
Chris@127 1087
Chris@127 1088 if (i->first == object) {
Chris@127 1089
Chris@127 1090 int completion = i->first->getCompletion(this);
Chris@127 1091
Chris@127 1092 if (completion >= 100) {
Chris@127 1093
Chris@127 1094 i->second->hide();
Chris@127 1095
Chris@127 1096 } else {
Chris@127 1097
Chris@127 1098 i->second->setText(i->first->getPropertyContainerName());
Chris@132 1099
Chris@127 1100 i->second->setValue(completion);
Chris@127 1101 i->second->move(0, ph - i->second->height());
Chris@127 1102
Chris@127 1103 i->second->show();
Chris@127 1104 i->second->update();
Chris@127 1105
Chris@127 1106 ph -= i->second->height();
Chris@127 1107 }
Chris@127 1108 } else {
Chris@127 1109 if (i->second->isVisible()) {
Chris@127 1110 ph -= i->second->height();
Chris@127 1111 }
Chris@127 1112 }
Chris@127 1113 }
Chris@127 1114 }
Chris@127 1115
Chris@127 1116 void
Chris@127 1117 View::paintEvent(QPaintEvent *e)
Chris@127 1118 {
Chris@127 1119 // Profiler prof("View::paintEvent", false);
Chris@127 1120 // std::cerr << "View::paintEvent" << std::endl;
Chris@127 1121
Chris@127 1122 if (m_layers.empty()) {
Chris@127 1123 QFrame::paintEvent(e);
Chris@127 1124 return;
Chris@127 1125 }
Chris@127 1126
Chris@127 1127 // ensure our constraints are met
Chris@137 1128
Chris@137 1129 /*!!! Should we do this only if we have layers that can't support other
Chris@137 1130 zoom levels?
Chris@137 1131
Chris@127 1132 m_zoomLevel = getZoomConstraintBlockSize(m_zoomLevel,
Chris@127 1133 ZoomConstraint::RoundUp);
Chris@137 1134 */
Chris@127 1135
Chris@127 1136 QPainter paint;
Chris@127 1137 bool repaintCache = false;
Chris@127 1138 bool paintedCacheRect = false;
Chris@127 1139
Chris@127 1140 QRect cacheRect(rect());
Chris@127 1141
Chris@127 1142 if (e) {
Chris@127 1143 cacheRect &= e->rect();
Chris@127 1144 #ifdef DEBUG_VIEW_WIDGET_PAINT
Chris@127 1145 std::cerr << "paint rect " << cacheRect.width() << "x" << cacheRect.height()
Chris@127 1146 << ", my rect " << width() << "x" << height() << std::endl;
Chris@127 1147 #endif
Chris@127 1148 }
Chris@127 1149
Chris@127 1150 QRect nonCacheRect(cacheRect);
Chris@127 1151
Chris@127 1152 // If not all layers are scrollable, but some of the back layers
Chris@127 1153 // are, we should store only those in the cache.
Chris@127 1154
Chris@127 1155 bool layersChanged = false;
Chris@127 1156 LayerList scrollables = getScrollableBackLayers(true, layersChanged);
Chris@127 1157 LayerList nonScrollables = getNonScrollableFrontLayers(true, layersChanged);
Chris@127 1158 bool selectionCacheable = nonScrollables.empty();
Chris@127 1159 bool haveSelections = m_manager && !m_manager->getSelections().empty();
Chris@127 1160 bool selectionDrawn = false;
Chris@127 1161
Chris@127 1162 // If all the non-scrollable layers are non-opaque, then we draw
Chris@127 1163 // the selection rectangle behind them and cache it. If any are
Chris@127 1164 // opaque, however, we can't cache.
Chris@127 1165 //
Chris@127 1166 if (!selectionCacheable) {
Chris@127 1167 selectionCacheable = true;
Chris@127 1168 for (LayerList::const_iterator i = nonScrollables.begin();
Chris@127 1169 i != nonScrollables.end(); ++i) {
Chris@127 1170 if ((*i)->isLayerOpaque()) {
Chris@127 1171 selectionCacheable = false;
Chris@127 1172 break;
Chris@127 1173 }
Chris@127 1174 }
Chris@127 1175 }
Chris@127 1176
Chris@127 1177 if (selectionCacheable) {
Chris@127 1178 QPoint localPos;
Chris@127 1179 bool closeToLeft, closeToRight;
Chris@127 1180 if (shouldIlluminateLocalSelection(localPos, closeToLeft, closeToRight)) {
Chris@127 1181 selectionCacheable = false;
Chris@127 1182 }
Chris@127 1183 }
Chris@127 1184
Chris@127 1185 #ifdef DEBUG_VIEW_WIDGET_PAINT
Chris@127 1186 std::cerr << "View(" << this << ")::paintEvent: have " << scrollables.size()
Chris@127 1187 << " scrollable back layers and " << nonScrollables.size()
Chris@127 1188 << " non-scrollable front layers" << std::endl;
Chris@127 1189 std::cerr << "haveSelections " << haveSelections << ", selectionCacheable "
Chris@127 1190 << selectionCacheable << ", m_selectionCached " << m_selectionCached << std::endl;
Chris@127 1191 #endif
Chris@127 1192
Chris@127 1193 if (layersChanged || scrollables.empty() ||
Chris@127 1194 (haveSelections && (selectionCacheable != m_selectionCached))) {
Chris@127 1195 delete m_cache;
Chris@127 1196 m_cache = 0;
Chris@127 1197 m_selectionCached = false;
Chris@127 1198 }
Chris@127 1199
Chris@127 1200 if (!scrollables.empty()) {
Chris@127 1201 if (!m_cache ||
Chris@127 1202 m_cacheZoomLevel != m_zoomLevel ||
Chris@127 1203 width() != m_cache->width() ||
Chris@127 1204 height() != m_cache->height()) {
Chris@127 1205
Chris@127 1206 // cache is not valid
Chris@127 1207
Chris@127 1208 if (cacheRect.width() < width()/10) {
Chris@127 1209 #ifdef DEBUG_VIEW_WIDGET_PAINT
Chris@127 1210 std::cerr << "View(" << this << ")::paintEvent: small repaint, not bothering to recreate cache" << std::endl;
Chris@127 1211 #endif
Chris@127 1212 } else {
Chris@127 1213 delete m_cache;
Chris@127 1214 m_cache = new QPixmap(width(), height());
Chris@127 1215 #ifdef DEBUG_VIEW_WIDGET_PAINT
Chris@127 1216 std::cerr << "View(" << this << ")::paintEvent: recreated cache" << std::endl;
Chris@127 1217 #endif
Chris@127 1218 cacheRect = rect();
Chris@127 1219 repaintCache = true;
Chris@127 1220 }
Chris@127 1221
Chris@127 1222 } else if (m_cacheCentreFrame != m_centreFrame) {
Chris@127 1223
Chris@127 1224 long dx =
Chris@127 1225 getXForFrame(m_cacheCentreFrame) -
Chris@127 1226 getXForFrame(m_centreFrame);
Chris@127 1227
Chris@127 1228 if (dx > -width() && dx < width()) {
Chris@127 1229 #if defined(Q_WS_WIN32) || defined(Q_WS_MAC)
Chris@127 1230 // Copying a pixmap to itself doesn't work properly on Windows
Chris@127 1231 // or Mac (it only works when moving in one direction)
Chris@127 1232 static QPixmap *tmpPixmap = 0;
Chris@127 1233 if (!tmpPixmap ||
Chris@127 1234 tmpPixmap->width() != width() ||
Chris@127 1235 tmpPixmap->height() != height()) {
Chris@127 1236 delete tmpPixmap;
Chris@127 1237 tmpPixmap = new QPixmap(width(), height());
Chris@127 1238 }
Chris@127 1239 paint.begin(tmpPixmap);
Chris@127 1240 paint.drawPixmap(0, 0, *m_cache);
Chris@127 1241 paint.end();
Chris@127 1242 paint.begin(m_cache);
Chris@127 1243 paint.drawPixmap(dx, 0, *tmpPixmap);
Chris@127 1244 paint.end();
Chris@127 1245 #else
Chris@127 1246 // But it seems to be fine on X11
Chris@127 1247 paint.begin(m_cache);
Chris@127 1248 paint.drawPixmap(dx, 0, *m_cache);
Chris@127 1249 paint.end();
Chris@127 1250 #endif
Chris@127 1251
Chris@127 1252 if (dx < 0) {
Chris@127 1253 cacheRect = QRect(width() + dx, 0, -dx, height());
Chris@127 1254 } else {
Chris@127 1255 cacheRect = QRect(0, 0, dx, height());
Chris@127 1256 }
Chris@127 1257 #ifdef DEBUG_VIEW_WIDGET_PAINT
Chris@127 1258 std::cerr << "View(" << this << ")::paintEvent: scrolled cache by " << dx << std::endl;
Chris@127 1259 #endif
Chris@127 1260 } else {
Chris@127 1261 cacheRect = rect();
Chris@127 1262 #ifdef DEBUG_VIEW_WIDGET_PAINT
Chris@127 1263 std::cerr << "View(" << this << ")::paintEvent: scrolling too far" << std::endl;
Chris@127 1264 #endif
Chris@127 1265 }
Chris@127 1266 repaintCache = true;
Chris@127 1267
Chris@127 1268 } else {
Chris@127 1269 #ifdef DEBUG_VIEW_WIDGET_PAINT
Chris@127 1270 std::cerr << "View(" << this << ")::paintEvent: cache is good" << std::endl;
Chris@127 1271 #endif
Chris@127 1272 paint.begin(this);
Chris@127 1273 paint.drawPixmap(cacheRect, *m_cache, cacheRect);
Chris@127 1274 paint.end();
Chris@127 1275 QFrame::paintEvent(e);
Chris@127 1276 paintedCacheRect = true;
Chris@127 1277 }
Chris@127 1278
Chris@127 1279 m_cacheCentreFrame = m_centreFrame;
Chris@127 1280 m_cacheZoomLevel = m_zoomLevel;
Chris@127 1281 }
Chris@127 1282
Chris@127 1283 #ifdef DEBUG_VIEW_WIDGET_PAINT
Chris@127 1284 // std::cerr << "View(" << this << ")::paintEvent: cacheRect " << cacheRect << ", nonCacheRect " << (nonCacheRect | cacheRect) << ", repaintCache " << repaintCache << ", paintedCacheRect " << paintedCacheRect << std::endl;
Chris@127 1285 #endif
Chris@127 1286
Chris@127 1287 // Scrollable (cacheable) items first
Chris@127 1288
Chris@127 1289 if (!paintedCacheRect) {
Chris@127 1290
Chris@127 1291 if (repaintCache) paint.begin(m_cache);
Chris@127 1292 else paint.begin(this);
Chris@127 1293
Chris@127 1294 paint.setClipRect(cacheRect);
Chris@127 1295
Chris@127 1296 if (hasLightBackground()) {
Chris@127 1297 paint.setPen(Qt::white);
Chris@127 1298 paint.setBrush(Qt::white);
Chris@127 1299 } else {
Chris@127 1300 paint.setPen(Qt::black);
Chris@127 1301 paint.setBrush(Qt::black);
Chris@127 1302 }
Chris@127 1303 paint.drawRect(cacheRect);
Chris@127 1304
Chris@127 1305 paint.setPen(Qt::black);
Chris@127 1306 paint.setBrush(Qt::NoBrush);
Chris@127 1307
Chris@127 1308 for (LayerList::iterator i = scrollables.begin(); i != scrollables.end(); ++i) {
Chris@127 1309 paint.setRenderHint(QPainter::Antialiasing, false);
Chris@127 1310 paint.save();
Chris@127 1311 (*i)->paint(this, paint, cacheRect);
Chris@127 1312 paint.restore();
Chris@127 1313 }
Chris@127 1314
Chris@127 1315 if (haveSelections && selectionCacheable) {
Chris@127 1316 drawSelections(paint);
Chris@127 1317 m_selectionCached = repaintCache;
Chris@127 1318 selectionDrawn = true;
Chris@127 1319 }
Chris@127 1320
Chris@127 1321 paint.end();
Chris@127 1322
Chris@127 1323 if (repaintCache) {
Chris@127 1324 cacheRect |= (e ? e->rect() : rect());
Chris@127 1325 paint.begin(this);
Chris@127 1326 paint.drawPixmap(cacheRect, *m_cache, cacheRect);
Chris@127 1327 paint.end();
Chris@127 1328 }
Chris@127 1329 }
Chris@127 1330
Chris@127 1331 // Now non-cacheable items. We always need to redraw the
Chris@127 1332 // non-cacheable items across at least the area we drew of the
Chris@127 1333 // cacheable items.
Chris@127 1334
Chris@127 1335 nonCacheRect |= cacheRect;
Chris@127 1336
Chris@127 1337 paint.begin(this);
Chris@127 1338 paint.setClipRect(nonCacheRect);
Chris@127 1339
Chris@127 1340 if (scrollables.empty()) {
Chris@127 1341 if (hasLightBackground()) {
Chris@127 1342 paint.setPen(Qt::white);
Chris@127 1343 paint.setBrush(Qt::white);
Chris@127 1344 } else {
Chris@127 1345 paint.setPen(Qt::black);
Chris@127 1346 paint.setBrush(Qt::black);
Chris@127 1347 }
Chris@127 1348 paint.drawRect(nonCacheRect);
Chris@127 1349 }
Chris@127 1350
Chris@127 1351 paint.setPen(Qt::black);
Chris@127 1352 paint.setBrush(Qt::NoBrush);
Chris@127 1353
Chris@127 1354 for (LayerList::iterator i = nonScrollables.begin(); i != nonScrollables.end(); ++i) {
Chris@127 1355 // Profiler profiler2("View::paintEvent non-cacheable");
Chris@127 1356 (*i)->paint(this, paint, nonCacheRect);
Chris@127 1357 }
Chris@127 1358
Chris@127 1359 paint.end();
Chris@127 1360
Chris@127 1361 paint.begin(this);
Chris@127 1362 if (e) paint.setClipRect(e->rect());
Chris@127 1363 if (!m_selectionCached) {
Chris@127 1364 drawSelections(paint);
Chris@127 1365 }
Chris@127 1366 paint.end();
Chris@127 1367
Chris@127 1368 if (m_followPlay != PlaybackScrollContinuous) {
Chris@127 1369
Chris@127 1370 paint.begin(this);
Chris@127 1371
Chris@127 1372 if (long(m_playPointerFrame) > getStartFrame() &&
Chris@127 1373 m_playPointerFrame < getEndFrame()) {
Chris@127 1374
Chris@127 1375 int playx = getXForFrame(m_playPointerFrame);
Chris@127 1376
Chris@127 1377 paint.setPen(Qt::black);
Chris@127 1378 paint.drawLine(playx - 1, 0, playx - 1, height() - 1);
Chris@127 1379 paint.drawLine(playx + 1, 0, playx + 1, height() - 1);
Chris@127 1380 paint.drawPoint(playx, 0);
Chris@127 1381 paint.drawPoint(playx, height() - 1);
Chris@127 1382 paint.setPen(Qt::white);
Chris@127 1383 paint.drawLine(playx, 1, playx, height() - 2);
Chris@127 1384 }
Chris@127 1385
Chris@127 1386 paint.end();
Chris@127 1387 }
Chris@127 1388
Chris@127 1389 QFrame::paintEvent(e);
Chris@127 1390 }
Chris@127 1391
Chris@127 1392 void
Chris@127 1393 View::drawSelections(QPainter &paint)
Chris@127 1394 {
Chris@127 1395 MultiSelection::SelectionList selections;
Chris@127 1396
Chris@127 1397 if (m_manager) {
Chris@127 1398 selections = m_manager->getSelections();
Chris@127 1399 if (m_manager->haveInProgressSelection()) {
Chris@127 1400 bool exclusive;
Chris@127 1401 Selection inProgressSelection =
Chris@127 1402 m_manager->getInProgressSelection(exclusive);
Chris@127 1403 if (exclusive) selections.clear();
Chris@127 1404 selections.insert(inProgressSelection);
Chris@127 1405 }
Chris@127 1406 }
Chris@127 1407
Chris@127 1408 paint.save();
Chris@183 1409
Chris@183 1410 bool translucent = !areLayerColoursSignificant();
Chris@183 1411
Chris@183 1412 if (translucent) {
Chris@183 1413 paint.setBrush(QColor(150, 150, 255, 80));
Chris@183 1414 } else {
Chris@183 1415 paint.setBrush(Qt::NoBrush);
Chris@183 1416 }
Chris@127 1417
Chris@127 1418 int sampleRate = getModelsSampleRate();
Chris@127 1419
Chris@127 1420 QPoint localPos;
Chris@127 1421 long illuminateFrame = -1;
Chris@127 1422 bool closeToLeft, closeToRight;
Chris@127 1423
Chris@127 1424 if (shouldIlluminateLocalSelection(localPos, closeToLeft, closeToRight)) {
Chris@127 1425 illuminateFrame = getFrameForX(localPos.x());
Chris@127 1426 }
Chris@127 1427
Chris@127 1428 const QFontMetrics &metrics = paint.fontMetrics();
Chris@127 1429
Chris@127 1430 for (MultiSelection::SelectionList::iterator i = selections.begin();
Chris@127 1431 i != selections.end(); ++i) {
Chris@127 1432
Chris@127 1433 int p0 = getXForFrame(i->getStartFrame());
Chris@127 1434 int p1 = getXForFrame(i->getEndFrame());
Chris@127 1435
Chris@127 1436 if (p1 < 0 || p0 > width()) continue;
Chris@127 1437
Chris@127 1438 #ifdef DEBUG_VIEW_WIDGET_PAINT
Chris@127 1439 std::cerr << "View::drawSelections: " << p0 << ",-1 [" << (p1-p0) << "x" << (height()+1) << "]" << std::endl;
Chris@127 1440 #endif
Chris@127 1441
Chris@127 1442 bool illuminateThis =
Chris@127 1443 (illuminateFrame >= 0 && i->contains(illuminateFrame));
Chris@127 1444
Chris@127 1445 paint.setPen(QColor(150, 150, 255));
Chris@183 1446
Chris@183 1447 if (translucent && shouldLabelSelections()) {
Chris@183 1448 paint.drawRect(p0, -1, p1 - p0, height() + 1);
Chris@183 1449 } else {
Chris@183 1450 // Make the top & bottom lines of the box visible if we
Chris@183 1451 // are lacking some of the other visual cues. There's no
Chris@183 1452 // particular logic to this, it's just a question of what
Chris@183 1453 // I happen to think looks nice.
Chris@183 1454 paint.drawRect(p0, 0, p1 - p0, height() - 1);
Chris@183 1455 }
Chris@127 1456
Chris@127 1457 if (illuminateThis) {
Chris@127 1458 paint.save();
Chris@127 1459 if (hasLightBackground()) {
Chris@127 1460 paint.setPen(QPen(Qt::black, 2));
Chris@127 1461 } else {
Chris@127 1462 paint.setPen(QPen(Qt::white, 2));
Chris@127 1463 }
Chris@127 1464 if (closeToLeft) {
Chris@127 1465 paint.drawLine(p0, 1, p1, 1);
Chris@127 1466 paint.drawLine(p0, 0, p0, height());
Chris@127 1467 paint.drawLine(p0, height() - 1, p1, height() - 1);
Chris@127 1468 } else if (closeToRight) {
Chris@127 1469 paint.drawLine(p0, 1, p1, 1);
Chris@127 1470 paint.drawLine(p1, 0, p1, height());
Chris@127 1471 paint.drawLine(p0, height() - 1, p1, height() - 1);
Chris@127 1472 } else {
Chris@127 1473 paint.setBrush(Qt::NoBrush);
Chris@127 1474 paint.drawRect(p0, 1, p1 - p0, height() - 2);
Chris@127 1475 }
Chris@127 1476 paint.restore();
Chris@127 1477 }
Chris@127 1478
Chris@127 1479 if (sampleRate && shouldLabelSelections() && m_manager &&
Chris@180 1480 m_manager->getOverlayMode() == ViewManager::AllOverlays) {
Chris@127 1481
Chris@127 1482 QString startText = QString("%1 / %2")
Chris@127 1483 .arg(QString::fromStdString
Chris@127 1484 (RealTime::frame2RealTime
Chris@127 1485 (i->getStartFrame(), sampleRate).toText(true)))
Chris@127 1486 .arg(i->getStartFrame());
Chris@127 1487
Chris@127 1488 QString endText = QString(" %1 / %2")
Chris@127 1489 .arg(QString::fromStdString
Chris@127 1490 (RealTime::frame2RealTime
Chris@127 1491 (i->getEndFrame(), sampleRate).toText(true)))
Chris@127 1492 .arg(i->getEndFrame());
Chris@127 1493
Chris@127 1494 QString durationText = QString("(%1 / %2) ")
Chris@127 1495 .arg(QString::fromStdString
Chris@127 1496 (RealTime::frame2RealTime
Chris@127 1497 (i->getEndFrame() - i->getStartFrame(), sampleRate)
Chris@127 1498 .toText(true)))
Chris@127 1499 .arg(i->getEndFrame() - i->getStartFrame());
Chris@127 1500
Chris@127 1501 int sw = metrics.width(startText),
Chris@127 1502 ew = metrics.width(endText),
Chris@127 1503 dw = metrics.width(durationText);
Chris@127 1504
Chris@127 1505 int sy = metrics.ascent() + metrics.height() + 4;
Chris@127 1506 int ey = sy;
Chris@127 1507 int dy = sy + metrics.height();
Chris@127 1508
Chris@127 1509 int sx = p0 + 2;
Chris@127 1510 int ex = sx;
Chris@127 1511 int dx = sx;
Chris@127 1512
Chris@127 1513 if (sw + ew > (p1 - p0)) {
Chris@127 1514 ey += metrics.height();
Chris@127 1515 dy += metrics.height();
Chris@127 1516 }
Chris@127 1517
Chris@127 1518 if (ew < (p1 - p0)) {
Chris@127 1519 ex = p1 - 2 - ew;
Chris@127 1520 }
Chris@127 1521
Chris@127 1522 if (dw < (p1 - p0)) {
Chris@127 1523 dx = p1 - 2 - dw;
Chris@127 1524 }
Chris@127 1525
Chris@127 1526 paint.drawText(sx, sy, startText);
Chris@127 1527 paint.drawText(ex, ey, endText);
Chris@127 1528 paint.drawText(dx, dy, durationText);
Chris@127 1529 }
Chris@127 1530 }
Chris@127 1531
Chris@127 1532 paint.restore();
Chris@127 1533 }
Chris@127 1534
Chris@127 1535 QString
Chris@127 1536 View::toXmlString(QString indent, QString extraAttributes) const
Chris@127 1537 {
Chris@127 1538 QString s;
Chris@127 1539
Chris@127 1540 s += indent;
Chris@127 1541
Chris@127 1542 s += QString("<view "
Chris@127 1543 "centre=\"%1\" "
Chris@127 1544 "zoom=\"%2\" "
Chris@127 1545 "followPan=\"%3\" "
Chris@127 1546 "followZoom=\"%4\" "
Chris@127 1547 "tracking=\"%5\" "
Chris@127 1548 "light=\"%6\" %7>\n")
Chris@127 1549 .arg(m_centreFrame)
Chris@127 1550 .arg(m_zoomLevel)
Chris@127 1551 .arg(m_followPan)
Chris@127 1552 .arg(m_followZoom)
Chris@127 1553 .arg(m_followPlay == PlaybackScrollContinuous ? "scroll" :
Chris@127 1554 m_followPlay == PlaybackScrollPage ? "page" : "ignore")
Chris@127 1555 .arg(m_lightBackground)
Chris@127 1556 .arg(extraAttributes);
Chris@127 1557
Chris@127 1558 for (size_t i = 0; i < m_layers.size(); ++i) {
Chris@127 1559 s += m_layers[i]->toXmlString(indent + " ");
Chris@127 1560 }
Chris@127 1561
Chris@127 1562 s += indent + "</view>\n";
Chris@127 1563
Chris@127 1564 return s;
Chris@127 1565 }
Chris@127 1566
Chris@127 1567 ViewPropertyContainer::ViewPropertyContainer(View *v) :
Chris@127 1568 m_v(v)
Chris@127 1569 {
Chris@127 1570 connect(m_v, SIGNAL(propertyChanged(PropertyContainer::PropertyName)),
Chris@127 1571 this, SIGNAL(propertyChanged(PropertyContainer::PropertyName)));
Chris@127 1572 }
Chris@127 1573
Chris@127 1574 #ifdef INCLUDE_MOCFILES
Chris@127 1575 #include "View.moc.cpp"
Chris@127 1576 #endif
Chris@127 1577