annotate view/View.cpp @ 378:22b72f0f6a4e

* More work to abstract out interactive components used in the data library, so that it does not need to depend on QtGui.
author Chris Cannam
date Fri, 14 Mar 2008 17:14:21 +0000
parents f1e6204c1f17
children 06b5f110c5d2
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@278 21 #include "base/Pitch.h"
Chris@338 22 #include "base/Preferences.h"
Chris@127 23
Chris@301 24 #include "layer/TimeRulerLayer.h"
Chris@287 25 #include "layer/SingleColourLayer.h"
Chris@301 26 #include "data/model/PowerOfSqrtTwoZoomConstraint.h"
Chris@301 27 #include "data/model/RangeSummarisableTimeValueModel.h"
Chris@127 28
Chris@127 29 #include <QPainter>
Chris@127 30 #include <QPaintEvent>
Chris@127 31 #include <QRect>
Chris@127 32 #include <QApplication>
Chris@226 33 #include <QProgressDialog>
Chris@316 34 #include <QTextStream>
Chris@338 35 #include <QFont>
Chris@127 36
Chris@127 37 #include <iostream>
Chris@127 38 #include <cassert>
Chris@127 39 #include <math.h>
Chris@127 40
Chris@127 41 //#define DEBUG_VIEW_WIDGET_PAINT 1
Chris@127 42
Chris@127 43 using std::cerr;
Chris@127 44 using std::endl;
Chris@127 45
Chris@127 46 View::View(QWidget *w, bool showProgress) :
Chris@127 47 QFrame(w),
Chris@127 48 m_centreFrame(0),
Chris@127 49 m_zoomLevel(1024),
Chris@127 50 m_followPan(true),
Chris@127 51 m_followZoom(true),
Chris@127 52 m_followPlay(PlaybackScrollPage),
Chris@153 53 m_playPointerFrame(0),
Chris@127 54 m_showProgress(showProgress),
Chris@127 55 m_cache(0),
Chris@127 56 m_cacheCentreFrame(0),
Chris@127 57 m_cacheZoomLevel(1024),
Chris@127 58 m_selectionCached(false),
Chris@127 59 m_deleting(false),
Chris@127 60 m_haveSelectedLayer(false),
Chris@127 61 m_manager(0),
Chris@127 62 m_propertyContainer(new ViewPropertyContainer(this))
Chris@127 63 {
Chris@127 64 }
Chris@127 65
Chris@127 66 View::~View()
Chris@127 67 {
Chris@127 68 // std::cerr << "View::~View(" << this << ")" << std::endl;
Chris@127 69
Chris@127 70 m_deleting = true;
Chris@127 71 delete m_propertyContainer;
Chris@127 72 }
Chris@127 73
Chris@127 74 PropertyContainer::PropertyList
Chris@127 75 View::getProperties() const
Chris@127 76 {
Chris@127 77 PropertyContainer::PropertyList list;
Chris@127 78 list.push_back("Global Scroll");
Chris@127 79 list.push_back("Global Zoom");
Chris@127 80 list.push_back("Follow Playback");
Chris@127 81 return list;
Chris@127 82 }
Chris@127 83
Chris@127 84 QString
Chris@127 85 View::getPropertyLabel(const PropertyName &pn) const
Chris@127 86 {
Chris@127 87 if (pn == "Global Scroll") return tr("Global Scroll");
Chris@127 88 if (pn == "Global Zoom") return tr("Global Zoom");
Chris@127 89 if (pn == "Follow Playback") return tr("Follow Playback");
Chris@127 90 return "";
Chris@127 91 }
Chris@127 92
Chris@127 93 PropertyContainer::PropertyType
Chris@127 94 View::getPropertyType(const PropertyContainer::PropertyName &name) const
Chris@127 95 {
Chris@127 96 if (name == "Global Scroll") return PropertyContainer::ToggleProperty;
Chris@127 97 if (name == "Global Zoom") return PropertyContainer::ToggleProperty;
Chris@127 98 if (name == "Follow Playback") return PropertyContainer::ValueProperty;
Chris@127 99 return PropertyContainer::InvalidProperty;
Chris@127 100 }
Chris@127 101
Chris@127 102 int
Chris@127 103 View::getPropertyRangeAndValue(const PropertyContainer::PropertyName &name,
Chris@216 104 int *min, int *max, int *deflt) const
Chris@127 105 {
Chris@216 106 if (deflt) *deflt = 1;
Chris@127 107 if (name == "Global Scroll") return m_followPan;
Chris@127 108 if (name == "Global Zoom") return m_followZoom;
Chris@127 109 if (name == "Follow Playback") {
Chris@127 110 if (min) *min = 0;
Chris@127 111 if (max) *max = 2;
Chris@216 112 if (deflt) *deflt = int(PlaybackScrollPage);
Chris@127 113 return int(m_followPlay);
Chris@127 114 }
Chris@127 115 if (min) *min = 0;
Chris@127 116 if (max) *max = 0;
Chris@216 117 if (deflt) *deflt = 0;
Chris@127 118 return 0;
Chris@127 119 }
Chris@127 120
Chris@127 121 QString
Chris@127 122 View::getPropertyValueLabel(const PropertyContainer::PropertyName &name,
Chris@127 123 int value) const
Chris@127 124 {
Chris@127 125 if (name == "Follow Playback") {
Chris@127 126 switch (value) {
Chris@127 127 default:
Chris@127 128 case 0: return tr("Scroll");
Chris@127 129 case 1: return tr("Page");
Chris@127 130 case 2: return tr("Off");
Chris@127 131 }
Chris@127 132 }
Chris@127 133 return tr("<unknown>");
Chris@127 134 }
Chris@127 135
Chris@127 136 void
Chris@127 137 View::setProperty(const PropertyContainer::PropertyName &name, int value)
Chris@127 138 {
Chris@127 139 if (name == "Global Scroll") {
Chris@127 140 setFollowGlobalPan(value != 0);
Chris@127 141 } else if (name == "Global Zoom") {
Chris@127 142 setFollowGlobalZoom(value != 0);
Chris@127 143 } else if (name == "Follow Playback") {
Chris@127 144 switch (value) {
Chris@127 145 default:
Chris@127 146 case 0: setPlaybackFollow(PlaybackScrollContinuous); break;
Chris@127 147 case 1: setPlaybackFollow(PlaybackScrollPage); break;
Chris@127 148 case 2: setPlaybackFollow(PlaybackIgnore); break;
Chris@127 149 }
Chris@127 150 }
Chris@127 151 }
Chris@127 152
Chris@127 153 size_t
Chris@127 154 View::getPropertyContainerCount() const
Chris@127 155 {
Chris@127 156 return m_layers.size() + 1; // the 1 is for me
Chris@127 157 }
Chris@127 158
Chris@127 159 const PropertyContainer *
Chris@127 160 View::getPropertyContainer(size_t i) const
Chris@127 161 {
Chris@127 162 return (const PropertyContainer *)(((View *)this)->
Chris@127 163 getPropertyContainer(i));
Chris@127 164 }
Chris@127 165
Chris@127 166 PropertyContainer *
Chris@127 167 View::getPropertyContainer(size_t i)
Chris@127 168 {
Chris@127 169 if (i == 0) return m_propertyContainer;
Chris@127 170 return m_layers[i-1];
Chris@127 171 }
Chris@127 172
Chris@127 173 bool
Chris@127 174 View::getValueExtents(QString unit, float &min, float &max, bool &log) const
Chris@127 175 {
Chris@127 176 bool have = false;
Chris@127 177
Chris@127 178 for (LayerList::const_iterator i = m_layers.begin();
Chris@127 179 i != m_layers.end(); ++i) {
Chris@127 180
Chris@127 181 QString layerUnit;
Chris@127 182 float layerMin = 0.0, layerMax = 0.0;
Chris@127 183 float displayMin = 0.0, displayMax = 0.0;
Chris@127 184 bool layerLog = false;
Chris@127 185
Chris@127 186 if ((*i)->getValueExtents(layerMin, layerMax, layerLog, layerUnit) &&
Chris@127 187 layerUnit.toLower() == unit.toLower()) {
Chris@127 188
Chris@127 189 if ((*i)->getDisplayExtents(displayMin, displayMax)) {
Chris@127 190
Chris@127 191 min = displayMin;
Chris@127 192 max = displayMax;
Chris@127 193 log = layerLog;
Chris@127 194 have = true;
Chris@127 195 break;
Chris@127 196
Chris@127 197 } else {
Chris@127 198
Chris@127 199 if (!have || layerMin < min) min = layerMin;
Chris@127 200 if (!have || layerMax > max) max = layerMax;
Chris@127 201 if (layerLog) log = true;
Chris@127 202 have = true;
Chris@127 203 }
Chris@127 204 }
Chris@127 205 }
Chris@127 206
Chris@127 207 return have;
Chris@127 208 }
Chris@127 209
Chris@127 210 int
Chris@127 211 View::getTextLabelHeight(const Layer *layer, QPainter &paint) const
Chris@127 212 {
Chris@127 213 std::map<int, Layer *> sortedLayers;
Chris@127 214
Chris@127 215 for (LayerList::const_iterator i = m_layers.begin();
Chris@127 216 i != m_layers.end(); ++i) {
Chris@127 217 if ((*i)->needsTextLabelHeight()) {
Chris@127 218 sortedLayers[getObjectExportId(*i)] = *i;
Chris@127 219 }
Chris@127 220 }
Chris@127 221
Chris@127 222 int y = 15 + paint.fontMetrics().ascent();
Chris@127 223
Chris@127 224 for (std::map<int, Layer *>::const_iterator i = sortedLayers.begin();
Chris@127 225 i != sortedLayers.end(); ++i) {
Chris@127 226 if (i->second == layer) return y;
Chris@127 227 y += paint.fontMetrics().height();
Chris@127 228 }
Chris@127 229
Chris@127 230 return y;
Chris@127 231 }
Chris@127 232
Chris@127 233 void
Chris@127 234 View::propertyContainerSelected(View *client, PropertyContainer *pc)
Chris@127 235 {
Chris@127 236 if (client != this) return;
Chris@127 237
Chris@127 238 if (pc == m_propertyContainer) {
Chris@127 239 if (m_haveSelectedLayer) {
Chris@127 240 m_haveSelectedLayer = false;
Chris@127 241 update();
Chris@127 242 }
Chris@127 243 return;
Chris@127 244 }
Chris@127 245
Chris@127 246 delete m_cache;
Chris@127 247 m_cache = 0;
Chris@127 248
Chris@127 249 Layer *selectedLayer = 0;
Chris@127 250
Chris@127 251 for (LayerList::iterator i = m_layers.begin(); i != m_layers.end(); ++i) {
Chris@127 252 if (*i == pc) {
Chris@127 253 selectedLayer = *i;
Chris@127 254 m_layers.erase(i);
Chris@127 255 break;
Chris@127 256 }
Chris@127 257 }
Chris@127 258
Chris@127 259 if (selectedLayer) {
Chris@127 260 m_haveSelectedLayer = true;
Chris@127 261 m_layers.push_back(selectedLayer);
Chris@127 262 update();
Chris@127 263 } else {
Chris@127 264 m_haveSelectedLayer = false;
Chris@127 265 }
Chris@298 266
Chris@298 267 emit propertyContainerSelected(pc);
Chris@127 268 }
Chris@127 269
Chris@127 270 void
Chris@127 271 View::toolModeChanged()
Chris@127 272 {
Chris@127 273 // std::cerr << "View::toolModeChanged(" << m_manager->getToolMode() << ")" << std::endl;
Chris@127 274 }
Chris@127 275
Chris@133 276 void
Chris@133 277 View::overlayModeChanged()
Chris@133 278 {
Chris@206 279 delete m_cache;
Chris@206 280 m_cache = 0;
Chris@133 281 update();
Chris@133 282 }
Chris@133 283
Chris@133 284 void
Chris@133 285 View::zoomWheelsEnabledChanged()
Chris@133 286 {
Chris@133 287 // subclass might override this
Chris@133 288 }
Chris@133 289
Chris@127 290 long
Chris@127 291 View::getStartFrame() const
Chris@127 292 {
Chris@313 293 return getFrameForX(0);
Chris@127 294 }
Chris@127 295
Chris@127 296 size_t
Chris@127 297 View::getEndFrame() const
Chris@127 298 {
Chris@127 299 return getFrameForX(width()) - 1;
Chris@127 300 }
Chris@127 301
Chris@127 302 void
Chris@127 303 View::setStartFrame(long f)
Chris@127 304 {
Chris@127 305 setCentreFrame(f + m_zoomLevel * (width() / 2));
Chris@127 306 }
Chris@127 307
Chris@127 308 bool
Chris@127 309 View::setCentreFrame(size_t f, bool e)
Chris@127 310 {
Chris@127 311 bool changeVisible = false;
Chris@127 312
Chris@127 313 if (m_centreFrame != f) {
Chris@127 314
Chris@127 315 int formerPixel = m_centreFrame / m_zoomLevel;
Chris@127 316
Chris@127 317 m_centreFrame = f;
Chris@127 318
Chris@127 319 int newPixel = m_centreFrame / m_zoomLevel;
Chris@127 320
Chris@127 321 if (newPixel != formerPixel) {
Chris@127 322
Chris@127 323 #ifdef DEBUG_VIEW_WIDGET_PAINT
Chris@127 324 std::cout << "View(" << this << ")::setCentreFrame: newPixel " << newPixel << ", formerPixel " << formerPixel << std::endl;
Chris@127 325 #endif
Chris@127 326 update();
Chris@127 327
Chris@127 328 changeVisible = true;
Chris@127 329 }
Chris@127 330
Chris@333 331 if (e) {
Chris@333 332 size_t rf = alignToReference(f);
Chris@355 333 #ifdef DEBUG_VIEW_WIDGET_PAINT
Chris@333 334 std::cerr << "View[" << this << "]::setCentreFrame(" << f
Chris@333 335 << "): emitting centreFrameChanged("
Chris@333 336 << rf << ")" << std::endl;
Chris@355 337 #endif
Chris@333 338 emit centreFrameChanged(rf, m_followPan, m_followPlay);
Chris@333 339 }
Chris@127 340 }
Chris@127 341
Chris@127 342 return changeVisible;
Chris@127 343 }
Chris@127 344
Chris@127 345 int
Chris@127 346 View::getXForFrame(long frame) const
Chris@127 347 {
Chris@127 348 return (frame - getStartFrame()) / m_zoomLevel;
Chris@127 349 }
Chris@127 350
Chris@127 351 long
Chris@127 352 View::getFrameForX(int x) const
Chris@127 353 {
Chris@313 354 long z = (long)m_zoomLevel;
Chris@313 355 long frame = m_centreFrame - (width()/2) * z;
Chris@354 356
Chris@355 357 #ifdef DEBUG_VIEW_WIDGET_PAINT
Chris@354 358 std::cerr << "View::getFrameForX(" << x << "): z = " << z << ", m_centreFrame = " << m_centreFrame << ", width() = " << width() << ", frame = " << frame << std::endl;
Chris@355 359 #endif
Chris@354 360
Chris@313 361 frame = (frame / z) * z; // this is start frame
Chris@313 362 return frame + x * z;
Chris@127 363 }
Chris@127 364
Chris@127 365 float
Chris@127 366 View::getYForFrequency(float frequency,
Chris@127 367 float minf,
Chris@127 368 float maxf,
Chris@127 369 bool logarithmic) const
Chris@127 370 {
Chris@127 371 int h = height();
Chris@127 372
Chris@127 373 if (logarithmic) {
Chris@127 374
Chris@127 375 static float lastminf = 0.0, lastmaxf = 0.0;
Chris@127 376 static float logminf = 0.0, logmaxf = 0.0;
Chris@127 377
Chris@127 378 if (lastminf != minf) {
Chris@127 379 lastminf = (minf == 0.0 ? 1.0 : minf);
Chris@127 380 logminf = log10f(minf);
Chris@127 381 }
Chris@127 382 if (lastmaxf != maxf) {
Chris@127 383 lastmaxf = (maxf < lastminf ? lastminf : maxf);
Chris@127 384 logmaxf = log10f(maxf);
Chris@127 385 }
Chris@127 386
Chris@127 387 if (logminf == logmaxf) return 0;
Chris@127 388 return h - (h * (log10f(frequency) - logminf)) / (logmaxf - logminf);
Chris@127 389
Chris@127 390 } else {
Chris@127 391
Chris@127 392 if (minf == maxf) return 0;
Chris@127 393 return h - (h * (frequency - minf)) / (maxf - minf);
Chris@127 394 }
Chris@127 395 }
Chris@127 396
Chris@127 397 float
Chris@127 398 View::getFrequencyForY(int y,
Chris@127 399 float minf,
Chris@127 400 float maxf,
Chris@127 401 bool logarithmic) const
Chris@127 402 {
Chris@127 403 int h = height();
Chris@127 404
Chris@127 405 if (logarithmic) {
Chris@127 406
Chris@127 407 static float lastminf = 0.0, lastmaxf = 0.0;
Chris@127 408 static float logminf = 0.0, logmaxf = 0.0;
Chris@127 409
Chris@127 410 if (lastminf != minf) {
Chris@127 411 lastminf = (minf == 0.0 ? 1.0 : minf);
Chris@127 412 logminf = log10f(minf);
Chris@127 413 }
Chris@127 414 if (lastmaxf != maxf) {
Chris@127 415 lastmaxf = (maxf < lastminf ? lastminf : maxf);
Chris@127 416 logmaxf = log10f(maxf);
Chris@127 417 }
Chris@127 418
Chris@127 419 if (logminf == logmaxf) return 0;
Chris@127 420 return pow(10.f, logminf + ((logmaxf - logminf) * (h - y)) / h);
Chris@127 421
Chris@127 422 } else {
Chris@127 423
Chris@127 424 if (minf == maxf) return 0;
Chris@127 425 return minf + ((h - y) * (maxf - minf)) / h;
Chris@127 426 }
Chris@127 427 }
Chris@127 428
Chris@127 429 int
Chris@127 430 View::getZoomLevel() const
Chris@127 431 {
Chris@127 432 #ifdef DEBUG_VIEW_WIDGET_PAINT
Chris@244 433 // std::cout << "zoom level: " << m_zoomLevel << std::endl;
Chris@127 434 #endif
Chris@127 435 return m_zoomLevel;
Chris@127 436 }
Chris@127 437
Chris@127 438 void
Chris@127 439 View::setZoomLevel(size_t z)
Chris@127 440 {
Chris@127 441 if (m_zoomLevel != int(z)) {
Chris@127 442 m_zoomLevel = z;
Chris@222 443 emit zoomLevelChanged(z, m_followZoom);
Chris@127 444 update();
Chris@127 445 }
Chris@127 446 }
Chris@127 447
Chris@224 448 bool
Chris@224 449 View::hasLightBackground() const
Chris@224 450 {
Chris@287 451 bool darkPalette = false;
Chris@292 452 if (m_manager) darkPalette = m_manager->getGlobalDarkBackground();
Chris@287 453
Chris@287 454 Layer::ColourSignificance maxSignificance = Layer::ColourAbsent;
Chris@287 455 bool mostSignificantHasDarkBackground = false;
Chris@287 456
Chris@224 457 for (LayerList::const_iterator i = m_layers.begin();
Chris@224 458 i != m_layers.end(); ++i) {
Chris@287 459
Chris@287 460 Layer::ColourSignificance s = (*i)->getLayerColourSignificance();
Chris@287 461 bool light = (*i)->hasLightBackground();
Chris@287 462
Chris@287 463 if (int(s) > int(maxSignificance)) {
Chris@287 464 maxSignificance = s;
Chris@287 465 mostSignificantHasDarkBackground = !light;
Chris@287 466 } else if (s == maxSignificance && !light) {
Chris@287 467 mostSignificantHasDarkBackground = true;
Chris@287 468 }
Chris@224 469 }
Chris@287 470
Chris@287 471 if (int(maxSignificance) >= int(Layer::ColourAndBackgroundSignificant)) {
Chris@287 472 return !mostSignificantHasDarkBackground;
Chris@287 473 } else {
Chris@287 474 return !darkPalette;
Chris@287 475 }
Chris@287 476 }
Chris@287 477
Chris@287 478 QColor
Chris@287 479 View::getBackground() const
Chris@287 480 {
Chris@287 481 bool light = hasLightBackground();
Chris@287 482
Chris@287 483 QColor widgetbg = palette().window().color();
Chris@287 484 bool widgetLight =
Chris@287 485 (widgetbg.red() + widgetbg.green() + widgetbg.blue()) > 384;
Chris@287 486
Chris@296 487 if (widgetLight == light) {
Chris@296 488 if (widgetLight) {
Chris@296 489 return widgetbg.light();
Chris@296 490 } else {
Chris@296 491 return widgetbg.dark();
Chris@296 492 }
Chris@296 493 }
Chris@287 494 else if (light) return Qt::white;
Chris@287 495 else return Qt::black;
Chris@287 496 }
Chris@287 497
Chris@287 498 QColor
Chris@287 499 View::getForeground() const
Chris@287 500 {
Chris@287 501 bool light = hasLightBackground();
Chris@287 502
Chris@287 503 QColor widgetfg = palette().text().color();
Chris@287 504 bool widgetLight =
Chris@287 505 (widgetfg.red() + widgetfg.green() + widgetfg.blue()) > 384;
Chris@287 506
Chris@287 507 if (widgetLight != light) return widgetfg;
Chris@287 508 else if (light) return Qt::black;
Chris@287 509 else return Qt::white;
Chris@224 510 }
Chris@224 511
Chris@127 512 View::LayerProgressBar::LayerProgressBar(QWidget *parent) :
Chris@127 513 QProgressBar(parent)
Chris@127 514 {
Chris@127 515 }
Chris@127 516
Chris@127 517 void
Chris@127 518 View::addLayer(Layer *layer)
Chris@127 519 {
Chris@127 520 delete m_cache;
Chris@127 521 m_cache = 0;
Chris@127 522
Chris@287 523 SingleColourLayer *scl = dynamic_cast<SingleColourLayer *>(layer);
Chris@287 524 if (scl) scl->setDefaultColourFor(this);
Chris@287 525
Chris@127 526 m_layers.push_back(layer);
Chris@127 527
Chris@127 528 m_progressBars[layer] = new LayerProgressBar(this);
Chris@127 529 m_progressBars[layer]->setMinimum(0);
Chris@127 530 m_progressBars[layer]->setMaximum(100);
Chris@127 531 m_progressBars[layer]->setMinimumWidth(80);
Chris@339 532
Chris@338 533 QFont f(m_progressBars[layer]->font());
Chris@339 534 int fs = Preferences::getInstance()->getViewFontSize();
Chris@339 535 f.setPointSize(std::min(fs, int(ceil(fs * 0.85))));
Chris@339 536
Chris@338 537 m_progressBars[layer]->setFont(f);
Chris@127 538 m_progressBars[layer]->hide();
Chris@127 539
Chris@127 540 connect(layer, SIGNAL(layerParametersChanged()),
Chris@127 541 this, SLOT(layerParametersChanged()));
Chris@197 542 connect(layer, SIGNAL(layerParameterRangesChanged()),
Chris@197 543 this, SLOT(layerParameterRangesChanged()));
Chris@268 544 connect(layer, SIGNAL(layerMeasurementRectsChanged()),
Chris@268 545 this, SLOT(layerMeasurementRectsChanged()));
Chris@127 546 connect(layer, SIGNAL(layerNameChanged()),
Chris@127 547 this, SLOT(layerNameChanged()));
Chris@127 548 connect(layer, SIGNAL(modelChanged()),
Chris@127 549 this, SLOT(modelChanged()));
Chris@127 550 connect(layer, SIGNAL(modelCompletionChanged()),
Chris@127 551 this, SLOT(modelCompletionChanged()));
Chris@320 552 connect(layer, SIGNAL(modelAlignmentCompletionChanged()),
Chris@320 553 this, SLOT(modelAlignmentCompletionChanged()));
Chris@127 554 connect(layer, SIGNAL(modelChanged(size_t, size_t)),
Chris@127 555 this, SLOT(modelChanged(size_t, size_t)));
Chris@127 556 connect(layer, SIGNAL(modelReplaced()),
Chris@127 557 this, SLOT(modelReplaced()));
Chris@127 558
Chris@127 559 update();
Chris@127 560
Chris@127 561 emit propertyContainerAdded(layer);
Chris@127 562 }
Chris@127 563
Chris@127 564 void
Chris@127 565 View::removeLayer(Layer *layer)
Chris@127 566 {
Chris@127 567 if (m_deleting) {
Chris@127 568 return;
Chris@127 569 }
Chris@127 570
Chris@127 571 delete m_cache;
Chris@127 572 m_cache = 0;
Chris@127 573
Chris@127 574 for (LayerList::iterator i = m_layers.begin(); i != m_layers.end(); ++i) {
Chris@127 575 if (*i == layer) {
Chris@127 576 m_layers.erase(i);
Chris@127 577 if (m_progressBars.find(layer) != m_progressBars.end()) {
Chris@127 578 delete m_progressBars[layer];
Chris@127 579 m_progressBars.erase(layer);
Chris@127 580 }
Chris@127 581 break;
Chris@127 582 }
Chris@127 583 }
Chris@127 584
Chris@197 585 disconnect(layer, SIGNAL(layerParametersChanged()),
Chris@197 586 this, SLOT(layerParametersChanged()));
Chris@197 587 disconnect(layer, SIGNAL(layerParameterRangesChanged()),
Chris@197 588 this, SLOT(layerParameterRangesChanged()));
Chris@197 589 disconnect(layer, SIGNAL(layerNameChanged()),
Chris@197 590 this, SLOT(layerNameChanged()));
Chris@197 591 disconnect(layer, SIGNAL(modelChanged()),
Chris@197 592 this, SLOT(modelChanged()));
Chris@197 593 disconnect(layer, SIGNAL(modelCompletionChanged()),
Chris@197 594 this, SLOT(modelCompletionChanged()));
Chris@320 595 disconnect(layer, SIGNAL(modelAlignmentCompletionChanged()),
Chris@320 596 this, SLOT(modelAlignmentCompletionChanged()));
Chris@197 597 disconnect(layer, SIGNAL(modelChanged(size_t, size_t)),
Chris@197 598 this, SLOT(modelChanged(size_t, size_t)));
Chris@197 599 disconnect(layer, SIGNAL(modelReplaced()),
Chris@197 600 this, SLOT(modelReplaced()));
Chris@197 601
Chris@127 602 update();
Chris@127 603
Chris@127 604 emit propertyContainerRemoved(layer);
Chris@127 605 }
Chris@127 606
Chris@127 607 Layer *
Chris@127 608 View::getSelectedLayer()
Chris@127 609 {
Chris@127 610 if (m_haveSelectedLayer && !m_layers.empty()) {
Chris@127 611 return getLayer(getLayerCount() - 1);
Chris@127 612 } else {
Chris@127 613 return 0;
Chris@127 614 }
Chris@127 615 }
Chris@127 616
Chris@127 617 const Layer *
Chris@127 618 View::getSelectedLayer() const
Chris@127 619 {
Chris@127 620 return const_cast<const Layer *>(const_cast<View *>(this)->getSelectedLayer());
Chris@127 621 }
Chris@127 622
Chris@127 623 void
Chris@127 624 View::setViewManager(ViewManager *manager)
Chris@127 625 {
Chris@127 626 if (m_manager) {
Chris@211 627 m_manager->disconnect(this, SLOT(globalCentreFrameChanged(unsigned long)));
Chris@211 628 m_manager->disconnect(this, SLOT(viewCentreFrameChanged(View *, unsigned long)));
Chris@211 629 m_manager->disconnect(this, SLOT(viewManagerPlaybackFrameChanged(unsigned long)));
Chris@222 630 m_manager->disconnect(this, SLOT(viewZoomLevelChanged(View *, unsigned long, bool)));
Chris@211 631 m_manager->disconnect(this, SLOT(toolModeChanged()));
Chris@211 632 m_manager->disconnect(this, SLOT(selectionChanged()));
Chris@211 633 m_manager->disconnect(this, SLOT(overlayModeChanged()));
Chris@211 634 m_manager->disconnect(this, SLOT(zoomWheelsEnabledChanged()));
Chris@222 635 disconnect(m_manager, SLOT(viewCentreFrameChanged(unsigned long, bool, PlaybackFollowMode)));
Chris@222 636 disconnect(m_manager, SLOT(zoomLevelChanged(unsigned long, bool)));
Chris@127 637 }
Chris@127 638
Chris@127 639 m_manager = manager;
Chris@127 640
Chris@211 641 connect(m_manager, SIGNAL(globalCentreFrameChanged(unsigned long)),
Chris@211 642 this, SLOT(globalCentreFrameChanged(unsigned long)));
Chris@213 643 connect(m_manager, SIGNAL(viewCentreFrameChanged(View *, unsigned long)),
Chris@211 644 this, SLOT(viewCentreFrameChanged(View *, unsigned long)));
Chris@127 645 connect(m_manager, SIGNAL(playbackFrameChanged(unsigned long)),
Chris@127 646 this, SLOT(viewManagerPlaybackFrameChanged(unsigned long)));
Chris@211 647
Chris@222 648 connect(m_manager, SIGNAL(viewZoomLevelChanged(View *, unsigned long, bool)),
Chris@222 649 this, SLOT(viewZoomLevelChanged(View *, unsigned long, bool)));
Chris@211 650
Chris@127 651 connect(m_manager, SIGNAL(toolModeChanged()),
Chris@127 652 this, SLOT(toolModeChanged()));
Chris@127 653 connect(m_manager, SIGNAL(selectionChanged()),
Chris@127 654 this, SLOT(selectionChanged()));
Chris@127 655 connect(m_manager, SIGNAL(inProgressSelectionChanged()),
Chris@127 656 this, SLOT(selectionChanged()));
Chris@127 657 connect(m_manager, SIGNAL(overlayModeChanged()),
Chris@133 658 this, SLOT(overlayModeChanged()));
Chris@133 659 connect(m_manager, SIGNAL(zoomWheelsEnabledChanged()),
Chris@133 660 this, SLOT(zoomWheelsEnabledChanged()));
Chris@127 661
Chris@211 662 connect(this, SIGNAL(centreFrameChanged(unsigned long, bool,
Chris@211 663 PlaybackFollowMode)),
Chris@211 664 m_manager, SLOT(viewCentreFrameChanged(unsigned long, bool,
Chris@211 665 PlaybackFollowMode)));
Chris@211 666
Chris@222 667 connect(this, SIGNAL(zoomLevelChanged(unsigned long, bool)),
Chris@222 668 m_manager, SLOT(viewZoomLevelChanged(unsigned long, bool)));
Chris@127 669
Chris@364 670 if (m_followPlay == PlaybackScrollPage) {
Chris@364 671 // std::cerr << "View::setViewManager: setting centre frame to global centre frame: " << m_manager->getGlobalCentreFrame() << std::endl;
Chris@364 672 setCentreFrame(m_manager->getGlobalCentreFrame(), false);
Chris@364 673 } else if (m_followPlay == PlaybackScrollContinuous) {
Chris@236 674 // std::cerr << "View::setViewManager: setting centre frame to playback frame: " << m_manager->getPlaybackFrame() << std::endl;
Chris@236 675 setCentreFrame(m_manager->getPlaybackFrame(), false);
Chris@236 676 } else if (m_followPan) {
Chris@236 677 // std::cerr << "View::setViewManager: setting centre frame to global centre frame: " << m_manager->getGlobalCentreFrame() << std::endl;
Chris@236 678 setCentreFrame(m_manager->getGlobalCentreFrame(), false);
Chris@236 679 }
Chris@236 680 if (m_followZoom) setZoomLevel(m_manager->getGlobalZoom());
Chris@236 681
Chris@127 682 toolModeChanged();
Chris@127 683 }
Chris@127 684
Chris@127 685 void
Chris@127 686 View::setFollowGlobalPan(bool f)
Chris@127 687 {
Chris@127 688 m_followPan = f;
Chris@127 689 emit propertyContainerPropertyChanged(m_propertyContainer);
Chris@127 690 }
Chris@127 691
Chris@127 692 void
Chris@127 693 View::setFollowGlobalZoom(bool f)
Chris@127 694 {
Chris@127 695 m_followZoom = f;
Chris@127 696 emit propertyContainerPropertyChanged(m_propertyContainer);
Chris@127 697 }
Chris@127 698
Chris@127 699 void
Chris@267 700 View::drawVisibleText(QPainter &paint, int x, int y, QString text, TextStyle style) const
Chris@127 701 {
Chris@127 702 if (style == OutlinedText) {
Chris@127 703
Chris@279 704 paint.save();
Chris@279 705
Chris@279 706 QColor penColour, surroundColour;
Chris@279 707
Chris@287 708 penColour = getForeground();
Chris@287 709 surroundColour = getBackground();
Chris@279 710
Chris@127 711 paint.setPen(surroundColour);
Chris@127 712
Chris@127 713 for (int dx = -1; dx <= 1; ++dx) {
Chris@127 714 for (int dy = -1; dy <= 1; ++dy) {
Chris@127 715 if (!(dx || dy)) continue;
Chris@127 716 paint.drawText(x + dx, y + dy, text);
Chris@127 717 }
Chris@127 718 }
Chris@127 719
Chris@127 720 paint.setPen(penColour);
Chris@127 721
Chris@127 722 paint.drawText(x, y, text);
Chris@287 723
Chris@279 724 paint.restore();
Chris@127 725
Chris@127 726 } else {
Chris@127 727
Chris@127 728 std::cerr << "ERROR: View::drawVisibleText: Boxed style not yet implemented!" << std::endl;
Chris@127 729 }
Chris@127 730 }
Chris@127 731
Chris@127 732 void
Chris@127 733 View::setPlaybackFollow(PlaybackFollowMode m)
Chris@127 734 {
Chris@127 735 m_followPlay = m;
Chris@127 736 emit propertyContainerPropertyChanged(m_propertyContainer);
Chris@127 737 }
Chris@127 738
Chris@127 739 void
Chris@127 740 View::modelChanged()
Chris@127 741 {
Chris@127 742 QObject *obj = sender();
Chris@127 743
Chris@127 744 #ifdef DEBUG_VIEW_WIDGET_PAINT
Chris@127 745 std::cerr << "View(" << this << ")::modelChanged()" << std::endl;
Chris@127 746 #endif
Chris@127 747
Chris@127 748 // If the model that has changed is not used by any of the cached
Chris@127 749 // layers, we won't need to recreate the cache
Chris@127 750
Chris@127 751 bool recreate = false;
Chris@127 752
Chris@127 753 bool discard;
Chris@127 754 LayerList scrollables = getScrollableBackLayers(false, discard);
Chris@127 755 for (LayerList::const_iterator i = scrollables.begin();
Chris@127 756 i != scrollables.end(); ++i) {
Chris@127 757 if (*i == obj || (*i)->getModel() == obj) {
Chris@127 758 recreate = true;
Chris@127 759 break;
Chris@127 760 }
Chris@127 761 }
Chris@127 762
Chris@127 763 if (recreate) {
Chris@127 764 delete m_cache;
Chris@127 765 m_cache = 0;
Chris@127 766 }
Chris@127 767
Chris@336 768 emit layerModelChanged();
Chris@336 769
Chris@127 770 checkProgress(obj);
Chris@127 771
Chris@127 772 update();
Chris@127 773 }
Chris@127 774
Chris@127 775 void
Chris@127 776 View::modelChanged(size_t startFrame, size_t endFrame)
Chris@127 777 {
Chris@127 778 QObject *obj = sender();
Chris@127 779
Chris@127 780 long myStartFrame = getStartFrame();
Chris@127 781 size_t myEndFrame = getEndFrame();
Chris@127 782
Chris@127 783 #ifdef DEBUG_VIEW_WIDGET_PAINT
Chris@127 784 std::cerr << "View(" << this << ")::modelChanged(" << startFrame << "," << endFrame << ") [me " << myStartFrame << "," << myEndFrame << "]" << std::endl;
Chris@127 785 #endif
Chris@127 786
Chris@127 787 if (myStartFrame > 0 && endFrame < size_t(myStartFrame)) {
Chris@127 788 checkProgress(obj);
Chris@127 789 return;
Chris@127 790 }
Chris@127 791 if (startFrame > myEndFrame) {
Chris@127 792 checkProgress(obj);
Chris@127 793 return;
Chris@127 794 }
Chris@127 795
Chris@127 796 // If the model that has changed is not used by any of the cached
Chris@127 797 // layers, we won't need to recreate the cache
Chris@127 798
Chris@127 799 bool recreate = false;
Chris@127 800
Chris@127 801 bool discard;
Chris@127 802 LayerList scrollables = getScrollableBackLayers(false, discard);
Chris@127 803 for (LayerList::const_iterator i = scrollables.begin();
Chris@127 804 i != scrollables.end(); ++i) {
Chris@127 805 if (*i == obj || (*i)->getModel() == obj) {
Chris@127 806 recreate = true;
Chris@127 807 break;
Chris@127 808 }
Chris@127 809 }
Chris@127 810
Chris@127 811 if (recreate) {
Chris@127 812 delete m_cache;
Chris@127 813 m_cache = 0;
Chris@127 814 }
Chris@127 815
Chris@127 816 if (long(startFrame) < myStartFrame) startFrame = myStartFrame;
Chris@127 817 if (endFrame > myEndFrame) endFrame = myEndFrame;
Chris@127 818
Chris@127 819 checkProgress(obj);
Chris@127 820
Chris@127 821 update();
Chris@127 822 }
Chris@127 823
Chris@127 824 void
Chris@127 825 View::modelCompletionChanged()
Chris@127 826 {
Chris@326 827 // std::cerr << "View(" << this << ")::modelCompletionChanged()" << std::endl;
Chris@301 828
Chris@127 829 QObject *obj = sender();
Chris@127 830 checkProgress(obj);
Chris@127 831 }
Chris@127 832
Chris@127 833 void
Chris@320 834 View::modelAlignmentCompletionChanged()
Chris@320 835 {
Chris@326 836 // std::cerr << "View(" << this << ")::modelAlignmentCompletionChanged()" << std::endl;
Chris@320 837
Chris@320 838 QObject *obj = sender();
Chris@320 839 checkProgress(obj);
Chris@320 840 }
Chris@320 841
Chris@320 842 void
Chris@127 843 View::modelReplaced()
Chris@127 844 {
Chris@127 845 #ifdef DEBUG_VIEW_WIDGET_PAINT
Chris@127 846 std::cerr << "View(" << this << ")::modelReplaced()" << std::endl;
Chris@127 847 #endif
Chris@127 848 delete m_cache;
Chris@127 849 m_cache = 0;
Chris@127 850
Chris@127 851 update();
Chris@127 852 }
Chris@127 853
Chris@127 854 void
Chris@127 855 View::layerParametersChanged()
Chris@127 856 {
Chris@127 857 Layer *layer = dynamic_cast<Layer *>(sender());
Chris@127 858
Chris@127 859 #ifdef DEBUG_VIEW_WIDGET_PAINT
Chris@127 860 std::cerr << "View::layerParametersChanged()" << std::endl;
Chris@127 861 #endif
Chris@127 862
Chris@127 863 delete m_cache;
Chris@127 864 m_cache = 0;
Chris@127 865 update();
Chris@127 866
Chris@127 867 if (layer) {
Chris@127 868 emit propertyContainerPropertyChanged(layer);
Chris@127 869 }
Chris@127 870 }
Chris@127 871
Chris@127 872 void
Chris@197 873 View::layerParameterRangesChanged()
Chris@197 874 {
Chris@197 875 Layer *layer = dynamic_cast<Layer *>(sender());
Chris@197 876 if (layer) emit propertyContainerPropertyRangeChanged(layer);
Chris@197 877 }
Chris@197 878
Chris@197 879 void
Chris@268 880 View::layerMeasurementRectsChanged()
Chris@268 881 {
Chris@268 882 Layer *layer = dynamic_cast<Layer *>(sender());
Chris@268 883 if (layer) update();
Chris@268 884 }
Chris@268 885
Chris@268 886 void
Chris@127 887 View::layerNameChanged()
Chris@127 888 {
Chris@127 889 Layer *layer = dynamic_cast<Layer *>(sender());
Chris@127 890 if (layer) emit propertyContainerNameChanged(layer);
Chris@127 891 }
Chris@127 892
Chris@127 893 void
Chris@333 894 View::globalCentreFrameChanged(unsigned long rf)
Chris@127 895 {
Chris@211 896 if (m_followPan) {
Chris@333 897 size_t f = alignFromReference(rf);
Chris@363 898 #ifdef DEBUG_VIEW_WIDGET_PAINT
Chris@333 899 std::cerr << "View[" << this << "]::globalCentreFrameChanged(" << rf
Chris@333 900 << "): setting centre frame to " << f << std::endl;
Chris@363 901 #endif
Chris@333 902 setCentreFrame(f, false);
Chris@127 903 }
Chris@127 904 }
Chris@127 905
Chris@127 906 void
Chris@248 907 View::viewCentreFrameChanged(View *, unsigned long )
Chris@211 908 {
Chris@211 909 // We do nothing with this, but a subclass might
Chris@211 910 }
Chris@211 911
Chris@211 912 void
Chris@127 913 View::viewManagerPlaybackFrameChanged(unsigned long f)
Chris@127 914 {
Chris@127 915 if (m_manager) {
Chris@127 916 if (sender() != m_manager) return;
Chris@127 917 }
Chris@127 918
Chris@301 919 f = getAlignedPlaybackFrame();
Chris@301 920
Chris@127 921 if (m_playPointerFrame == f) return;
Chris@127 922 bool visible = (getXForFrame(m_playPointerFrame) != getXForFrame(f));
Chris@127 923 size_t oldPlayPointerFrame = m_playPointerFrame;
Chris@127 924 m_playPointerFrame = f;
Chris@127 925 if (!visible) return;
Chris@127 926
Chris@127 927 switch (m_followPlay) {
Chris@127 928
Chris@127 929 case PlaybackScrollContinuous:
Chris@127 930 if (QApplication::mouseButtons() == Qt::NoButton) {
Chris@211 931 setCentreFrame(f, false);
Chris@127 932 }
Chris@127 933 break;
Chris@127 934
Chris@127 935 case PlaybackScrollPage:
Chris@127 936 {
Chris@127 937 int xold = getXForFrame(oldPlayPointerFrame);
Chris@127 938 update(xold - 1, 0, 3, height());
Chris@127 939
Chris@127 940 long w = getEndFrame() - getStartFrame();
Chris@127 941 w -= w/5;
Chris@127 942 long sf = (f / w) * w - w/8;
Chris@127 943
Chris@127 944 if (m_manager &&
Chris@127 945 m_manager->isPlaying() &&
Chris@127 946 m_manager->getPlaySelectionMode()) {
Chris@127 947 MultiSelection::SelectionList selections = m_manager->getSelections();
Chris@127 948 if (!selections.empty()) {
Chris@127 949 size_t selectionStart = selections.begin()->getStartFrame();
Chris@127 950 if (sf < long(selectionStart) - w / 10) {
Chris@127 951 sf = long(selectionStart) - w / 10;
Chris@127 952 }
Chris@127 953 }
Chris@127 954 }
Chris@127 955
Chris@127 956 #ifdef DEBUG_VIEW_WIDGET_PAINT
Chris@127 957 std::cerr << "PlaybackScrollPage: f = " << f << ", sf = " << sf << ", start frame "
Chris@127 958 << getStartFrame() << std::endl;
Chris@127 959 #endif
Chris@127 960
Chris@127 961 // We don't consider scrolling unless the pointer is outside
Chris@127 962 // the clearly visible range already
Chris@127 963
Chris@127 964 int xnew = getXForFrame(m_playPointerFrame);
Chris@127 965
Chris@127 966 #ifdef DEBUG_VIEW_WIDGET_PAINT
Chris@127 967 std::cerr << "xnew = " << xnew << ", width = " << width() << std::endl;
Chris@127 968 #endif
Chris@127 969
Chris@127 970 if (xnew < width()/8 || xnew > (width()*7)/8) {
Chris@127 971 if (QApplication::mouseButtons() == Qt::NoButton) {
Chris@127 972 long offset = getFrameForX(width()/2) - getStartFrame();
Chris@127 973 long newCentre = sf + offset;
Chris@127 974 bool changed = setCentreFrame(newCentre, false);
Chris@127 975 if (changed) {
Chris@127 976 xold = getXForFrame(oldPlayPointerFrame);
Chris@127 977 update(xold - 1, 0, 3, height());
Chris@127 978 }
Chris@127 979 }
Chris@127 980 }
Chris@127 981
Chris@127 982 update(xnew - 1, 0, 3, height());
Chris@127 983
Chris@127 984 break;
Chris@127 985 }
Chris@127 986
Chris@127 987 case PlaybackIgnore:
Chris@127 988 if (long(f) >= getStartFrame() && f < getEndFrame()) {
Chris@127 989 update();
Chris@127 990 }
Chris@127 991 break;
Chris@127 992 }
Chris@127 993 }
Chris@127 994
Chris@127 995 void
Chris@222 996 View::viewZoomLevelChanged(View *p, unsigned long z, bool locked)
Chris@127 997 {
Chris@244 998 #ifdef DEBUG_VIEW_WIDGET_PAINT
Chris@244 999 std::cerr << "View[" << this << "]: viewZoomLevelChanged(" << p << ", " << z << ", " << locked << ")" << std::endl;
Chris@244 1000 #endif
Chris@127 1001 if (m_followZoom && p != this && locked) {
Chris@222 1002 setZoomLevel(z);
Chris@127 1003 }
Chris@127 1004 }
Chris@127 1005
Chris@127 1006 void
Chris@127 1007 View::selectionChanged()
Chris@127 1008 {
Chris@127 1009 if (m_selectionCached) {
Chris@127 1010 delete m_cache;
Chris@127 1011 m_cache = 0;
Chris@127 1012 m_selectionCached = false;
Chris@127 1013 }
Chris@127 1014 update();
Chris@127 1015 }
Chris@127 1016
Chris@127 1017 size_t
Chris@222 1018 View::getFirstVisibleFrame() const
Chris@222 1019 {
Chris@222 1020 long f0 = getStartFrame();
Chris@222 1021 size_t f = getModelsStartFrame();
Chris@222 1022 if (f0 < 0 || f0 < long(f)) return f;
Chris@222 1023 return f0;
Chris@222 1024 }
Chris@222 1025
Chris@222 1026 size_t
Chris@222 1027 View::getLastVisibleFrame() const
Chris@222 1028 {
Chris@222 1029 size_t f0 = getEndFrame();
Chris@222 1030 size_t f = getModelsEndFrame();
Chris@222 1031 if (f0 > f) return f;
Chris@222 1032 return f0;
Chris@222 1033 }
Chris@222 1034
Chris@222 1035 size_t
Chris@127 1036 View::getModelsStartFrame() const
Chris@127 1037 {
Chris@127 1038 bool first = true;
Chris@127 1039 size_t startFrame = 0;
Chris@127 1040
Chris@127 1041 for (LayerList::const_iterator i = m_layers.begin(); i != m_layers.end(); ++i) {
Chris@127 1042
Chris@127 1043 if ((*i)->getModel() && (*i)->getModel()->isOK()) {
Chris@127 1044
Chris@127 1045 size_t thisStartFrame = (*i)->getModel()->getStartFrame();
Chris@127 1046
Chris@127 1047 if (first || thisStartFrame < startFrame) {
Chris@127 1048 startFrame = thisStartFrame;
Chris@127 1049 }
Chris@127 1050 first = false;
Chris@127 1051 }
Chris@127 1052 }
Chris@127 1053 return startFrame;
Chris@127 1054 }
Chris@127 1055
Chris@127 1056 size_t
Chris@127 1057 View::getModelsEndFrame() const
Chris@127 1058 {
Chris@127 1059 bool first = true;
Chris@127 1060 size_t endFrame = 0;
Chris@127 1061
Chris@127 1062 for (LayerList::const_iterator i = m_layers.begin(); i != m_layers.end(); ++i) {
Chris@127 1063
Chris@127 1064 if ((*i)->getModel() && (*i)->getModel()->isOK()) {
Chris@127 1065
Chris@127 1066 size_t thisEndFrame = (*i)->getModel()->getEndFrame();
Chris@127 1067
Chris@127 1068 if (first || thisEndFrame > endFrame) {
Chris@127 1069 endFrame = thisEndFrame;
Chris@127 1070 }
Chris@127 1071 first = false;
Chris@127 1072 }
Chris@127 1073 }
Chris@127 1074
Chris@127 1075 if (first) return getModelsStartFrame();
Chris@127 1076 return endFrame;
Chris@127 1077 }
Chris@127 1078
Chris@127 1079 int
Chris@127 1080 View::getModelsSampleRate() const
Chris@127 1081 {
Chris@127 1082 //!!! Just go for the first, for now. If we were supporting
Chris@127 1083 // multiple samplerates, we'd probably want to do frame/time
Chris@127 1084 // conversion in the model
Chris@127 1085
Chris@159 1086 //!!! nah, this wants to always return the sr of the main model!
Chris@159 1087
Chris@127 1088 for (LayerList::const_iterator i = m_layers.begin(); i != m_layers.end(); ++i) {
Chris@127 1089 if ((*i)->getModel() && (*i)->getModel()->isOK()) {
Chris@127 1090 return (*i)->getModel()->getSampleRate();
Chris@127 1091 }
Chris@127 1092 }
Chris@127 1093 return 0;
Chris@127 1094 }
Chris@127 1095
Chris@315 1096 View::ModelSet
Chris@315 1097 View::getModels()
Chris@315 1098 {
Chris@315 1099 ModelSet models;
Chris@315 1100
Chris@315 1101 for (int i = 0; i < getLayerCount(); ++i) {
Chris@315 1102
Chris@315 1103 Layer *layer = getLayer(i);
Chris@315 1104
Chris@315 1105 if (dynamic_cast<TimeRulerLayer *>(layer)) {
Chris@315 1106 continue;
Chris@315 1107 }
Chris@315 1108
Chris@315 1109 if (layer && layer->getModel()) {
Chris@315 1110 Model *model = layer->getModel();
Chris@315 1111 models.insert(model);
Chris@315 1112 }
Chris@315 1113 }
Chris@315 1114
Chris@315 1115 return models;
Chris@315 1116 }
Chris@315 1117
Chris@320 1118 Model *
Chris@320 1119 View::getAligningModel() const
Chris@301 1120 {
Chris@320 1121 if (!m_manager ||
Chris@320 1122 !m_manager->getAlignMode() ||
Chris@314 1123 !m_manager->getPlaybackModel()) {
Chris@320 1124 return 0;
Chris@314 1125 }
Chris@301 1126
Chris@320 1127 Model *anyModel = 0;
Chris@359 1128 Model *alignedModel = 0;
Chris@320 1129 Model *goodModel = 0;
Chris@301 1130
Chris@320 1131 for (LayerList::const_iterator i = m_layers.begin();
Chris@320 1132 i != m_layers.end(); ++i) {
Chris@320 1133
Chris@320 1134 Layer *layer = *i;
Chris@320 1135
Chris@320 1136 if (!layer) continue;
Chris@320 1137 if (dynamic_cast<TimeRulerLayer *>(layer)) continue;
Chris@301 1138
Chris@301 1139 Model *model = (*i)->getModel();
Chris@301 1140 if (!model) continue;
Chris@301 1141
Chris@359 1142 anyModel = model;
Chris@359 1143
Chris@320 1144 if (model->getAlignmentReference()) {
Chris@359 1145 alignedModel = model;
Chris@320 1146 if (layer->isLayerOpaque() ||
Chris@320 1147 dynamic_cast<RangeSummarisableTimeValueModel *>(model)) {
Chris@320 1148 goodModel = model;
Chris@320 1149 }
Chris@301 1150 }
Chris@320 1151 }
Chris@301 1152
Chris@320 1153 if (goodModel) return goodModel;
Chris@359 1154 else if (alignedModel) return alignedModel;
Chris@320 1155 else return anyModel;
Chris@320 1156 }
Chris@320 1157
Chris@320 1158 size_t
Chris@320 1159 View::alignFromReference(size_t f) const
Chris@320 1160 {
Chris@321 1161 if (!m_manager->getAlignMode()) return f;
Chris@320 1162 Model *aligningModel = getAligningModel();
Chris@320 1163 if (!aligningModel) return f;
Chris@320 1164 return aligningModel->alignFromReference(f);
Chris@320 1165 }
Chris@320 1166
Chris@320 1167 size_t
Chris@320 1168 View::alignToReference(size_t f) const
Chris@320 1169 {
Chris@321 1170 if (!m_manager->getAlignMode()) return f;
Chris@320 1171 Model *aligningModel = getAligningModel();
Chris@320 1172 if (!aligningModel) return f;
Chris@320 1173 return aligningModel->alignToReference(f);
Chris@320 1174 }
Chris@320 1175
Chris@320 1176 int
Chris@320 1177 View::getAlignedPlaybackFrame() const
Chris@320 1178 {
Chris@321 1179 int pf = m_manager->getPlaybackFrame();
Chris@321 1180 if (!m_manager->getAlignMode()) return pf;
Chris@321 1181
Chris@320 1182 Model *aligningModel = getAligningModel();
Chris@320 1183 if (!aligningModel) return pf;
Chris@333 1184 /*
Chris@320 1185 Model *pm = m_manager->getPlaybackModel();
Chris@301 1186
Chris@301 1187 // std::cerr << "View[" << this << "]::getAlignedPlaybackFrame: pf = " << pf;
Chris@301 1188
Chris@301 1189 if (pm) {
Chris@333 1190 pf = pm->alignToReference(pf);
Chris@301 1191 // std::cerr << " -> " << pf;
Chris@301 1192 }
Chris@333 1193 */
Chris@333 1194 int af = aligningModel->alignFromReference(pf);
Chris@301 1195
Chris@301 1196 // std::cerr << ", aligned = " << af << std::endl;
Chris@301 1197 return af;
Chris@301 1198 }
Chris@301 1199
Chris@127 1200 bool
Chris@127 1201 View::areLayersScrollable() const
Chris@127 1202 {
Chris@127 1203 // True iff all views are scrollable
Chris@127 1204 for (LayerList::const_iterator i = m_layers.begin(); i != m_layers.end(); ++i) {
Chris@127 1205 if (!(*i)->isLayerScrollable(this)) return false;
Chris@127 1206 }
Chris@127 1207 return true;
Chris@127 1208 }
Chris@127 1209
Chris@127 1210 View::LayerList
Chris@127 1211 View::getScrollableBackLayers(bool testChanged, bool &changed) const
Chris@127 1212 {
Chris@127 1213 changed = false;
Chris@127 1214
Chris@127 1215 // We want a list of all the scrollable layers that are behind the
Chris@127 1216 // backmost non-scrollable layer.
Chris@127 1217
Chris@127 1218 LayerList scrollables;
Chris@127 1219 bool metUnscrollable = false;
Chris@127 1220
Chris@127 1221 for (LayerList::const_iterator i = m_layers.begin(); i != m_layers.end(); ++i) {
Chris@132 1222 // std::cerr << "View::getScrollableBackLayers: calling isLayerDormant on layer " << *i << std::endl;
Chris@132 1223 // std::cerr << "(name is " << (*i)->objectName().toStdString() << ")"
Chris@132 1224 // << std::endl;
Chris@132 1225 // std::cerr << "View::getScrollableBackLayers: I am " << this << std::endl;
Chris@127 1226 if ((*i)->isLayerDormant(this)) continue;
Chris@127 1227 if ((*i)->isLayerOpaque()) {
Chris@127 1228 // You can't see anything behind an opaque layer!
Chris@127 1229 scrollables.clear();
Chris@127 1230 if (metUnscrollable) break;
Chris@127 1231 }
Chris@127 1232 if (!metUnscrollable && (*i)->isLayerScrollable(this)) {
Chris@127 1233 scrollables.push_back(*i);
Chris@127 1234 } else {
Chris@127 1235 metUnscrollable = true;
Chris@127 1236 }
Chris@127 1237 }
Chris@127 1238
Chris@127 1239 if (testChanged && scrollables != m_lastScrollableBackLayers) {
Chris@127 1240 m_lastScrollableBackLayers = scrollables;
Chris@127 1241 changed = true;
Chris@127 1242 }
Chris@127 1243 return scrollables;
Chris@127 1244 }
Chris@127 1245
Chris@127 1246 View::LayerList
Chris@127 1247 View::getNonScrollableFrontLayers(bool testChanged, bool &changed) const
Chris@127 1248 {
Chris@127 1249 changed = false;
Chris@127 1250 LayerList nonScrollables;
Chris@127 1251
Chris@127 1252 // Everything in front of the first non-scrollable from the back
Chris@127 1253 // should also be considered non-scrollable
Chris@127 1254
Chris@127 1255 bool started = false;
Chris@127 1256
Chris@127 1257 for (LayerList::const_iterator i = m_layers.begin(); i != m_layers.end(); ++i) {
Chris@127 1258 if ((*i)->isLayerDormant(this)) continue;
Chris@127 1259 if (!started && (*i)->isLayerScrollable(this)) {
Chris@127 1260 continue;
Chris@127 1261 }
Chris@127 1262 started = true;
Chris@127 1263 if ((*i)->isLayerOpaque()) {
Chris@127 1264 // You can't see anything behind an opaque layer!
Chris@127 1265 nonScrollables.clear();
Chris@127 1266 }
Chris@127 1267 nonScrollables.push_back(*i);
Chris@127 1268 }
Chris@127 1269
Chris@127 1270 if (testChanged && nonScrollables != m_lastNonScrollableBackLayers) {
Chris@127 1271 m_lastNonScrollableBackLayers = nonScrollables;
Chris@127 1272 changed = true;
Chris@127 1273 }
Chris@127 1274
Chris@127 1275 return nonScrollables;
Chris@127 1276 }
Chris@127 1277
Chris@127 1278 size_t
Chris@127 1279 View::getZoomConstraintBlockSize(size_t blockSize,
Chris@127 1280 ZoomConstraint::RoundingDirection dir)
Chris@127 1281 const
Chris@127 1282 {
Chris@127 1283 size_t candidate = blockSize;
Chris@127 1284 bool haveCandidate = false;
Chris@127 1285
Chris@127 1286 PowerOfSqrtTwoZoomConstraint defaultZoomConstraint;
Chris@127 1287
Chris@127 1288 for (LayerList::const_iterator i = m_layers.begin(); i != m_layers.end(); ++i) {
Chris@127 1289
Chris@127 1290 const ZoomConstraint *zoomConstraint = (*i)->getZoomConstraint();
Chris@127 1291 if (!zoomConstraint) zoomConstraint = &defaultZoomConstraint;
Chris@127 1292
Chris@127 1293 size_t thisBlockSize =
Chris@127 1294 zoomConstraint->getNearestBlockSize(blockSize, dir);
Chris@127 1295
Chris@127 1296 // Go for the block size that's furthest from the one
Chris@127 1297 // passed in. Most of the time, that's what we want.
Chris@127 1298 if (!haveCandidate ||
Chris@127 1299 (thisBlockSize > blockSize && thisBlockSize > candidate) ||
Chris@127 1300 (thisBlockSize < blockSize && thisBlockSize < candidate)) {
Chris@127 1301 candidate = thisBlockSize;
Chris@127 1302 haveCandidate = true;
Chris@127 1303 }
Chris@127 1304 }
Chris@127 1305
Chris@127 1306 return candidate;
Chris@127 1307 }
Chris@127 1308
Chris@183 1309 bool
Chris@183 1310 View::areLayerColoursSignificant() const
Chris@183 1311 {
Chris@183 1312 for (LayerList::const_iterator i = m_layers.begin(); i != m_layers.end(); ++i) {
Chris@287 1313 if ((*i)->getLayerColourSignificance() ==
Chris@287 1314 Layer::ColourHasMeaningfulValue) return true;
Chris@183 1315 if ((*i)->isLayerOpaque()) break;
Chris@183 1316 }
Chris@183 1317 return false;
Chris@183 1318 }
Chris@183 1319
Chris@217 1320 bool
Chris@217 1321 View::hasTopLayerTimeXAxis() const
Chris@217 1322 {
Chris@217 1323 LayerList::const_iterator i = m_layers.end();
Chris@217 1324 if (i == m_layers.begin()) return false;
Chris@217 1325 --i;
Chris@217 1326 return (*i)->hasTimeXAxis();
Chris@217 1327 }
Chris@217 1328
Chris@127 1329 void
Chris@127 1330 View::zoom(bool in)
Chris@127 1331 {
Chris@127 1332 int newZoomLevel = m_zoomLevel;
Chris@127 1333
Chris@127 1334 if (in) {
Chris@127 1335 newZoomLevel = getZoomConstraintBlockSize(newZoomLevel - 1,
Chris@127 1336 ZoomConstraint::RoundDown);
Chris@127 1337 } else {
Chris@127 1338 newZoomLevel = getZoomConstraintBlockSize(newZoomLevel + 1,
Chris@127 1339 ZoomConstraint::RoundUp);
Chris@127 1340 }
Chris@127 1341
Chris@127 1342 if (newZoomLevel != m_zoomLevel) {
Chris@127 1343 setZoomLevel(newZoomLevel);
Chris@127 1344 }
Chris@127 1345 }
Chris@127 1346
Chris@127 1347 void
Chris@127 1348 View::scroll(bool right, bool lots)
Chris@127 1349 {
Chris@127 1350 long delta;
Chris@127 1351 if (lots) {
Chris@127 1352 delta = (getEndFrame() - getStartFrame()) / 2;
Chris@127 1353 } else {
Chris@127 1354 delta = (getEndFrame() - getStartFrame()) / 20;
Chris@127 1355 }
Chris@127 1356 if (right) delta = -delta;
Chris@127 1357
Chris@127 1358 if (int(m_centreFrame) < delta) {
Chris@127 1359 setCentreFrame(0);
Chris@127 1360 } else if (int(m_centreFrame) - delta >= int(getModelsEndFrame())) {
Chris@127 1361 setCentreFrame(getModelsEndFrame());
Chris@127 1362 } else {
Chris@127 1363 setCentreFrame(m_centreFrame - delta);
Chris@127 1364 }
Chris@127 1365 }
Chris@127 1366
Chris@127 1367 void
Chris@127 1368 View::checkProgress(void *object)
Chris@127 1369 {
Chris@127 1370 if (!m_showProgress) return;
Chris@127 1371
Chris@127 1372 int ph = height();
Chris@127 1373
Chris@127 1374 for (ProgressMap::const_iterator i = m_progressBars.begin();
Chris@127 1375 i != m_progressBars.end(); ++i) {
Chris@127 1376
Chris@127 1377 if (i->first == object) {
Chris@127 1378
Chris@127 1379 int completion = i->first->getCompletion(this);
Chris@301 1380 QString text = i->first->getPropertyContainerName();
Chris@301 1381
Chris@301 1382 if (completion >= 100) {
Chris@301 1383
Chris@301 1384 //!!!
Chris@301 1385 Model *model = i->first->getModel();
Chris@301 1386 RangeSummarisableTimeValueModel *wfm =
Chris@301 1387 dynamic_cast<RangeSummarisableTimeValueModel *>(model);
Chris@326 1388 if (wfm ||
Chris@328 1389 (wfm = dynamic_cast<RangeSummarisableTimeValueModel *>
Chris@328 1390 (model->getSourceModel()))) {
Chris@301 1391 completion = wfm->getAlignmentCompletion();
Chris@301 1392 if (completion < 100) {
Chris@301 1393 text = tr("Alignment");
Chris@301 1394 }
Chris@301 1395 }
Chris@301 1396 }
Chris@127 1397
Chris@127 1398 if (completion >= 100) {
Chris@127 1399
Chris@127 1400 i->second->hide();
Chris@127 1401
Chris@127 1402 } else {
Chris@127 1403
Chris@301 1404 i->second->setText(text);
Chris@132 1405
Chris@127 1406 i->second->setValue(completion);
Chris@127 1407 i->second->move(0, ph - i->second->height());
Chris@127 1408
Chris@127 1409 i->second->show();
Chris@127 1410 i->second->update();
Chris@127 1411
Chris@127 1412 ph -= i->second->height();
Chris@127 1413 }
Chris@127 1414 } else {
Chris@127 1415 if (i->second->isVisible()) {
Chris@127 1416 ph -= i->second->height();
Chris@127 1417 }
Chris@127 1418 }
Chris@127 1419 }
Chris@127 1420 }
Chris@127 1421
Chris@127 1422 void
Chris@339 1423 View::setPaintFont(QPainter &paint)
Chris@339 1424 {
Chris@339 1425 QFont font(paint.font());
Chris@339 1426 font.setPointSize(Preferences::getInstance()->getViewFontSize());
Chris@339 1427 paint.setFont(font);
Chris@339 1428 }
Chris@339 1429
Chris@339 1430 void
Chris@127 1431 View::paintEvent(QPaintEvent *e)
Chris@127 1432 {
Chris@127 1433 // Profiler prof("View::paintEvent", false);
Chris@236 1434 // std::cerr << "View::paintEvent: centre frame is " << m_centreFrame << std::endl;
Chris@127 1435
Chris@127 1436 if (m_layers.empty()) {
Chris@127 1437 QFrame::paintEvent(e);
Chris@127 1438 return;
Chris@127 1439 }
Chris@127 1440
Chris@127 1441 // ensure our constraints are met
Chris@137 1442
Chris@137 1443 /*!!! Should we do this only if we have layers that can't support other
Chris@137 1444 zoom levels?
Chris@137 1445
Chris@127 1446 m_zoomLevel = getZoomConstraintBlockSize(m_zoomLevel,
Chris@127 1447 ZoomConstraint::RoundUp);
Chris@137 1448 */
Chris@127 1449
Chris@127 1450 QPainter paint;
Chris@127 1451 bool repaintCache = false;
Chris@127 1452 bool paintedCacheRect = false;
Chris@127 1453
Chris@127 1454 QRect cacheRect(rect());
Chris@127 1455
Chris@127 1456 if (e) {
Chris@127 1457 cacheRect &= e->rect();
Chris@127 1458 #ifdef DEBUG_VIEW_WIDGET_PAINT
Chris@127 1459 std::cerr << "paint rect " << cacheRect.width() << "x" << cacheRect.height()
Chris@127 1460 << ", my rect " << width() << "x" << height() << std::endl;
Chris@127 1461 #endif
Chris@127 1462 }
Chris@127 1463
Chris@127 1464 QRect nonCacheRect(cacheRect);
Chris@127 1465
Chris@127 1466 // If not all layers are scrollable, but some of the back layers
Chris@127 1467 // are, we should store only those in the cache.
Chris@127 1468
Chris@127 1469 bool layersChanged = false;
Chris@127 1470 LayerList scrollables = getScrollableBackLayers(true, layersChanged);
Chris@127 1471 LayerList nonScrollables = getNonScrollableFrontLayers(true, layersChanged);
Chris@127 1472 bool selectionCacheable = nonScrollables.empty();
Chris@127 1473 bool haveSelections = m_manager && !m_manager->getSelections().empty();
Chris@127 1474 bool selectionDrawn = false;
Chris@127 1475
Chris@127 1476 // If all the non-scrollable layers are non-opaque, then we draw
Chris@127 1477 // the selection rectangle behind them and cache it. If any are
Chris@127 1478 // opaque, however, we can't cache.
Chris@127 1479 //
Chris@127 1480 if (!selectionCacheable) {
Chris@127 1481 selectionCacheable = true;
Chris@127 1482 for (LayerList::const_iterator i = nonScrollables.begin();
Chris@127 1483 i != nonScrollables.end(); ++i) {
Chris@127 1484 if ((*i)->isLayerOpaque()) {
Chris@127 1485 selectionCacheable = false;
Chris@127 1486 break;
Chris@127 1487 }
Chris@127 1488 }
Chris@127 1489 }
Chris@127 1490
Chris@127 1491 if (selectionCacheable) {
Chris@127 1492 QPoint localPos;
Chris@127 1493 bool closeToLeft, closeToRight;
Chris@127 1494 if (shouldIlluminateLocalSelection(localPos, closeToLeft, closeToRight)) {
Chris@127 1495 selectionCacheable = false;
Chris@127 1496 }
Chris@127 1497 }
Chris@127 1498
Chris@127 1499 #ifdef DEBUG_VIEW_WIDGET_PAINT
Chris@127 1500 std::cerr << "View(" << this << ")::paintEvent: have " << scrollables.size()
Chris@127 1501 << " scrollable back layers and " << nonScrollables.size()
Chris@127 1502 << " non-scrollable front layers" << std::endl;
Chris@127 1503 std::cerr << "haveSelections " << haveSelections << ", selectionCacheable "
Chris@127 1504 << selectionCacheable << ", m_selectionCached " << m_selectionCached << std::endl;
Chris@127 1505 #endif
Chris@127 1506
Chris@127 1507 if (layersChanged || scrollables.empty() ||
Chris@127 1508 (haveSelections && (selectionCacheable != m_selectionCached))) {
Chris@127 1509 delete m_cache;
Chris@127 1510 m_cache = 0;
Chris@127 1511 m_selectionCached = false;
Chris@127 1512 }
Chris@127 1513
Chris@127 1514 if (!scrollables.empty()) {
Chris@244 1515
Chris@244 1516 #ifdef DEBUG_VIEW_WIDGET_PAINT
Chris@244 1517 std::cerr << "View(" << this << "): cache " << m_cache << ", cache zoom "
Chris@244 1518 << m_cacheZoomLevel << ", zoom " << m_zoomLevel << std::endl;
Chris@244 1519 #endif
Chris@244 1520
Chris@127 1521 if (!m_cache ||
Chris@127 1522 m_cacheZoomLevel != m_zoomLevel ||
Chris@127 1523 width() != m_cache->width() ||
Chris@127 1524 height() != m_cache->height()) {
Chris@127 1525
Chris@127 1526 // cache is not valid
Chris@127 1527
Chris@127 1528 if (cacheRect.width() < width()/10) {
Chris@244 1529 delete m_cache;
Chris@244 1530 m_cache = 0;
Chris@127 1531 #ifdef DEBUG_VIEW_WIDGET_PAINT
Chris@127 1532 std::cerr << "View(" << this << ")::paintEvent: small repaint, not bothering to recreate cache" << std::endl;
Chris@127 1533 #endif
Chris@127 1534 } else {
Chris@127 1535 delete m_cache;
Chris@127 1536 m_cache = new QPixmap(width(), height());
Chris@127 1537 #ifdef DEBUG_VIEW_WIDGET_PAINT
Chris@127 1538 std::cerr << "View(" << this << ")::paintEvent: recreated cache" << std::endl;
Chris@127 1539 #endif
Chris@127 1540 cacheRect = rect();
Chris@127 1541 repaintCache = true;
Chris@127 1542 }
Chris@127 1543
Chris@127 1544 } else if (m_cacheCentreFrame != m_centreFrame) {
Chris@127 1545
Chris@127 1546 long dx =
Chris@127 1547 getXForFrame(m_cacheCentreFrame) -
Chris@127 1548 getXForFrame(m_centreFrame);
Chris@127 1549
Chris@127 1550 if (dx > -width() && dx < width()) {
Chris@127 1551 #if defined(Q_WS_WIN32) || defined(Q_WS_MAC)
Chris@127 1552 // Copying a pixmap to itself doesn't work properly on Windows
Chris@127 1553 // or Mac (it only works when moving in one direction)
Chris@127 1554 static QPixmap *tmpPixmap = 0;
Chris@127 1555 if (!tmpPixmap ||
Chris@127 1556 tmpPixmap->width() != width() ||
Chris@127 1557 tmpPixmap->height() != height()) {
Chris@127 1558 delete tmpPixmap;
Chris@127 1559 tmpPixmap = new QPixmap(width(), height());
Chris@127 1560 }
Chris@127 1561 paint.begin(tmpPixmap);
Chris@127 1562 paint.drawPixmap(0, 0, *m_cache);
Chris@127 1563 paint.end();
Chris@127 1564 paint.begin(m_cache);
Chris@127 1565 paint.drawPixmap(dx, 0, *tmpPixmap);
Chris@127 1566 paint.end();
Chris@127 1567 #else
Chris@127 1568 // But it seems to be fine on X11
Chris@127 1569 paint.begin(m_cache);
Chris@127 1570 paint.drawPixmap(dx, 0, *m_cache);
Chris@127 1571 paint.end();
Chris@127 1572 #endif
Chris@127 1573
Chris@127 1574 if (dx < 0) {
Chris@127 1575 cacheRect = QRect(width() + dx, 0, -dx, height());
Chris@127 1576 } else {
Chris@127 1577 cacheRect = QRect(0, 0, dx, height());
Chris@127 1578 }
Chris@127 1579 #ifdef DEBUG_VIEW_WIDGET_PAINT
Chris@127 1580 std::cerr << "View(" << this << ")::paintEvent: scrolled cache by " << dx << std::endl;
Chris@127 1581 #endif
Chris@127 1582 } else {
Chris@127 1583 cacheRect = rect();
Chris@127 1584 #ifdef DEBUG_VIEW_WIDGET_PAINT
Chris@127 1585 std::cerr << "View(" << this << ")::paintEvent: scrolling too far" << std::endl;
Chris@127 1586 #endif
Chris@127 1587 }
Chris@127 1588 repaintCache = true;
Chris@127 1589
Chris@127 1590 } else {
Chris@127 1591 #ifdef DEBUG_VIEW_WIDGET_PAINT
Chris@127 1592 std::cerr << "View(" << this << ")::paintEvent: cache is good" << std::endl;
Chris@127 1593 #endif
Chris@127 1594 paint.begin(this);
Chris@127 1595 paint.drawPixmap(cacheRect, *m_cache, cacheRect);
Chris@127 1596 paint.end();
Chris@127 1597 QFrame::paintEvent(e);
Chris@127 1598 paintedCacheRect = true;
Chris@127 1599 }
Chris@127 1600
Chris@127 1601 m_cacheCentreFrame = m_centreFrame;
Chris@127 1602 m_cacheZoomLevel = m_zoomLevel;
Chris@127 1603 }
Chris@127 1604
Chris@127 1605 #ifdef DEBUG_VIEW_WIDGET_PAINT
Chris@127 1606 // std::cerr << "View(" << this << ")::paintEvent: cacheRect " << cacheRect << ", nonCacheRect " << (nonCacheRect | cacheRect) << ", repaintCache " << repaintCache << ", paintedCacheRect " << paintedCacheRect << std::endl;
Chris@127 1607 #endif
Chris@127 1608
Chris@127 1609 // Scrollable (cacheable) items first
Chris@127 1610
Chris@127 1611 if (!paintedCacheRect) {
Chris@127 1612
Chris@127 1613 if (repaintCache) paint.begin(m_cache);
Chris@127 1614 else paint.begin(this);
Chris@339 1615 setPaintFont(paint);
Chris@127 1616 paint.setClipRect(cacheRect);
Chris@287 1617
Chris@287 1618 paint.setPen(getBackground());
Chris@287 1619 paint.setBrush(getBackground());
Chris@127 1620 paint.drawRect(cacheRect);
Chris@127 1621
Chris@287 1622 paint.setPen(getForeground());
Chris@127 1623 paint.setBrush(Qt::NoBrush);
Chris@127 1624
Chris@127 1625 for (LayerList::iterator i = scrollables.begin(); i != scrollables.end(); ++i) {
Chris@127 1626 paint.setRenderHint(QPainter::Antialiasing, false);
Chris@127 1627 paint.save();
Chris@127 1628 (*i)->paint(this, paint, cacheRect);
Chris@127 1629 paint.restore();
Chris@127 1630 }
Chris@127 1631
Chris@127 1632 if (haveSelections && selectionCacheable) {
Chris@127 1633 drawSelections(paint);
Chris@127 1634 m_selectionCached = repaintCache;
Chris@127 1635 selectionDrawn = true;
Chris@127 1636 }
Chris@127 1637
Chris@127 1638 paint.end();
Chris@127 1639
Chris@127 1640 if (repaintCache) {
Chris@127 1641 cacheRect |= (e ? e->rect() : rect());
Chris@127 1642 paint.begin(this);
Chris@127 1643 paint.drawPixmap(cacheRect, *m_cache, cacheRect);
Chris@127 1644 paint.end();
Chris@127 1645 }
Chris@127 1646 }
Chris@127 1647
Chris@127 1648 // Now non-cacheable items. We always need to redraw the
Chris@127 1649 // non-cacheable items across at least the area we drew of the
Chris@127 1650 // cacheable items.
Chris@127 1651
Chris@127 1652 nonCacheRect |= cacheRect;
Chris@127 1653
Chris@127 1654 paint.begin(this);
Chris@127 1655 paint.setClipRect(nonCacheRect);
Chris@339 1656 setPaintFont(paint);
Chris@127 1657 if (scrollables.empty()) {
Chris@287 1658 paint.setPen(getBackground());
Chris@287 1659 paint.setBrush(getBackground());
Chris@127 1660 paint.drawRect(nonCacheRect);
Chris@127 1661 }
Chris@127 1662
Chris@287 1663 paint.setPen(getForeground());
Chris@127 1664 paint.setBrush(Qt::NoBrush);
Chris@127 1665
Chris@127 1666 for (LayerList::iterator i = nonScrollables.begin(); i != nonScrollables.end(); ++i) {
Chris@127 1667 // Profiler profiler2("View::paintEvent non-cacheable");
Chris@127 1668 (*i)->paint(this, paint, nonCacheRect);
Chris@127 1669 }
Chris@127 1670
Chris@127 1671 paint.end();
Chris@127 1672
Chris@127 1673 paint.begin(this);
Chris@339 1674 setPaintFont(paint);
Chris@127 1675 if (e) paint.setClipRect(e->rect());
Chris@127 1676 if (!m_selectionCached) {
Chris@127 1677 drawSelections(paint);
Chris@127 1678 }
Chris@127 1679 paint.end();
Chris@127 1680
Chris@211 1681 bool showPlayPointer = true;
Chris@211 1682 if (m_followPlay == PlaybackScrollContinuous) {
Chris@211 1683 showPlayPointer = false;
Chris@211 1684 } else if (long(m_playPointerFrame) <= getStartFrame() ||
Chris@211 1685 m_playPointerFrame >= getEndFrame()) {
Chris@211 1686 showPlayPointer = false;
Chris@211 1687 } else if (m_manager && !m_manager->isPlaying()) {
Chris@211 1688 if (m_playPointerFrame == getCentreFrame() &&
Chris@211 1689 m_followPlay != PlaybackIgnore) {
Chris@211 1690 showPlayPointer = false;
Chris@211 1691 }
Chris@211 1692 }
Chris@211 1693
Chris@211 1694 if (showPlayPointer) {
Chris@127 1695
Chris@127 1696 paint.begin(this);
Chris@127 1697
Chris@211 1698 int playx = getXForFrame(m_playPointerFrame);
Chris@211 1699
Chris@287 1700 paint.setPen(getForeground());
Chris@211 1701 paint.drawLine(playx - 1, 0, playx - 1, height() - 1);
Chris@211 1702 paint.drawLine(playx + 1, 0, playx + 1, height() - 1);
Chris@211 1703 paint.drawPoint(playx, 0);
Chris@211 1704 paint.drawPoint(playx, height() - 1);
Chris@287 1705 paint.setPen(getBackground());
Chris@211 1706 paint.drawLine(playx, 1, playx, height() - 2);
Chris@127 1707
Chris@127 1708 paint.end();
Chris@127 1709 }
Chris@127 1710
Chris@127 1711 QFrame::paintEvent(e);
Chris@127 1712 }
Chris@127 1713
Chris@127 1714 void
Chris@127 1715 View::drawSelections(QPainter &paint)
Chris@127 1716 {
Chris@217 1717 if (!hasTopLayerTimeXAxis()) return;
Chris@217 1718
Chris@127 1719 MultiSelection::SelectionList selections;
Chris@127 1720
Chris@127 1721 if (m_manager) {
Chris@127 1722 selections = m_manager->getSelections();
Chris@127 1723 if (m_manager->haveInProgressSelection()) {
Chris@127 1724 bool exclusive;
Chris@127 1725 Selection inProgressSelection =
Chris@127 1726 m_manager->getInProgressSelection(exclusive);
Chris@127 1727 if (exclusive) selections.clear();
Chris@127 1728 selections.insert(inProgressSelection);
Chris@127 1729 }
Chris@127 1730 }
Chris@127 1731
Chris@127 1732 paint.save();
Chris@183 1733
Chris@183 1734 bool translucent = !areLayerColoursSignificant();
Chris@183 1735
Chris@183 1736 if (translucent) {
Chris@183 1737 paint.setBrush(QColor(150, 150, 255, 80));
Chris@183 1738 } else {
Chris@183 1739 paint.setBrush(Qt::NoBrush);
Chris@183 1740 }
Chris@127 1741
Chris@127 1742 int sampleRate = getModelsSampleRate();
Chris@127 1743
Chris@127 1744 QPoint localPos;
Chris@127 1745 long illuminateFrame = -1;
Chris@127 1746 bool closeToLeft, closeToRight;
Chris@127 1747
Chris@127 1748 if (shouldIlluminateLocalSelection(localPos, closeToLeft, closeToRight)) {
Chris@127 1749 illuminateFrame = getFrameForX(localPos.x());
Chris@127 1750 }
Chris@127 1751
Chris@127 1752 const QFontMetrics &metrics = paint.fontMetrics();
Chris@127 1753
Chris@127 1754 for (MultiSelection::SelectionList::iterator i = selections.begin();
Chris@127 1755 i != selections.end(); ++i) {
Chris@127 1756
Chris@333 1757 int p0 = getXForFrame(alignFromReference(i->getStartFrame()));
Chris@333 1758 int p1 = getXForFrame(alignFromReference(i->getEndFrame()));
Chris@127 1759
Chris@127 1760 if (p1 < 0 || p0 > width()) continue;
Chris@127 1761
Chris@127 1762 #ifdef DEBUG_VIEW_WIDGET_PAINT
Chris@127 1763 std::cerr << "View::drawSelections: " << p0 << ",-1 [" << (p1-p0) << "x" << (height()+1) << "]" << std::endl;
Chris@127 1764 #endif
Chris@127 1765
Chris@127 1766 bool illuminateThis =
Chris@127 1767 (illuminateFrame >= 0 && i->contains(illuminateFrame));
Chris@127 1768
Chris@127 1769 paint.setPen(QColor(150, 150, 255));
Chris@183 1770
Chris@183 1771 if (translucent && shouldLabelSelections()) {
Chris@183 1772 paint.drawRect(p0, -1, p1 - p0, height() + 1);
Chris@183 1773 } else {
Chris@183 1774 // Make the top & bottom lines of the box visible if we
Chris@183 1775 // are lacking some of the other visual cues. There's no
Chris@183 1776 // particular logic to this, it's just a question of what
Chris@183 1777 // I happen to think looks nice.
Chris@183 1778 paint.drawRect(p0, 0, p1 - p0, height() - 1);
Chris@183 1779 }
Chris@127 1780
Chris@127 1781 if (illuminateThis) {
Chris@127 1782 paint.save();
Chris@287 1783 paint.setPen(QPen(getForeground(), 2));
Chris@127 1784 if (closeToLeft) {
Chris@127 1785 paint.drawLine(p0, 1, p1, 1);
Chris@127 1786 paint.drawLine(p0, 0, p0, height());
Chris@127 1787 paint.drawLine(p0, height() - 1, p1, height() - 1);
Chris@127 1788 } else if (closeToRight) {
Chris@127 1789 paint.drawLine(p0, 1, p1, 1);
Chris@127 1790 paint.drawLine(p1, 0, p1, height());
Chris@127 1791 paint.drawLine(p0, height() - 1, p1, height() - 1);
Chris@127 1792 } else {
Chris@127 1793 paint.setBrush(Qt::NoBrush);
Chris@127 1794 paint.drawRect(p0, 1, p1 - p0, height() - 2);
Chris@127 1795 }
Chris@127 1796 paint.restore();
Chris@127 1797 }
Chris@127 1798
Chris@127 1799 if (sampleRate && shouldLabelSelections() && m_manager &&
Chris@189 1800 m_manager->shouldShowSelectionExtents()) {
Chris@127 1801
Chris@127 1802 QString startText = QString("%1 / %2")
Chris@127 1803 .arg(QString::fromStdString
Chris@127 1804 (RealTime::frame2RealTime
Chris@127 1805 (i->getStartFrame(), sampleRate).toText(true)))
Chris@127 1806 .arg(i->getStartFrame());
Chris@127 1807
Chris@127 1808 QString endText = QString(" %1 / %2")
Chris@127 1809 .arg(QString::fromStdString
Chris@127 1810 (RealTime::frame2RealTime
Chris@127 1811 (i->getEndFrame(), sampleRate).toText(true)))
Chris@127 1812 .arg(i->getEndFrame());
Chris@127 1813
Chris@127 1814 QString durationText = QString("(%1 / %2) ")
Chris@127 1815 .arg(QString::fromStdString
Chris@127 1816 (RealTime::frame2RealTime
Chris@127 1817 (i->getEndFrame() - i->getStartFrame(), sampleRate)
Chris@127 1818 .toText(true)))
Chris@127 1819 .arg(i->getEndFrame() - i->getStartFrame());
Chris@127 1820
Chris@127 1821 int sw = metrics.width(startText),
Chris@127 1822 ew = metrics.width(endText),
Chris@127 1823 dw = metrics.width(durationText);
Chris@127 1824
Chris@127 1825 int sy = metrics.ascent() + metrics.height() + 4;
Chris@127 1826 int ey = sy;
Chris@127 1827 int dy = sy + metrics.height();
Chris@127 1828
Chris@127 1829 int sx = p0 + 2;
Chris@127 1830 int ex = sx;
Chris@127 1831 int dx = sx;
Chris@127 1832
Chris@127 1833 if (sw + ew > (p1 - p0)) {
Chris@127 1834 ey += metrics.height();
Chris@127 1835 dy += metrics.height();
Chris@127 1836 }
Chris@127 1837
Chris@127 1838 if (ew < (p1 - p0)) {
Chris@127 1839 ex = p1 - 2 - ew;
Chris@127 1840 }
Chris@127 1841
Chris@127 1842 if (dw < (p1 - p0)) {
Chris@127 1843 dx = p1 - 2 - dw;
Chris@127 1844 }
Chris@127 1845
Chris@127 1846 paint.drawText(sx, sy, startText);
Chris@127 1847 paint.drawText(ex, ey, endText);
Chris@127 1848 paint.drawText(dx, dy, durationText);
Chris@127 1849 }
Chris@127 1850 }
Chris@127 1851
Chris@127 1852 paint.restore();
Chris@127 1853 }
Chris@127 1854
Chris@267 1855 void
Chris@270 1856 View::drawMeasurementRect(QPainter &paint, const Layer *topLayer, QRect r,
Chris@270 1857 bool focus) const
Chris@267 1858 {
Chris@268 1859 // std::cerr << "View::drawMeasurementRect(" << r.x() << "," << r.y() << " "
Chris@268 1860 // << r.width() << "x" << r.height() << ")" << std::endl;
Chris@268 1861
Chris@267 1862 if (r.x() + r.width() < 0 || r.x() >= width()) return;
Chris@267 1863
Chris@270 1864 if (r.width() != 0 || r.height() != 0) {
Chris@270 1865 paint.save();
Chris@270 1866 if (focus) {
Chris@270 1867 paint.setPen(Qt::NoPen);
Chris@272 1868 QColor brushColour(Qt::black);
Chris@272 1869 brushColour.setAlpha(hasLightBackground() ? 15 : 40);
Chris@270 1870 paint.setBrush(brushColour);
Chris@270 1871 if (r.x() > 0) {
Chris@270 1872 paint.drawRect(0, 0, r.x(), height());
Chris@270 1873 }
Chris@270 1874 if (r.x() + r.width() < width()) {
Chris@270 1875 paint.drawRect(r.x() + r.width(), 0, width()-r.x()-r.width(), height());
Chris@270 1876 }
Chris@270 1877 if (r.y() > 0) {
Chris@270 1878 paint.drawRect(r.x(), 0, r.width(), r.y());
Chris@270 1879 }
Chris@270 1880 if (r.y() + r.height() < height()) {
Chris@270 1881 paint.drawRect(r.x(), r.y() + r.height(), r.width(), height()-r.y()-r.height());
Chris@270 1882 }
Chris@270 1883 paint.setBrush(Qt::NoBrush);
Chris@270 1884 }
Chris@270 1885 paint.setPen(Qt::green);
Chris@270 1886 paint.drawRect(r);
Chris@270 1887 paint.restore();
Chris@270 1888 } else {
Chris@270 1889 paint.save();
Chris@270 1890 paint.setPen(Qt::green);
Chris@270 1891 paint.drawPoint(r.x(), r.y());
Chris@270 1892 paint.restore();
Chris@270 1893 }
Chris@270 1894
Chris@270 1895 if (!focus) return;
Chris@270 1896
Chris@278 1897 paint.save();
Chris@278 1898 QFont fn = paint.font();
Chris@278 1899 if (fn.pointSize() > 8) {
Chris@278 1900 fn.setPointSize(fn.pointSize() - 1);
Chris@278 1901 paint.setFont(fn);
Chris@278 1902 }
Chris@278 1903
Chris@267 1904 int fontHeight = paint.fontMetrics().height();
Chris@267 1905 int fontAscent = paint.fontMetrics().ascent();
Chris@267 1906
Chris@267 1907 float v0, v1;
Chris@267 1908 QString u0, u1;
Chris@267 1909 bool b0 = false, b1 = false;
Chris@267 1910
Chris@267 1911 QString axs, ays, bxs, bys, dxs, dys;
Chris@267 1912
Chris@267 1913 int axx, axy, bxx, bxy, dxx, dxy;
Chris@267 1914 int aw = 0, bw = 0, dw = 0;
Chris@267 1915
Chris@267 1916 int labelCount = 0;
Chris@267 1917
Chris@362 1918 // top-left point, x-coord
Chris@362 1919
Chris@267 1920 if ((b0 = topLayer->getXScaleValue(this, r.x(), v0, u0))) {
Chris@267 1921 axs = QString("%1 %2").arg(v0).arg(u0);
Chris@278 1922 if (u0 == "Hz" && Pitch::isFrequencyInMidiRange(v0)) {
Chris@278 1923 axs = QString("%1 (%2)").arg(axs)
Chris@278 1924 .arg(Pitch::getPitchLabelForFrequency(v0));
Chris@278 1925 }
Chris@267 1926 aw = paint.fontMetrics().width(axs);
Chris@267 1927 ++labelCount;
Chris@267 1928 }
Chris@362 1929
Chris@362 1930 // bottom-right point, x-coord
Chris@267 1931
Chris@267 1932 if (r.width() > 0) {
Chris@267 1933 if ((b1 = topLayer->getXScaleValue(this, r.x() + r.width(), v1, u1))) {
Chris@267 1934 bxs = QString("%1 %2").arg(v1).arg(u1);
Chris@278 1935 if (u1 == "Hz" && Pitch::isFrequencyInMidiRange(v1)) {
Chris@278 1936 bxs = QString("%1 (%2)").arg(bxs)
Chris@278 1937 .arg(Pitch::getPitchLabelForFrequency(v1));
Chris@278 1938 }
Chris@267 1939 bw = paint.fontMetrics().width(bxs);
Chris@267 1940 }
Chris@267 1941 }
Chris@362 1942
Chris@362 1943 // dimension, width
Chris@267 1944
Chris@283 1945 if (b0 && b1 && v1 != v0 && u0 == u1) {
Chris@362 1946 dxs = QString("[%1 %2]").arg(fabs(v1 - v0)).arg(u1);
Chris@267 1947 dw = paint.fontMetrics().width(dxs);
Chris@267 1948 }
Chris@267 1949
Chris@267 1950 b0 = false;
Chris@267 1951 b1 = false;
Chris@267 1952
Chris@362 1953 // top-left point, y-coord
Chris@362 1954
Chris@267 1955 if ((b0 = topLayer->getYScaleValue(this, r.y(), v0, u0))) {
Chris@267 1956 ays = QString("%1 %2").arg(v0).arg(u0);
Chris@278 1957 if (u0 == "Hz" && Pitch::isFrequencyInMidiRange(v0)) {
Chris@278 1958 ays = QString("%1 (%2)").arg(ays)
Chris@278 1959 .arg(Pitch::getPitchLabelForFrequency(v0));
Chris@278 1960 }
Chris@267 1961 aw = std::max(aw, paint.fontMetrics().width(ays));
Chris@267 1962 ++labelCount;
Chris@267 1963 }
Chris@267 1964
Chris@362 1965 // bottom-right point, y-coord
Chris@362 1966
Chris@267 1967 if (r.height() > 0) {
Chris@267 1968 if ((b1 = topLayer->getYScaleValue(this, r.y() + r.height(), v1, u1))) {
Chris@267 1969 bys = QString("%1 %2").arg(v1).arg(u1);
Chris@278 1970 if (u1 == "Hz" && Pitch::isFrequencyInMidiRange(v1)) {
Chris@278 1971 bys = QString("%1 (%2)").arg(bys)
Chris@278 1972 .arg(Pitch::getPitchLabelForFrequency(v1));
Chris@278 1973 }
Chris@267 1974 bw = std::max(bw, paint.fontMetrics().width(bys));
Chris@267 1975 }
Chris@267 1976 }
Chris@274 1977
Chris@274 1978 bool bd = false;
Chris@274 1979 float dy = 0.f;
Chris@274 1980 QString du;
Chris@274 1981
Chris@362 1982 // dimension, height
Chris@362 1983
Chris@274 1984 if ((bd = topLayer->getYScaleDifference(this, r.y(), r.y() + r.height(),
Chris@283 1985 dy, du)) &&
Chris@283 1986 dy != 0) {
Chris@274 1987 if (du != "") {
Chris@362 1988 if (du == "Hz") {
Chris@362 1989 int semis;
Chris@362 1990 float cents;
Chris@362 1991 semis = Pitch::getPitchForFrequencyDifference(v0, v1, &cents);
Chris@362 1992 dys = QString("[%1 %2 (%3)]")
Chris@362 1993 .arg(dy).arg(du)
Chris@362 1994 .arg(Pitch::getLabelForPitchRange(semis, cents));
Chris@362 1995 } else {
Chris@362 1996 dys = QString("[%1 %2]").arg(dy).arg(du);
Chris@362 1997 }
Chris@274 1998 } else {
Chris@362 1999 dys = QString("[%1]").arg(dy);
Chris@274 2000 }
Chris@267 2001 dw = std::max(dw, paint.fontMetrics().width(dys));
Chris@267 2002 }
Chris@267 2003
Chris@267 2004 int mw = r.width();
Chris@267 2005 int mh = r.height();
Chris@267 2006
Chris@267 2007 bool edgeLabelsInside = false;
Chris@267 2008 bool sizeLabelsInside = false;
Chris@267 2009
Chris@267 2010 if (mw < std::max(aw, std::max(bw, dw)) + 4) {
Chris@267 2011 // defaults stand
Chris@267 2012 } else if (mw < aw + bw + 4) {
Chris@267 2013 if (mh > fontHeight * labelCount * 3 + 4) {
Chris@267 2014 edgeLabelsInside = true;
Chris@267 2015 sizeLabelsInside = true;
Chris@267 2016 } else if (mh > fontHeight * labelCount * 2 + 4) {
Chris@267 2017 edgeLabelsInside = true;
Chris@267 2018 }
Chris@267 2019 } else if (mw < aw + bw + dw + 4) {
Chris@267 2020 if (mh > fontHeight * labelCount * 3 + 4) {
Chris@267 2021 edgeLabelsInside = true;
Chris@267 2022 sizeLabelsInside = true;
Chris@267 2023 } else if (mh > fontHeight * labelCount + 4) {
Chris@267 2024 edgeLabelsInside = true;
Chris@267 2025 }
Chris@267 2026 } else {
Chris@267 2027 if (mh > fontHeight * labelCount + 4) {
Chris@267 2028 edgeLabelsInside = true;
Chris@267 2029 sizeLabelsInside = true;
Chris@267 2030 }
Chris@267 2031 }
Chris@267 2032
Chris@267 2033 if (edgeLabelsInside) {
Chris@267 2034
Chris@267 2035 axx = r.x() + 2;
Chris@267 2036 axy = r.y() + fontAscent + 2;
Chris@267 2037
Chris@267 2038 bxx = r.x() + r.width() - bw - 2;
Chris@267 2039 bxy = r.y() + r.height() - (labelCount-1) * fontHeight - 2;
Chris@267 2040
Chris@267 2041 } else {
Chris@267 2042
Chris@267 2043 axx = r.x() - aw - 2;
Chris@267 2044 axy = r.y() + fontAscent;
Chris@267 2045
Chris@267 2046 bxx = r.x() + r.width() + 2;
Chris@267 2047 bxy = r.y() + r.height() - (labelCount-1) * fontHeight;
Chris@267 2048 }
Chris@267 2049
Chris@267 2050 dxx = r.width()/2 + r.x() - dw/2;
Chris@267 2051
Chris@267 2052 if (sizeLabelsInside) {
Chris@267 2053
Chris@267 2054 dxy = r.height()/2 + r.y() - (labelCount * fontHeight)/2 + fontAscent;
Chris@267 2055
Chris@267 2056 } else {
Chris@267 2057
Chris@267 2058 dxy = r.y() + r.height() + fontAscent + 2;
Chris@267 2059 }
Chris@267 2060
Chris@267 2061 if (axs != "") {
Chris@267 2062 drawVisibleText(paint, axx, axy, axs, OutlinedText);
Chris@267 2063 axy += fontHeight;
Chris@267 2064 }
Chris@267 2065
Chris@267 2066 if (ays != "") {
Chris@267 2067 drawVisibleText(paint, axx, axy, ays, OutlinedText);
Chris@267 2068 axy += fontHeight;
Chris@267 2069 }
Chris@267 2070
Chris@267 2071 if (bxs != "") {
Chris@267 2072 drawVisibleText(paint, bxx, bxy, bxs, OutlinedText);
Chris@267 2073 bxy += fontHeight;
Chris@267 2074 }
Chris@267 2075
Chris@267 2076 if (bys != "") {
Chris@267 2077 drawVisibleText(paint, bxx, bxy, bys, OutlinedText);
Chris@267 2078 bxy += fontHeight;
Chris@267 2079 }
Chris@267 2080
Chris@267 2081 if (dxs != "") {
Chris@267 2082 drawVisibleText(paint, dxx, dxy, dxs, OutlinedText);
Chris@267 2083 dxy += fontHeight;
Chris@267 2084 }
Chris@267 2085
Chris@267 2086 if (dys != "") {
Chris@267 2087 drawVisibleText(paint, dxx, dxy, dys, OutlinedText);
Chris@267 2088 dxy += fontHeight;
Chris@267 2089 }
Chris@278 2090
Chris@278 2091 paint.restore();
Chris@267 2092 }
Chris@267 2093
Chris@227 2094 bool
Chris@229 2095 View::render(QPainter &paint, int xorigin, size_t f0, size_t f1)
Chris@227 2096 {
Chris@227 2097 size_t x0 = f0 / m_zoomLevel;
Chris@227 2098 size_t x1 = f1 / m_zoomLevel;
Chris@227 2099
Chris@227 2100 size_t w = x1 - x0;
Chris@227 2101
Chris@227 2102 size_t origCentreFrame = m_centreFrame;
Chris@227 2103
Chris@227 2104 bool someLayersIncomplete = false;
Chris@227 2105
Chris@227 2106 for (LayerList::iterator i = m_layers.begin();
Chris@227 2107 i != m_layers.end(); ++i) {
Chris@227 2108
Chris@227 2109 int c = (*i)->getCompletion(this);
Chris@227 2110 if (c < 100) {
Chris@227 2111 someLayersIncomplete = true;
Chris@227 2112 break;
Chris@227 2113 }
Chris@227 2114 }
Chris@227 2115
Chris@227 2116 if (someLayersIncomplete) {
Chris@227 2117
Chris@227 2118 QProgressDialog progress(tr("Waiting for layers to be ready..."),
Chris@227 2119 tr("Cancel"), 0, 100, this);
Chris@227 2120
Chris@227 2121 int layerCompletion = 0;
Chris@227 2122
Chris@227 2123 while (layerCompletion < 100) {
Chris@227 2124
Chris@227 2125 for (LayerList::iterator i = m_layers.begin();
Chris@227 2126 i != m_layers.end(); ++i) {
Chris@227 2127
Chris@227 2128 int c = (*i)->getCompletion(this);
Chris@227 2129 if (i == m_layers.begin() || c < layerCompletion) {
Chris@227 2130 layerCompletion = c;
Chris@227 2131 }
Chris@227 2132 }
Chris@227 2133
Chris@227 2134 if (layerCompletion >= 100) break;
Chris@227 2135
Chris@227 2136 progress.setValue(layerCompletion);
Chris@227 2137 qApp->processEvents();
Chris@227 2138 if (progress.wasCanceled()) {
Chris@227 2139 update();
Chris@227 2140 return false;
Chris@227 2141 }
Chris@227 2142
Chris@227 2143 usleep(50000);
Chris@227 2144 }
Chris@227 2145 }
Chris@227 2146
Chris@227 2147 QProgressDialog progress(tr("Rendering image..."),
Chris@227 2148 tr("Cancel"), 0, w / width(), this);
Chris@227 2149
Chris@227 2150 for (size_t x = 0; x < w; x += width()) {
Chris@227 2151
Chris@227 2152 progress.setValue(x / width());
Chris@227 2153 qApp->processEvents();
Chris@227 2154 if (progress.wasCanceled()) {
Chris@227 2155 m_centreFrame = origCentreFrame;
Chris@227 2156 update();
Chris@227 2157 return false;
Chris@227 2158 }
Chris@227 2159
Chris@229 2160 m_centreFrame = f0 + (x + width()/2) * m_zoomLevel;
Chris@227 2161
Chris@227 2162 QRect chunk(0, 0, width(), height());
Chris@227 2163
Chris@287 2164 paint.setPen(getBackground());
Chris@287 2165 paint.setBrush(getBackground());
Chris@227 2166
Chris@229 2167 paint.drawRect(QRect(xorigin + x, 0, width(), height()));
Chris@227 2168
Chris@287 2169 paint.setPen(getForeground());
Chris@227 2170 paint.setBrush(Qt::NoBrush);
Chris@227 2171
Chris@227 2172 for (LayerList::iterator i = m_layers.begin();
Chris@227 2173 i != m_layers.end(); ++i) {
Chris@227 2174
Chris@227 2175 paint.setRenderHint(QPainter::Antialiasing, false);
Chris@227 2176
Chris@227 2177 paint.save();
Chris@229 2178 paint.translate(xorigin + x, 0);
Chris@227 2179
Chris@229 2180 std::cerr << "Centre frame now: " << m_centreFrame << " drawing to " << chunk.x() + x + xorigin << ", " << chunk.width() << std::endl;
Chris@227 2181
Chris@227 2182 (*i)->paint(this, paint, chunk);
Chris@227 2183
Chris@227 2184 paint.restore();
Chris@227 2185 }
Chris@227 2186 }
Chris@227 2187
Chris@227 2188 m_centreFrame = origCentreFrame;
Chris@227 2189 update();
Chris@227 2190 return true;
Chris@227 2191 }
Chris@227 2192
Chris@227 2193 QImage *
Chris@227 2194 View::toNewImage()
Chris@227 2195 {
Chris@227 2196 size_t f0 = getModelsStartFrame();
Chris@227 2197 size_t f1 = getModelsEndFrame();
Chris@227 2198
Chris@229 2199 return toNewImage(f0, f1);
Chris@229 2200 }
Chris@229 2201
Chris@229 2202 QImage *
Chris@229 2203 View::toNewImage(size_t f0, size_t f1)
Chris@229 2204 {
Chris@227 2205 size_t x0 = f0 / getZoomLevel();
Chris@227 2206 size_t x1 = f1 / getZoomLevel();
Chris@227 2207
Chris@227 2208 QImage *image = new QImage(x1 - x0, height(), QImage::Format_RGB32);
Chris@227 2209
Chris@227 2210 QPainter *paint = new QPainter(image);
Chris@229 2211 if (!render(*paint, 0, f0, f1)) {
Chris@227 2212 delete paint;
Chris@227 2213 delete image;
Chris@227 2214 return 0;
Chris@227 2215 } else {
Chris@227 2216 delete paint;
Chris@227 2217 return image;
Chris@227 2218 }
Chris@227 2219 }
Chris@227 2220
Chris@229 2221 QSize
Chris@229 2222 View::getImageSize()
Chris@229 2223 {
Chris@229 2224 size_t f0 = getModelsStartFrame();
Chris@229 2225 size_t f1 = getModelsEndFrame();
Chris@229 2226
Chris@229 2227 return getImageSize(f0, f1);
Chris@229 2228 }
Chris@229 2229
Chris@229 2230 QSize
Chris@229 2231 View::getImageSize(size_t f0, size_t f1)
Chris@229 2232 {
Chris@229 2233 size_t x0 = f0 / getZoomLevel();
Chris@229 2234 size_t x1 = f1 / getZoomLevel();
Chris@229 2235
Chris@229 2236 return QSize(x1 - x0, height());
Chris@229 2237 }
Chris@229 2238
Chris@316 2239 void
Chris@316 2240 View::toXml(QTextStream &stream,
Chris@316 2241 QString indent, QString extraAttributes) const
Chris@127 2242 {
Chris@316 2243 stream << indent;
Chris@127 2244
Chris@316 2245 stream << QString("<view "
Chris@316 2246 "centre=\"%1\" "
Chris@316 2247 "zoom=\"%2\" "
Chris@316 2248 "followPan=\"%3\" "
Chris@316 2249 "followZoom=\"%4\" "
Chris@316 2250 "tracking=\"%5\" "
Chris@316 2251 " %6>\n")
Chris@127 2252 .arg(m_centreFrame)
Chris@127 2253 .arg(m_zoomLevel)
Chris@127 2254 .arg(m_followPan)
Chris@127 2255 .arg(m_followZoom)
Chris@127 2256 .arg(m_followPlay == PlaybackScrollContinuous ? "scroll" :
Chris@127 2257 m_followPlay == PlaybackScrollPage ? "page" : "ignore")
Chris@127 2258 .arg(extraAttributes);
Chris@127 2259
Chris@127 2260 for (size_t i = 0; i < m_layers.size(); ++i) {
Chris@186 2261 bool visible = !m_layers[i]->isLayerDormant(this);
Chris@316 2262 m_layers[i]->toBriefXml(stream, indent + " ",
Chris@316 2263 QString("visible=\"%1\"")
Chris@316 2264 .arg(visible ? "true" : "false"));
Chris@127 2265 }
Chris@127 2266
Chris@316 2267 stream << indent + "</view>\n";
Chris@127 2268 }
Chris@127 2269
Chris@127 2270 ViewPropertyContainer::ViewPropertyContainer(View *v) :
Chris@127 2271 m_v(v)
Chris@127 2272 {
Chris@127 2273 connect(m_v, SIGNAL(propertyChanged(PropertyContainer::PropertyName)),
Chris@127 2274 this, SIGNAL(propertyChanged(PropertyContainer::PropertyName)));
Chris@127 2275 }
Chris@127 2276