annotate view/View.cpp @ 473:4f4f943bfdfc

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