annotate view/View.cpp @ 1553:76e4302a3fc2

Fix note numbering - ensure stable across whole track (as it used to be, but without scanning all notes in paint in order to do that)
author Chris Cannam
date Fri, 22 Nov 2019 14:12:50 +0000
parents bd6af89982d7
children 27f3e64489e1
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@1357 23 #include "base/HitCount.h"
Chris@919 24 #include "ViewProxy.h"
Chris@127 25
Chris@301 26 #include "layer/TimeRulerLayer.h"
Chris@287 27 #include "layer/SingleColourLayer.h"
Chris@1078 28 #include "layer/PaintAssistant.h"
Chris@1078 29
Chris@1354 30 #include "data/model/RelativelyFineZoomConstraint.h"
Chris@301 31 #include "data/model/RangeSummarisableTimeValueModel.h"
Chris@127 32
Chris@797 33 #include "widgets/IconLoader.h"
Chris@797 34
Chris@127 35 #include <QPainter>
Chris@127 36 #include <QPaintEvent>
Chris@127 37 #include <QRect>
Chris@127 38 #include <QApplication>
Chris@226 39 #include <QProgressDialog>
Chris@316 40 #include <QTextStream>
Chris@338 41 #include <QFont>
Chris@583 42 #include <QMessageBox>
Chris@797 43 #include <QPushButton>
Chris@956 44 #include <QSettings>
Chris@1202 45 #include <QSvgGenerator>
Chris@127 46
Chris@127 47 #include <iostream>
Chris@127 48 #include <cassert>
Chris@520 49 #include <cmath>
Chris@127 50
Chris@1352 51 //#define DEBUG_VIEW 1
Chris@1003 52 //#define DEBUG_VIEW_WIDGET_PAINT 1
Chris@1448 53 //#define DEBUG_PROGRESS_STUFF 1
Chris@1541 54 //#define DEBUG_VIEW_SCALE_CHOICE 1
Chris@127 55
Chris@127 56 View::View(QWidget *w, bool showProgress) :
Chris@127 57 QFrame(w),
Chris@1044 58 m_id(getNextId()),
Chris@127 59 m_centreFrame(0),
Chris@1326 60 m_zoomLevel(ZoomLevel::FramesPerPixel, 1024),
Chris@127 61 m_followPan(true),
Chris@127 62 m_followZoom(true),
Chris@815 63 m_followPlay(PlaybackScrollPageWithCentre),
Chris@789 64 m_followPlayIsDetached(false),
Chris@153 65 m_playPointerFrame(0),
Chris@127 66 m_showProgress(showProgress),
Chris@1408 67 m_cache(nullptr),
Chris@1408 68 m_buffer(nullptr),
Chris@1357 69 m_cacheValid(false),
Chris@127 70 m_cacheCentreFrame(0),
Chris@1326 71 m_cacheZoomLevel(ZoomLevel::FramesPerPixel, 1024),
Chris@127 72 m_selectionCached(false),
Chris@127 73 m_deleting(false),
Chris@127 74 m_haveSelectedLayer(false),
Chris@1490 75 m_useAligningProxy(false),
Chris@1496 76 m_alignmentProgressBar({ {}, nullptr }),
Chris@1408 77 m_manager(nullptr),
Chris@127 78 m_propertyContainer(new ViewPropertyContainer(this))
Chris@127 79 {
Chris@1506 80 // SVCERR << "View::View[" << getId() << "]" << endl;
Chris@127 81 }
Chris@127 82
Chris@127 83 View::~View()
Chris@127 84 {
Chris@1506 85 // SVCERR << "View::~View[" << getId() << "]" << endl;
Chris@127 86
Chris@127 87 m_deleting = true;
Chris@127 88 delete m_propertyContainer;
Chris@1215 89 delete m_cache;
Chris@1215 90 delete m_buffer;
Chris@127 91 }
Chris@127 92
Chris@127 93 PropertyContainer::PropertyList
Chris@127 94 View::getProperties() const
Chris@127 95 {
Chris@127 96 PropertyContainer::PropertyList list;
Chris@127 97 list.push_back("Global Scroll");
Chris@127 98 list.push_back("Global Zoom");
Chris@127 99 list.push_back("Follow Playback");
Chris@127 100 return list;
Chris@127 101 }
Chris@127 102
Chris@127 103 QString
Chris@127 104 View::getPropertyLabel(const PropertyName &pn) const
Chris@127 105 {
Chris@127 106 if (pn == "Global Scroll") return tr("Global Scroll");
Chris@127 107 if (pn == "Global Zoom") return tr("Global Zoom");
Chris@127 108 if (pn == "Follow Playback") return tr("Follow Playback");
Chris@127 109 return "";
Chris@127 110 }
Chris@127 111
Chris@127 112 PropertyContainer::PropertyType
Chris@127 113 View::getPropertyType(const PropertyContainer::PropertyName &name) const
Chris@127 114 {
Chris@127 115 if (name == "Global Scroll") return PropertyContainer::ToggleProperty;
Chris@127 116 if (name == "Global Zoom") return PropertyContainer::ToggleProperty;
Chris@127 117 if (name == "Follow Playback") return PropertyContainer::ValueProperty;
Chris@127 118 return PropertyContainer::InvalidProperty;
Chris@127 119 }
Chris@127 120
Chris@127 121 int
Chris@127 122 View::getPropertyRangeAndValue(const PropertyContainer::PropertyName &name,
Chris@1266 123 int *min, int *max, int *deflt) const
Chris@127 124 {
Chris@216 125 if (deflt) *deflt = 1;
Chris@127 126 if (name == "Global Scroll") return m_followPan;
Chris@127 127 if (name == "Global Zoom") return m_followZoom;
Chris@127 128 if (name == "Follow Playback") {
Chris@1266 129 if (min) *min = 0;
Chris@1266 130 if (max) *max = 2;
Chris@815 131 if (deflt) *deflt = int(PlaybackScrollPageWithCentre);
Chris@815 132 switch (m_followPlay) {
Chris@815 133 case PlaybackScrollContinuous: return 0;
Chris@815 134 case PlaybackScrollPageWithCentre: case PlaybackScrollPage: return 1;
Chris@815 135 case PlaybackIgnore: return 2;
Chris@815 136 }
Chris@127 137 }
Chris@127 138 if (min) *min = 0;
Chris@127 139 if (max) *max = 0;
Chris@216 140 if (deflt) *deflt = 0;
Chris@127 141 return 0;
Chris@127 142 }
Chris@127 143
Chris@127 144 QString
Chris@127 145 View::getPropertyValueLabel(const PropertyContainer::PropertyName &name,
Chris@1266 146 int value) const
Chris@127 147 {
Chris@127 148 if (name == "Follow Playback") {
Chris@1266 149 switch (value) {
Chris@1266 150 default:
Chris@1266 151 case 0: return tr("Scroll");
Chris@1266 152 case 1: return tr("Page");
Chris@1266 153 case 2: return tr("Off");
Chris@1266 154 }
Chris@127 155 }
Chris@127 156 return tr("<unknown>");
Chris@127 157 }
Chris@127 158
Chris@127 159 void
Chris@127 160 View::setProperty(const PropertyContainer::PropertyName &name, int value)
Chris@127 161 {
Chris@127 162 if (name == "Global Scroll") {
Chris@1266 163 setFollowGlobalPan(value != 0);
Chris@127 164 } else if (name == "Global Zoom") {
Chris@1266 165 setFollowGlobalZoom(value != 0);
Chris@127 166 } else if (name == "Follow Playback") {
Chris@1266 167 switch (value) {
Chris@1266 168 default:
Chris@1266 169 case 0: setPlaybackFollow(PlaybackScrollContinuous); break;
Chris@1266 170 case 1: setPlaybackFollow(PlaybackScrollPageWithCentre); break;
Chris@1266 171 case 2: setPlaybackFollow(PlaybackIgnore); break;
Chris@1266 172 }
Chris@127 173 }
Chris@127 174 }
Chris@127 175
Chris@806 176 int
Chris@127 177 View::getPropertyContainerCount() const
Chris@127 178 {
Chris@908 179 return int(m_fixedOrderLayers.size()) + 1; // the 1 is for me
Chris@127 180 }
Chris@127 181
Chris@127 182 const PropertyContainer *
Chris@806 183 View::getPropertyContainer(int i) const
Chris@127 184 {
Chris@127 185 return (const PropertyContainer *)(((View *)this)->
Chris@1266 186 getPropertyContainer(i));
Chris@127 187 }
Chris@127 188
Chris@127 189 PropertyContainer *
Chris@806 190 View::getPropertyContainer(int i)
Chris@127 191 {
Chris@127 192 if (i == 0) return m_propertyContainer;
Chris@837 193 return m_fixedOrderLayers[i-1];
Chris@127 194 }
Chris@127 195
Chris@127 196 bool
Chris@1537 197 View::getVisibleExtentsForUnit(QString unit,
Chris@1537 198 double &min, double &max,
Chris@1537 199 bool &log) const
Chris@127 200 {
Chris@1541 201 #ifdef DEBUG_VIEW_SCALE_CHOICE
Chris@1541 202 SVCERR << "View[" << getId() << "]::getVisibleExtentsForUnit("
Chris@1541 203 << unit << ")" << endl;
Chris@1541 204 #endif
Chris@1541 205
Chris@1539 206 Layer *layer = getScaleProvidingLayerForUnit(unit);
Chris@1539 207
Chris@1539 208 QString layerUnit;
Chris@1539 209 double layerMin, layerMax;
Chris@1539 210
Chris@1541 211 if (!layer) {
Chris@1541 212 #ifdef DEBUG_VIEW_SCALE_CHOICE
Chris@1541 213 SVCERR << "View[" << getId() << "]::getVisibleExtentsForUnit("
Chris@1541 214 << unit << "): No scale-providing layer for this unit, "
Chris@1541 215 << "taking union of extents of layers with this unit" << endl;
Chris@1541 216 #endif
Chris@1541 217 bool haveAny = false;
Chris@1541 218 bool layerLog;
Chris@1541 219 for (auto i = m_layerStack.rbegin(); i != m_layerStack.rend(); ++i) {
Chris@1541 220 Layer *layer = *i;
Chris@1541 221 if (layer->getValueExtents(layerMin, layerMax,
Chris@1541 222 layerLog, layerUnit)) {
Chris@1541 223 if (unit.toLower() != layerUnit.toLower()) {
Chris@1541 224 continue;
Chris@1541 225 }
Chris@1541 226 if (!haveAny || layerMin < min) {
Chris@1541 227 min = layerMin;
Chris@1541 228 }
Chris@1541 229 if (!haveAny || layerMax > max) {
Chris@1541 230 max = layerMax;
Chris@1541 231 }
Chris@1541 232 if (!haveAny || layerLog) {
Chris@1541 233 log = layerLog;
Chris@1541 234 }
Chris@1541 235 haveAny = true;
Chris@1541 236 }
Chris@1539 237 }
Chris@1541 238 return haveAny;
Chris@1539 239 }
Chris@1541 240
Chris@1541 241 return (layer->getValueExtents(layerMin, layerMax, log, layerUnit) &&
Chris@1541 242 layer->getDisplayExtents(min, max));
Chris@1539 243 }
Chris@1539 244
Chris@1539 245 Layer *
Chris@1539 246 View::getScaleProvidingLayerForUnit(QString unit) const
Chris@1539 247 {
Chris@1541 248 // Return the layer which is used to provide the min/max/log for
Chris@1541 249 // any auto-align layer of a given unit. This is also the layer
Chris@1548 250 // that will draw the scale, if possible.
Chris@1548 251 //
Chris@1548 252 // The returned layer is
Chris@1548 253 //
Chris@1548 254 // - the topmost visible layer having that unit that is not also
Chris@1548 255 // auto-aligning; or if there is no such layer,
Chris@1548 256 //
Chris@1548 257 // - the topmost layer of any visibility having that unit that is
Chris@1548 258 // not also auto-aligning (because a dormant layer can still draw
Chris@1548 259 // a scale, and it makes sense for layers aligned to it not to
Chris@1548 260 // jump about when its visibility is toggled); or if there is no
Chris@1548 261 // such layer,
Chris@1548 262 //
Chris@1548 263 // - none
Chris@1548 264
Chris@1548 265 Layer *dormantOption = nullptr;
Chris@1537 266
Chris@1537 267 for (auto i = m_layerStack.rbegin(); i != m_layerStack.rend(); ++i) {
Chris@1537 268
Chris@1537 269 Layer *layer = *i;
Chris@1537 270
Chris@1541 271 #ifdef DEBUG_VIEW_SCALE_CHOICE
Chris@1541 272 SVCERR << "View[" << getId() << "]::getScaleProvidingLayerForUnit("
Chris@1541 273 << unit << "): Looking at layer " << layer
Chris@1541 274 << " (" << layer->getLayerPresentationName() << ")" << endl;
Chris@1541 275 #endif
Chris@1537 276
Chris@127 277 QString layerUnit;
Chris@904 278 double layerMin = 0.0, layerMax = 0.0;
Chris@1537 279 bool layerLog = false;
Chris@1537 280
Chris@1537 281 if (!layer->getValueExtents(layerMin, layerMax, layerLog, layerUnit)) {
Chris@1541 282 #ifdef DEBUG_VIEW_SCALE_CHOICE
Chris@1541 283 SVCERR << "... it has no value extents" << endl;
Chris@1541 284 #endif
Chris@1537 285 continue;
Chris@1537 286 }
Chris@1548 287
Chris@1537 288 if (layerUnit.toLower() != unit.toLower()) {
Chris@1541 289 #ifdef DEBUG_VIEW_SCALE_CHOICE
Chris@1541 290 SVCERR << "... it has the wrong unit (" << layerUnit << ")" << endl;
Chris@1541 291 #endif
Chris@1537 292 continue;
Chris@1537 293 }
Chris@1537 294
Chris@1541 295 double displayMin = 0.0, displayMax = 0.0;
Chris@1541 296 if (!layer->getDisplayExtents(displayMin, displayMax)) {
Chris@1541 297 #ifdef DEBUG_VIEW_SCALE_CHOICE
Chris@1541 298 SVCERR << "... it has no display extents (is auto-aligning or not alignable)" << endl;
Chris@1541 299 #endif
Chris@1541 300 continue;
Chris@1541 301 }
Chris@1541 302
Chris@1548 303 if (layer->isLayerDormant(this)) {
Chris@1548 304 #ifdef DEBUG_VIEW_SCALE_CHOICE
Chris@1548 305 SVCERR << "... it's dormant" << endl;
Chris@1548 306 #endif
Chris@1548 307 if (!dormantOption) {
Chris@1548 308 dormantOption = layer;
Chris@1548 309 }
Chris@1548 310 continue;
Chris@1548 311 }
Chris@1548 312
Chris@1541 313 #ifdef DEBUG_VIEW_SCALE_CHOICE
Chris@1541 314 SVCERR << "... it's good" << endl;
Chris@1541 315 #endif
Chris@1539 316 return layer;
Chris@127 317 }
Chris@127 318
Chris@1548 319 return dormantOption;
Chris@127 320 }
Chris@127 321
Chris@1537 322 bool
Chris@1537 323 View::getVisibleExtentsForAnyUnit(double &min, double &max,
Chris@1537 324 bool &log, QString &unit) const
Chris@1537 325 {
Chris@1537 326 bool have = false;
Chris@1537 327
Chris@1537 328 // Iterate in reverse order, so as to return display extents of
Chris@1537 329 // topmost layer that fits the bill
Chris@1537 330
Chris@1537 331 for (auto i = m_layerStack.rbegin(); i != m_layerStack.rend(); ++i) {
Chris@1537 332
Chris@1537 333 Layer *layer = *i;
Chris@1537 334
Chris@1537 335 if (layer->isLayerDormant(this)) {
Chris@1537 336 continue;
Chris@1537 337 }
Chris@1537 338
Chris@1537 339 QString layerUnit;
Chris@1537 340 double layerMin = 0.0, layerMax = 0.0;
Chris@1537 341 bool layerLog = false;
Chris@1537 342
Chris@1537 343 if (!layer->getValueExtents(layerMin, layerMax, layerLog, layerUnit)) {
Chris@1537 344 continue;
Chris@1537 345 }
Chris@1537 346 if (layerUnit == "") {
Chris@1537 347 continue;
Chris@1537 348 }
Chris@1537 349
Chris@1537 350 double displayMin = 0.0, displayMax = 0.0;
Chris@1537 351
Chris@1537 352 if (layer->getDisplayExtents(displayMin, displayMax)) {
Chris@1537 353
Chris@1537 354 min = displayMin;
Chris@1537 355 max = displayMax;
Chris@1537 356 log = layerLog;
Chris@1537 357 unit = layerUnit;
Chris@1537 358 have = true;
Chris@1537 359 break;
Chris@1537 360 }
Chris@1537 361 }
Chris@1537 362
Chris@1537 363 return have;
Chris@1537 364 }
Chris@1537 365
Chris@127 366 int
Chris@1537 367 View::getTextLabelYCoord(const Layer *layer, QPainter &paint) const
Chris@127 368 {
Chris@127 369 std::map<int, Layer *> sortedLayers;
Chris@127 370
Chris@835 371 for (LayerList::const_iterator i = m_layerStack.begin();
Chris@835 372 i != m_layerStack.end(); ++i) {
Chris@127 373 if ((*i)->needsTextLabelHeight()) {
Chris@1439 374 sortedLayers[(*i)->getExportId()] = *i;
Chris@127 375 }
Chris@127 376 }
Chris@127 377
Chris@1402 378 int y = scalePixelSize(15) + paint.fontMetrics().ascent();
Chris@127 379
Chris@127 380 for (std::map<int, Layer *>::const_iterator i = sortedLayers.begin();
Chris@127 381 i != sortedLayers.end(); ++i) {
Chris@1273 382 if (i->second == layer) break;
Chris@127 383 y += paint.fontMetrics().height();
Chris@127 384 }
Chris@127 385
Chris@127 386 return y;
Chris@127 387 }
Chris@127 388
Chris@127 389 void
Chris@127 390 View::propertyContainerSelected(View *client, PropertyContainer *pc)
Chris@127 391 {
Chris@127 392 if (client != this) return;
Chris@127 393
Chris@127 394 if (pc == m_propertyContainer) {
Chris@1266 395 if (m_haveSelectedLayer) {
Chris@1266 396 m_haveSelectedLayer = false;
Chris@1266 397 update();
Chris@1266 398 }
Chris@1266 399 return;
Chris@127 400 }
Chris@127 401
Chris@1357 402 m_cacheValid = false;
Chris@127 403
Chris@1408 404 Layer *selectedLayer = nullptr;
Chris@127 405
Chris@835 406 for (LayerList::iterator i = m_layerStack.begin(); i != m_layerStack.end(); ++i) {
Chris@1266 407 if (*i == pc) {
Chris@1266 408 selectedLayer = *i;
Chris@1266 409 m_layerStack.erase(i);
Chris@1266 410 break;
Chris@1266 411 }
Chris@127 412 }
Chris@127 413
Chris@127 414 if (selectedLayer) {
Chris@1266 415 m_haveSelectedLayer = true;
Chris@1266 416 m_layerStack.push_back(selectedLayer);
Chris@1266 417 update();
Chris@127 418 } else {
Chris@1266 419 m_haveSelectedLayer = false;
Chris@127 420 }
Chris@298 421
Chris@298 422 emit propertyContainerSelected(pc);
Chris@127 423 }
Chris@127 424
Chris@127 425 void
Chris@127 426 View::toolModeChanged()
Chris@127 427 {
Chris@587 428 // SVDEBUG << "View::toolModeChanged(" << m_manager->getToolMode() << ")" << endl;
Chris@127 429 }
Chris@127 430
Chris@133 431 void
Chris@133 432 View::overlayModeChanged()
Chris@133 433 {
Chris@1357 434 m_cacheValid = false;
Chris@133 435 update();
Chris@133 436 }
Chris@133 437
Chris@133 438 void
Chris@133 439 View::zoomWheelsEnabledChanged()
Chris@133 440 {
Chris@133 441 // subclass might override this
Chris@133 442 }
Chris@133 443
Chris@908 444 sv_frame_t
Chris@127 445 View::getStartFrame() const
Chris@127 446 {
Chris@313 447 return getFrameForX(0);
Chris@127 448 }
Chris@127 449
Chris@908 450 sv_frame_t
Chris@127 451 View::getEndFrame() const
Chris@127 452 {
Chris@127 453 return getFrameForX(width()) - 1;
Chris@127 454 }
Chris@127 455
Chris@127 456 void
Chris@908 457 View::setStartFrame(sv_frame_t f)
Chris@127 458 {
Chris@1326 459 setCentreFrame(f + sv_frame_t(round
Chris@1326 460 (m_zoomLevel.pixelsToFrames(width() / 2))));
Chris@127 461 }
Chris@127 462
Chris@127 463 bool
Chris@908 464 View::setCentreFrame(sv_frame_t f, bool e)
Chris@127 465 {
Chris@127 466 bool changeVisible = false;
Chris@127 467
Chris@1329 468 #ifdef DEBUG_VIEW
Chris@1506 469 SVCERR << "View[" << getId() << "]::setCentreFrame: from " << m_centreFrame
Chris@1329 470 << " to " << f << endl;
Chris@1329 471 #endif
Chris@1329 472
Chris@127 473 if (m_centreFrame != f) {
Chris@127 474
Chris@1327 475 sv_frame_t formerCentre = m_centreFrame;
Chris@1266 476 m_centreFrame = f;
Chris@1266 477
Chris@1327 478 if (m_zoomLevel.zone == ZoomLevel::PixelsPerFrame) {
Chris@127 479
Chris@1363 480 #ifdef DEBUG_VIEW
Chris@1506 481 SVCERR << "View[" << getId() << "]::setCentreFrame: in PixelsPerFrame zone, so change must be visible" << endl;
Chris@127 482 #endif
Chris@1266 483 update();
Chris@1266 484 changeVisible = true;
Chris@1327 485
Chris@1327 486 } else {
Chris@1327 487
Chris@1327 488 int formerPixel = int(formerCentre / m_zoomLevel.level);
Chris@1327 489 int newPixel = int(m_centreFrame / m_zoomLevel.level);
Chris@1327 490
Chris@1327 491 if (newPixel != formerPixel) {
Chris@1327 492
Chris@1363 493 #ifdef DEBUG_VIEW
Chris@1506 494 SVCERR << "View[" << getId() << "]::setCentreFrame: newPixel " << newPixel << ", formerPixel " << formerPixel << endl;
Chris@1327 495 #endif
Chris@1329 496 // ensure the centre frame is a multiple of the zoom level
Chris@1329 497 m_centreFrame = sv_frame_t(newPixel) * m_zoomLevel.level;
Chris@1363 498
Chris@1363 499 #ifdef DEBUG_VIEW
Chris@1506 500 SVCERR << "View[" << getId()
Chris@1506 501 << "]::setCentreFrame: centre frame rounded to "
Chris@1363 502 << m_centreFrame << " (zoom level is "
Chris@1363 503 << m_zoomLevel.level << ")" << endl;
Chris@1363 504 #endif
Chris@1329 505
Chris@1327 506 update();
Chris@1327 507 changeVisible = true;
Chris@1327 508 }
Chris@1266 509 }
Chris@1266 510
Chris@1266 511 if (e) {
Chris@1363 512 sv_frame_t rf = alignToReference(m_centreFrame);
Chris@830 513 #ifdef DEBUG_VIEW
Chris@1506 514 SVCERR << "View[" << getId() << "]::setCentreFrame(" << f
Chris@1363 515 << "): m_centreFrame = " << m_centreFrame
Chris@1363 516 << ", emitting centreFrameChanged with aligned frame "
Chris@1363 517 << rf << endl;
Chris@355 518 #endif
Chris@333 519 emit centreFrameChanged(rf, m_followPan, m_followPlay);
Chris@333 520 }
Chris@127 521 }
Chris@127 522
Chris@127 523 return changeVisible;
Chris@127 524 }
Chris@127 525
Chris@127 526 int
Chris@908 527 View::getXForFrame(sv_frame_t frame) const
Chris@127 528 {
Chris@1341 529 // In FramesPerPixel mode, the pixel should be the one "covering"
Chris@1341 530 // the given frame, i.e. to the "left" of it - not necessarily the
Chris@1341 531 // nearest boundary.
Chris@1341 532
Chris@1341 533 sv_frame_t level = m_zoomLevel.level;
Chris@1507 534 sv_frame_t fdiff = frame - m_centreFrame;
Chris@1375 535 int result = 0;
Chris@1375 536
Chris@1375 537 bool inRange = false;
Chris@1327 538 if (m_zoomLevel.zone == ZoomLevel::FramesPerPixel) {
Chris@1375 539 inRange = ((fdiff / level) < sv_frame_t(INT_MAX) &&
Chris@1375 540 (fdiff / level) > sv_frame_t(INT_MIN));
Chris@1375 541 } else {
Chris@1375 542 inRange = (fdiff < sv_frame_t(INT_MAX) / level &&
Chris@1375 543 fdiff > sv_frame_t(INT_MIN) / level);
Chris@1375 544 }
Chris@1375 545
Chris@1375 546 if (inRange) {
Chris@1375 547
Chris@1375 548 sv_frame_t adjusted;
Chris@1375 549
Chris@1375 550 if (m_zoomLevel.zone == ZoomLevel::FramesPerPixel) {
Chris@1507 551 sv_frame_t roundedCentreFrame = (m_centreFrame / level) * level;
Chris@1507 552 fdiff = frame - roundedCentreFrame;
Chris@1375 553 adjusted = fdiff / level;
Chris@1375 554 if ((fdiff < 0) && ((fdiff % level) != 0)) {
Chris@1375 555 --adjusted; // round to the left
Chris@1375 556 }
Chris@1375 557 } else {
Chris@1375 558 adjusted = fdiff * level;
Chris@1341 559 }
Chris@1375 560
Chris@1375 561 adjusted = adjusted + (width()/2);
Chris@1375 562
Chris@1375 563 if (adjusted > INT_MAX || adjusted < INT_MIN) {
Chris@1375 564 inRange = false;
Chris@1375 565 } else {
Chris@1375 566 result = int(adjusted);
Chris@1375 567 }
Chris@1327 568 }
Chris@1327 569
Chris@1375 570 if (!inRange) {
Chris@1375 571 SVCERR << "ERROR: Frame " << frame
Chris@1375 572 << " is out of range in View::getXForFrame" << endl;
Chris@1375 573 SVCERR << "ERROR: (centre frame = " << getCentreFrame() << ", fdiff = "
Chris@1375 574 << fdiff << ", zoom level = " << m_zoomLevel << ")" << endl;
Chris@1375 575 SVCERR << "ERROR: This is a logic error: getXForFrame should not be "
Chris@1375 576 << "called for locations unadjacent to the current view"
Chris@1375 577 << endl;
Chris@1375 578 return 0;
Chris@1375 579 }
Chris@1375 580
Chris@1507 581 #ifdef DEBUG_VIEW
Chris@1507 582 if (m_zoomLevel.zone == ZoomLevel::PixelsPerFrame) {
Chris@1507 583 sv_frame_t reversed = getFrameForX(result);
Chris@1507 584 if (reversed != frame) {
Chris@1507 585 SVCERR << "View[" << getId() << "]::getXForFrame: WARNING: Converted frame " << frame << " to x " << result << " in PixelsPerFrame zone, but the reverse conversion gives frame " << reversed << " (error = " << reversed - frame << ")" << endl;
Chris@1507 586 SVCERR << "(centre frame = " << getCentreFrame() << ", fdiff = "
Chris@1507 587 << fdiff << ", level = " << level << ", centre % level = "
Chris@1507 588 << (getCentreFrame() % level) << ", fdiff % level = "
Chris@1507 589 << (fdiff % level) << ", frame % level = "
Chris@1507 590 << (frame % level) << ", reversed % level = "
Chris@1507 591 << (reversed % level) << ")" << endl;
Chris@1507 592 }
Chris@1507 593 }
Chris@1507 594 #endif
Chris@1507 595
Chris@1341 596 return result;
Chris@127 597 }
Chris@127 598
Chris@908 599 sv_frame_t
Chris@127 600 View::getFrameForX(int x) const
Chris@127 601 {
Chris@1330 602 // Note, this must always return a value that is on a zoom-level
Chris@1330 603 // boundary - regardless of whether the nominal centre frame is on
Chris@1507 604 // such a boundary or not. (It is legitimate for the centre frame
Chris@1507 605 // not to be on a zoom-level boundary, because the centre frame
Chris@1507 606 // may be shared with other views having different zoom levels.)
Chris@1507 607
Chris@1507 608 // In FramesPerPixel mode, the frame returned for a given x should
Chris@1507 609 // be the first for which getXForFrame(frame) == x; a corollary is
Chris@1507 610 // that if the centre frame is not on a zoom-level boundary, then
Chris@1507 611 // getFrameForX(x) should not return the centre frame for any x.
Chris@1341 612
Chris@1507 613 // In PixelsPerFrame mode, the frame returned should be the one
Chris@1507 614 // immediately left of the given pixel, not necessarily the
Chris@1507 615 // nearest.
Chris@1330 616
Chris@1327 617 int diff = x - (width()/2);
Chris@1330 618 sv_frame_t level = m_zoomLevel.level;
Chris@1330 619 sv_frame_t fdiff, result;
Chris@1330 620
Chris@1327 621 if (m_zoomLevel.zone == ZoomLevel::FramesPerPixel) {
Chris@1507 622 sv_frame_t roundedCentreFrame = (m_centreFrame / level) * level;
Chris@1330 623 fdiff = diff * level;
Chris@1507 624 result = fdiff + roundedCentreFrame;
Chris@1327 625 } else {
Chris@1330 626 fdiff = diff / level;
Chris@1341 627 if ((diff < 0) && ((diff % level) != 0)) {
Chris@1341 628 --fdiff; // round to the left
Chris@1341 629 }
Chris@1330 630 result = fdiff + m_centreFrame;
Chris@1327 631 }
Chris@1341 632
Chris@1363 633 #ifdef DEBUG_VIEW_WIDGET_PAINT
Chris@1507 634 /*
Chris@1330 635 if (x == 0) {
Chris@1330 636 SVCERR << "getFrameForX(" << x << "): diff = " << diff << ", fdiff = "
Chris@1330 637 << fdiff << ", m_centreFrame = " << m_centreFrame
Chris@1330 638 << ", level = " << m_zoomLevel.level
Chris@1330 639 << ", diff % level = " << (diff % m_zoomLevel.level)
Chris@1330 640 << ", nominal " << fdiff + m_centreFrame
Chris@1330 641 << ", will return " << result
Chris@1330 642 << endl;
Chris@1330 643 }
Chris@1507 644 */
Chris@1507 645 #endif
Chris@1507 646
Chris@1507 647 #ifdef DEBUG_VIEW
Chris@1507 648 if (m_zoomLevel.zone == ZoomLevel::FramesPerPixel) {
Chris@1507 649 int reversed = getXForFrame(result);
Chris@1507 650 if (reversed != x) {
Chris@1507 651 SVCERR << "View[" << getId() << "]::getFrameForX: WARNING: Converted pixel " << x << " to frame " << result << " in FramesPerPixel zone, but the reverse conversion gives pixel " << reversed << " (error = " << reversed - x << ")" << endl;
Chris@1507 652 SVCERR << "(centre frame = " << getCentreFrame()
Chris@1507 653 << ", width/2 = " << width()/2 << ", diff = " << diff
Chris@1507 654 << ", fdiff = " << fdiff << ", level = " << level
Chris@1507 655 << ", centre % level = " << (getCentreFrame() % level)
Chris@1507 656 << ", fdiff % level = " << (fdiff % level)
Chris@1507 657 << ", frame % level = " << (result % level) << ")" << endl;
Chris@1507 658 }
Chris@1507 659 }
Chris@1352 660 #endif
Chris@1352 661
Chris@1330 662 return result;
Chris@127 663 }
Chris@127 664
Chris@904 665 double
Chris@904 666 View::getYForFrequency(double frequency,
Chris@1266 667 double minf,
Chris@1266 668 double maxf,
Chris@1266 669 bool logarithmic) const
Chris@127 670 {
Chris@382 671 Profiler profiler("View::getYForFrequency");
Chris@382 672
Chris@127 673 int h = height();
Chris@127 674
Chris@127 675 if (logarithmic) {
Chris@127 676
Chris@1266 677 static double lastminf = 0.0, lastmaxf = 0.0;
Chris@1266 678 static double logminf = 0.0, logmaxf = 0.0;
Chris@1266 679
Chris@1266 680 if (lastminf != minf) {
Chris@1266 681 lastminf = (minf == 0.0 ? 1.0 : minf);
Chris@1266 682 logminf = log10(minf);
Chris@1266 683 }
Chris@1266 684 if (lastmaxf != maxf) {
Chris@1266 685 lastmaxf = (maxf < lastminf ? lastminf : maxf);
Chris@1266 686 logmaxf = log10(maxf);
Chris@1266 687 }
Chris@1266 688
Chris@1266 689 if (logminf == logmaxf) return 0;
Chris@1266 690 return h - (h * (log10(frequency) - logminf)) / (logmaxf - logminf);
Chris@127 691
Chris@127 692 } else {
Chris@1266 693
Chris@1266 694 if (minf == maxf) return 0;
Chris@1266 695 return h - (h * (frequency - minf)) / (maxf - minf);
Chris@127 696 }
Chris@127 697 }
Chris@127 698
Chris@904 699 double
Chris@1085 700 View::getFrequencyForY(double y,
Chris@1266 701 double minf,
Chris@1266 702 double maxf,
Chris@1266 703 bool logarithmic) const
Chris@127 704 {
Chris@1085 705 double h = height();
Chris@127 706
Chris@127 707 if (logarithmic) {
Chris@127 708
Chris@1266 709 static double lastminf = 0.0, lastmaxf = 0.0;
Chris@1266 710 static double logminf = 0.0, logmaxf = 0.0;
Chris@1266 711
Chris@1266 712 if (lastminf != minf) {
Chris@1266 713 lastminf = (minf == 0.0 ? 1.0 : minf);
Chris@1266 714 logminf = log10(minf);
Chris@1266 715 }
Chris@1266 716 if (lastmaxf != maxf) {
Chris@1266 717 lastmaxf = (maxf < lastminf ? lastminf : maxf);
Chris@1266 718 logmaxf = log10(maxf);
Chris@1266 719 }
Chris@1266 720
Chris@1266 721 if (logminf == logmaxf) return 0;
Chris@1266 722 return pow(10.0, logminf + ((logmaxf - logminf) * (h - y)) / h);
Chris@127 723
Chris@127 724 } else {
Chris@127 725
Chris@1266 726 if (minf == maxf) return 0;
Chris@1266 727 return minf + ((h - y) * (maxf - minf)) / h;
Chris@127 728 }
Chris@127 729 }
Chris@127 730
Chris@1183 731 ZoomLevel
Chris@127 732 View::getZoomLevel() const
Chris@127 733 {
Chris@127 734 #ifdef DEBUG_VIEW_WIDGET_PAINT
Chris@1266 735 // cout << "zoom level: " << m_zoomLevel << endl;
Chris@127 736 #endif
Chris@127 737 return m_zoomLevel;
Chris@127 738 }
Chris@127 739
Chris@956 740 int
Chris@956 741 View::effectiveDevicePixelRatio() const
Chris@956 742 {
Chris@956 743 #ifdef Q_OS_MAC
Chris@956 744 int dpratio = devicePixelRatio();
Chris@956 745 if (dpratio > 1) {
Chris@956 746 QSettings settings;
Chris@956 747 settings.beginGroup("Preferences");
Chris@956 748 if (!settings.value("scaledHiDpi", true).toBool()) {
Chris@956 749 dpratio = 1;
Chris@956 750 }
Chris@956 751 settings.endGroup();
Chris@956 752 }
Chris@956 753 return dpratio;
Chris@956 754 #else
Chris@956 755 return 1;
Chris@956 756 #endif
Chris@956 757 }
Chris@956 758
Chris@127 759 void
Chris@1183 760 View::setZoomLevel(ZoomLevel z)
Chris@127 761 {
Chris@1327 762 //!!! int dpratio = effectiveDevicePixelRatio();
Chris@1327 763 // if (z < dpratio) return;
Chris@1327 764 // if (z < 1) z = 1;
Chris@1327 765 if (m_zoomLevel == z) {
Chris@1327 766 return;
Chris@127 767 }
Chris@1327 768 m_zoomLevel = z;
Chris@1327 769 emit zoomLevelChanged(z, m_followZoom);
Chris@1327 770 update();
Chris@127 771 }
Chris@127 772
Chris@224 773 bool
Chris@224 774 View::hasLightBackground() const
Chris@224 775 {
Chris@287 776 bool darkPalette = false;
Chris@292 777 if (m_manager) darkPalette = m_manager->getGlobalDarkBackground();
Chris@287 778
Chris@287 779 Layer::ColourSignificance maxSignificance = Layer::ColourAbsent;
Chris@287 780 bool mostSignificantHasDarkBackground = false;
Chris@287 781
Chris@835 782 for (LayerList::const_iterator i = m_layerStack.begin();
Chris@835 783 i != m_layerStack.end(); ++i) {
Chris@287 784
Chris@287 785 Layer::ColourSignificance s = (*i)->getLayerColourSignificance();
Chris@287 786 bool light = (*i)->hasLightBackground();
Chris@287 787
Chris@287 788 if (int(s) > int(maxSignificance)) {
Chris@287 789 maxSignificance = s;
Chris@287 790 mostSignificantHasDarkBackground = !light;
Chris@287 791 } else if (s == maxSignificance && !light) {
Chris@287 792 mostSignificantHasDarkBackground = true;
Chris@287 793 }
Chris@224 794 }
Chris@287 795
Chris@1314 796 if (int(maxSignificance) >= int(Layer::ColourDistinguishes)) {
Chris@287 797 return !mostSignificantHasDarkBackground;
Chris@287 798 } else {
Chris@287 799 return !darkPalette;
Chris@287 800 }
Chris@287 801 }
Chris@287 802
Chris@287 803 QColor
Chris@287 804 View::getBackground() const
Chris@287 805 {
Chris@287 806 bool light = hasLightBackground();
Chris@287 807
Chris@287 808 QColor widgetbg = palette().window().color();
Chris@287 809 bool widgetLight =
Chris@287 810 (widgetbg.red() + widgetbg.green() + widgetbg.blue()) > 384;
Chris@287 811
Chris@296 812 if (widgetLight == light) {
Chris@296 813 if (widgetLight) {
Chris@1475 814 return widgetbg.lighter();
Chris@296 815 } else {
Chris@1475 816 return widgetbg.darker();
Chris@296 817 }
Chris@296 818 }
Chris@287 819 else if (light) return Qt::white;
Chris@287 820 else return Qt::black;
Chris@287 821 }
Chris@287 822
Chris@287 823 QColor
Chris@287 824 View::getForeground() const
Chris@287 825 {
Chris@287 826 bool light = hasLightBackground();
Chris@287 827
Chris@287 828 QColor widgetfg = palette().text().color();
Chris@287 829 bool widgetLight =
Chris@287 830 (widgetfg.red() + widgetfg.green() + widgetfg.blue()) > 384;
Chris@287 831
Chris@287 832 if (widgetLight != light) return widgetfg;
Chris@287 833 else if (light) return Qt::black;
Chris@287 834 else return Qt::white;
Chris@224 835 }
Chris@224 836
Chris@127 837 void
Chris@127 838 View::addLayer(Layer *layer)
Chris@127 839 {
Chris@1357 840 m_cacheValid = false;
Chris@127 841
Chris@287 842 SingleColourLayer *scl = dynamic_cast<SingleColourLayer *>(layer);
Chris@287 843 if (scl) scl->setDefaultColourFor(this);
Chris@287 844
Chris@836 845 m_fixedOrderLayers.push_back(layer);
Chris@835 846 m_layerStack.push_back(layer);
Chris@127 847
Chris@555 848 QProgressBar *pb = new QProgressBar(this);
Chris@555 849 pb->setMinimum(0);
Chris@555 850 pb->setMaximum(0);
Chris@555 851 pb->setFixedWidth(80);
Chris@555 852 pb->setTextVisible(false);
Chris@555 853
Chris@797 854 QPushButton *cancel = new QPushButton(this);
Chris@1350 855 cancel->setIcon(IconLoader().load("cancel"));
Chris@797 856 cancel->setFlat(true);
Chris@1402 857 int scaled20 = scalePixelSize(20);
Chris@1351 858 cancel->setFixedSize(QSize(scaled20, scaled20));
Chris@797 859 connect(cancel, SIGNAL(clicked()), this, SLOT(cancelClicked()));
Chris@797 860
Chris@555 861 ProgressBarRec pbr;
Chris@797 862 pbr.cancel = cancel;
Chris@555 863 pbr.bar = pb;
Chris@1496 864 pbr.lastStallCheckValue = 0;
Chris@1496 865 pbr.stallCheckTimer = new QTimer();
Chris@1496 866 connect(pbr.stallCheckTimer, SIGNAL(timeout()), this,
Chris@555 867 SLOT(progressCheckStalledTimerElapsed()));
Chris@555 868
Chris@555 869 m_progressBars[layer] = pbr;
Chris@555 870
Chris@555 871 QFont f(pb->font());
Chris@339 872 int fs = Preferences::getInstance()->getViewFontSize();
Chris@339 873 f.setPointSize(std::min(fs, int(ceil(fs * 0.85))));
Chris@339 874
Chris@797 875 cancel->hide();
Chris@797 876
Chris@555 877 pb->setFont(f);
Chris@555 878 pb->hide();
Chris@127 879
Chris@127 880 connect(layer, SIGNAL(layerParametersChanged()),
Chris@1266 881 this, SLOT(layerParametersChanged()));
Chris@197 882 connect(layer, SIGNAL(layerParameterRangesChanged()),
Chris@1266 883 this, SLOT(layerParameterRangesChanged()));
Chris@268 884 connect(layer, SIGNAL(layerMeasurementRectsChanged()),
Chris@1266 885 this, SLOT(layerMeasurementRectsChanged()));
Chris@127 886 connect(layer, SIGNAL(layerNameChanged()),
Chris@1266 887 this, SLOT(layerNameChanged()));
Chris@1481 888 connect(layer, SIGNAL(modelChanged(ModelId)),
Chris@1481 889 this, SLOT(modelChanged(ModelId)));
Chris@1481 890 connect(layer, SIGNAL(modelCompletionChanged(ModelId)),
Chris@1481 891 this, SLOT(modelCompletionChanged(ModelId)));
Chris@1481 892 connect(layer, SIGNAL(modelAlignmentCompletionChanged(ModelId)),
Chris@1481 893 this, SLOT(modelAlignmentCompletionChanged(ModelId)));
Chris@1481 894 connect(layer, SIGNAL(modelChangedWithin(ModelId, sv_frame_t, sv_frame_t)),
Chris@1481 895 this, SLOT(modelChangedWithin(ModelId, sv_frame_t, sv_frame_t)));
Chris@127 896 connect(layer, SIGNAL(modelReplaced()),
Chris@1266 897 this, SLOT(modelReplaced()));
Chris@127 898
Chris@127 899 update();
Chris@127 900
Chris@127 901 emit propertyContainerAdded(layer);
Chris@127 902 }
Chris@127 903
Chris@127 904 void
Chris@127 905 View::removeLayer(Layer *layer)
Chris@127 906 {
Chris@127 907 if (m_deleting) {
Chris@1266 908 return;
Chris@127 909 }
Chris@127 910
Chris@1357 911 m_cacheValid = false;
Chris@127 912
Chris@836 913 for (LayerList::iterator i = m_fixedOrderLayers.begin();
Chris@836 914 i != m_fixedOrderLayers.end();
Chris@836 915 ++i) {
Chris@1266 916 if (*i == layer) {
Chris@1266 917 m_fixedOrderLayers.erase(i);
Chris@836 918 break;
Chris@836 919 }
Chris@836 920 }
Chris@836 921
Chris@836 922 for (LayerList::iterator i = m_layerStack.begin();
Chris@836 923 i != m_layerStack.end();
Chris@836 924 ++i) {
Chris@1266 925 if (*i == layer) {
Chris@1266 926 m_layerStack.erase(i);
Chris@1266 927 if (m_progressBars.find(layer) != m_progressBars.end()) {
Chris@1266 928 delete m_progressBars[layer].bar;
Chris@797 929 delete m_progressBars[layer].cancel;
Chris@1496 930 delete m_progressBars[layer].stallCheckTimer;
Chris@1266 931 m_progressBars.erase(layer);
Chris@1266 932 }
Chris@1266 933 break;
Chris@1266 934 }
Chris@127 935 }
Chris@127 936
Chris@197 937 disconnect(layer, SIGNAL(layerParametersChanged()),
Chris@197 938 this, SLOT(layerParametersChanged()));
Chris@197 939 disconnect(layer, SIGNAL(layerParameterRangesChanged()),
Chris@197 940 this, SLOT(layerParameterRangesChanged()));
Chris@197 941 disconnect(layer, SIGNAL(layerNameChanged()),
Chris@197 942 this, SLOT(layerNameChanged()));
Chris@1481 943 disconnect(layer, SIGNAL(modelChanged(ModelId)),
Chris@1481 944 this, SLOT(modelChanged(ModelId)));
Chris@1481 945 disconnect(layer, SIGNAL(modelCompletionChanged(ModelId)),
Chris@1481 946 this, SLOT(modelCompletionChanged(ModelId)));
Chris@1481 947 disconnect(layer, SIGNAL(modelAlignmentCompletionChanged(ModelId)),
Chris@1481 948 this, SLOT(modelAlignmentCompletionChanged(ModelId)));
Chris@1481 949 disconnect(layer, SIGNAL(modelChangedWithin(ModelId, sv_frame_t, sv_frame_t)),
Chris@1481 950 this, SLOT(modelChangedWithin(ModelId, sv_frame_t, sv_frame_t)));
Chris@197 951 disconnect(layer, SIGNAL(modelReplaced()),
Chris@197 952 this, SLOT(modelReplaced()));
Chris@197 953
Chris@127 954 update();
Chris@127 955
Chris@127 956 emit propertyContainerRemoved(layer);
Chris@127 957 }
Chris@127 958
Chris@127 959 Layer *
Chris@834 960 View::getInteractionLayer()
Chris@834 961 {
Chris@834 962 Layer *sl = getSelectedLayer();
Chris@834 963 if (sl && !(sl->isLayerDormant(this))) {
Chris@834 964 return sl;
Chris@834 965 }
Chris@835 966 if (!m_layerStack.empty()) {
Chris@834 967 int n = getLayerCount();
Chris@834 968 while (n > 0) {
Chris@834 969 --n;
Chris@834 970 Layer *layer = getLayer(n);
Chris@834 971 if (!(layer->isLayerDormant(this))) {
Chris@834 972 return layer;
Chris@834 973 }
Chris@834 974 }
Chris@834 975 }
Chris@1408 976 return nullptr;
Chris@834 977 }
Chris@834 978
Chris@841 979 const Layer *
Chris@841 980 View::getInteractionLayer() const
Chris@841 981 {
Chris@841 982 return const_cast<const Layer *>(const_cast<View *>(this)->getInteractionLayer());
Chris@841 983 }
Chris@841 984
Chris@834 985 Layer *
Chris@127 986 View::getSelectedLayer()
Chris@127 987 {
Chris@835 988 if (m_haveSelectedLayer && !m_layerStack.empty()) {
Chris@839 989 return getLayer(getLayerCount() - 1);
Chris@127 990 } else {
Chris@1408 991 return nullptr;
Chris@127 992 }
Chris@127 993 }
Chris@127 994
Chris@127 995 const Layer *
Chris@127 996 View::getSelectedLayer() const
Chris@127 997 {
Chris@127 998 return const_cast<const Layer *>(const_cast<View *>(this)->getSelectedLayer());
Chris@127 999 }
Chris@127 1000
Chris@127 1001 void
Chris@127 1002 View::setViewManager(ViewManager *manager)
Chris@127 1003 {
Chris@127 1004 if (m_manager) {
Chris@1266 1005 m_manager->disconnect(this, SLOT(globalCentreFrameChanged(sv_frame_t)));
Chris@1266 1006 m_manager->disconnect(this, SLOT(viewCentreFrameChanged(View *, sv_frame_t)));
Chris@1266 1007 m_manager->disconnect(this, SLOT(viewManagerPlaybackFrameChanged(sv_frame_t)));
Chris@1324 1008 m_manager->disconnect(this, SLOT(viewZoomLevelChanged(View *, ZoomLevel, bool)));
Chris@211 1009 m_manager->disconnect(this, SLOT(toolModeChanged()));
Chris@211 1010 m_manager->disconnect(this, SLOT(selectionChanged()));
Chris@211 1011 m_manager->disconnect(this, SLOT(overlayModeChanged()));
Chris@211 1012 m_manager->disconnect(this, SLOT(zoomWheelsEnabledChanged()));
Chris@908 1013 disconnect(m_manager, SLOT(viewCentreFrameChanged(sv_frame_t, bool, PlaybackFollowMode)));
Chris@1324 1014 disconnect(m_manager, SLOT(zoomLevelChanged(ZoomLevel, bool)));
Chris@127 1015 }
Chris@127 1016
Chris@127 1017 m_manager = manager;
Chris@127 1018
Chris@908 1019 connect(m_manager, SIGNAL(globalCentreFrameChanged(sv_frame_t)),
Chris@1266 1020 this, SLOT(globalCentreFrameChanged(sv_frame_t)));
Chris@908 1021 connect(m_manager, SIGNAL(viewCentreFrameChanged(View *, sv_frame_t)),
Chris@1266 1022 this, SLOT(viewCentreFrameChanged(View *, sv_frame_t)));
Chris@908 1023 connect(m_manager, SIGNAL(playbackFrameChanged(sv_frame_t)),
Chris@1266 1024 this, SLOT(viewManagerPlaybackFrameChanged(sv_frame_t)));
Chris@806 1025
Chris@1183 1026 connect(m_manager, SIGNAL(viewZoomLevelChanged(View *, ZoomLevel, bool)),
Chris@1324 1027 this, SLOT(viewZoomLevelChanged(View *, ZoomLevel, bool)));
Chris@211 1028
Chris@127 1029 connect(m_manager, SIGNAL(toolModeChanged()),
Chris@1266 1030 this, SLOT(toolModeChanged()));
Chris@127 1031 connect(m_manager, SIGNAL(selectionChanged()),
Chris@1266 1032 this, SLOT(selectionChanged()));
Chris@127 1033 connect(m_manager, SIGNAL(inProgressSelectionChanged()),
Chris@1266 1034 this, SLOT(selectionChanged()));
Chris@127 1035 connect(m_manager, SIGNAL(overlayModeChanged()),
Chris@133 1036 this, SLOT(overlayModeChanged()));
Chris@607 1037 connect(m_manager, SIGNAL(showCentreLineChanged()),
Chris@607 1038 this, SLOT(overlayModeChanged()));
Chris@133 1039 connect(m_manager, SIGNAL(zoomWheelsEnabledChanged()),
Chris@133 1040 this, SLOT(zoomWheelsEnabledChanged()));
Chris@127 1041
Chris@908 1042 connect(this, SIGNAL(centreFrameChanged(sv_frame_t, bool,
Chris@211 1043 PlaybackFollowMode)),
Chris@908 1044 m_manager, SLOT(viewCentreFrameChanged(sv_frame_t, bool,
Chris@211 1045 PlaybackFollowMode)));
Chris@211 1046
Chris@1183 1047 connect(this, SIGNAL(zoomLevelChanged(ZoomLevel, bool)),
Chris@1324 1048 m_manager, SLOT(viewZoomLevelChanged(ZoomLevel, bool)));
Chris@127 1049
Chris@815 1050 switch (m_followPlay) {
Chris@815 1051
Chris@815 1052 case PlaybackScrollPage:
Chris@815 1053 case PlaybackScrollPageWithCentre:
Chris@364 1054 setCentreFrame(m_manager->getGlobalCentreFrame(), false);
Chris@815 1055 break;
Chris@815 1056
Chris@815 1057 case PlaybackScrollContinuous:
Chris@236 1058 setCentreFrame(m_manager->getPlaybackFrame(), false);
Chris@815 1059 break;
Chris@815 1060
Chris@815 1061 case PlaybackIgnore:
Chris@815 1062 if (m_followPan) {
Chris@815 1063 setCentreFrame(m_manager->getGlobalCentreFrame(), false);
Chris@815 1064 }
Chris@815 1065 break;
Chris@236 1066 }
Chris@516 1067
Chris@236 1068 if (m_followZoom) setZoomLevel(m_manager->getGlobalZoom());
Chris@236 1069
Chris@511 1070 movePlayPointer(getAlignedPlaybackFrame());
Chris@511 1071
Chris@127 1072 toolModeChanged();
Chris@127 1073 }
Chris@127 1074
Chris@127 1075 void
Chris@908 1076 View::setViewManager(ViewManager *vm, sv_frame_t initialCentreFrame)
Chris@516 1077 {
Chris@516 1078 setViewManager(vm);
Chris@516 1079 setCentreFrame(initialCentreFrame, false);
Chris@516 1080 }
Chris@516 1081
Chris@516 1082 void
Chris@127 1083 View::setFollowGlobalPan(bool f)
Chris@127 1084 {
Chris@127 1085 m_followPan = f;
Chris@127 1086 emit propertyContainerPropertyChanged(m_propertyContainer);
Chris@127 1087 }
Chris@127 1088
Chris@127 1089 void
Chris@127 1090 View::setFollowGlobalZoom(bool f)
Chris@127 1091 {
Chris@127 1092 m_followZoom = f;
Chris@127 1093 emit propertyContainerPropertyChanged(m_propertyContainer);
Chris@127 1094 }
Chris@127 1095
Chris@127 1096 void
Chris@127 1097 View::setPlaybackFollow(PlaybackFollowMode m)
Chris@127 1098 {
Chris@127 1099 m_followPlay = m;
Chris@127 1100 emit propertyContainerPropertyChanged(m_propertyContainer);
Chris@127 1101 }
Chris@127 1102
Chris@127 1103 void
Chris@1481 1104 View::modelChanged(ModelId modelId)
Chris@127 1105 {
Chris@1495 1106 #if defined(DEBUG_VIEW_WIDGET_PAINT) || defined(DEBUG_PROGRESS_STUFF)
Chris@1506 1107 SVCERR << "View[" << getId() << "]::modelChanged(" << modelId << ")" << endl;
Chris@127 1108 #endif
Chris@1475 1109
Chris@127 1110 // If the model that has changed is not used by any of the cached
Chris@127 1111 // layers, we won't need to recreate the cache
Chris@127 1112
Chris@127 1113 bool recreate = false;
Chris@127 1114
Chris@127 1115 bool discard;
Chris@127 1116 LayerList scrollables = getScrollableBackLayers(false, discard);
Chris@127 1117 for (LayerList::const_iterator i = scrollables.begin();
Chris@1266 1118 i != scrollables.end(); ++i) {
Chris@1481 1119 if ((*i)->getModel() == modelId) {
Chris@1266 1120 recreate = true;
Chris@1266 1121 break;
Chris@1266 1122 }
Chris@127 1123 }
Chris@127 1124
Chris@127 1125 if (recreate) {
Chris@1357 1126 m_cacheValid = false;
Chris@127 1127 }
Chris@127 1128
Chris@336 1129 emit layerModelChanged();
Chris@336 1130
Chris@1481 1131 checkProgress(modelId);
Chris@127 1132
Chris@127 1133 update();
Chris@127 1134 }
Chris@127 1135
Chris@127 1136 void
Chris@1481 1137 View::modelChangedWithin(ModelId modelId,
Chris@1481 1138 sv_frame_t startFrame, sv_frame_t endFrame)
Chris@127 1139 {
Chris@908 1140 sv_frame_t myStartFrame = getStartFrame();
Chris@908 1141 sv_frame_t myEndFrame = getEndFrame();
Chris@127 1142
Chris@127 1143 #ifdef DEBUG_VIEW_WIDGET_PAINT
Chris@1506 1144 SVCERR << "View[" << getId() << "]::modelChangedWithin(" << startFrame << "," << endFrame << ") [me " << myStartFrame << "," << myEndFrame << "]" << endl;
Chris@127 1145 #endif
Chris@127 1146
Chris@908 1147 if (myStartFrame > 0 && endFrame < myStartFrame) {
Chris@1481 1148 checkProgress(modelId);
Chris@1266 1149 return;
Chris@127 1150 }
Chris@127 1151 if (startFrame > myEndFrame) {
Chris@1481 1152 checkProgress(modelId);
Chris@1266 1153 return;
Chris@127 1154 }
Chris@127 1155
Chris@127 1156 // If the model that has changed is not used by any of the cached
Chris@127 1157 // layers, we won't need to recreate the cache
Chris@127 1158
Chris@127 1159 bool recreate = false;
Chris@127 1160
Chris@127 1161 bool discard;
Chris@127 1162 LayerList scrollables = getScrollableBackLayers(false, discard);
Chris@127 1163 for (LayerList::const_iterator i = scrollables.begin();
Chris@1266 1164 i != scrollables.end(); ++i) {
Chris@1481 1165 if ((*i)->getModel() == modelId) {
Chris@1266 1166 recreate = true;
Chris@1266 1167 break;
Chris@1266 1168 }
Chris@127 1169 }
Chris@127 1170
Chris@127 1171 if (recreate) {
Chris@1357 1172 m_cacheValid = false;
Chris@127 1173 }
Chris@127 1174
Chris@806 1175 if (startFrame < myStartFrame) startFrame = myStartFrame;
Chris@127 1176 if (endFrame > myEndFrame) endFrame = myEndFrame;
Chris@127 1177
Chris@1481 1178 checkProgress(modelId);
Chris@127 1179
Chris@127 1180 update();
Chris@127 1181 }
Chris@127 1182
Chris@127 1183 void
Chris@1481 1184 View::modelCompletionChanged(ModelId modelId)
Chris@127 1185 {
Chris@1495 1186 #ifdef DEBUG_PROGRESS_STUFF
Chris@1506 1187 SVCERR << "View[" << getId() << "]::modelCompletionChanged(" << modelId << ")" << endl;
Chris@1495 1188 #endif
Chris@1481 1189 checkProgress(modelId);
Chris@127 1190 }
Chris@127 1191
Chris@127 1192 void
Chris@1481 1193 View::modelAlignmentCompletionChanged(ModelId modelId)
Chris@320 1194 {
Chris@1495 1195 #ifdef DEBUG_PROGRESS_STUFF
Chris@1506 1196 SVCERR << "View[" << getId() << "]::modelAlignmentCompletionChanged(" << modelId << ")" << endl;
Chris@1495 1197 #endif
Chris@1496 1198 checkAlignmentProgress(modelId);
Chris@320 1199 }
Chris@320 1200
Chris@320 1201 void
Chris@127 1202 View::modelReplaced()
Chris@127 1203 {
Chris@127 1204 #ifdef DEBUG_VIEW_WIDGET_PAINT
Chris@1506 1205 SVCERR << "View[" << getId() << "]::modelReplaced()" << endl;
Chris@127 1206 #endif
Chris@1357 1207 m_cacheValid = false;
Chris@127 1208 update();
Chris@127 1209 }
Chris@127 1210
Chris@127 1211 void
Chris@127 1212 View::layerParametersChanged()
Chris@127 1213 {
Chris@127 1214 Layer *layer = dynamic_cast<Layer *>(sender());
Chris@127 1215
Chris@127 1216 #ifdef DEBUG_VIEW_WIDGET_PAINT
Chris@587 1217 SVDEBUG << "View::layerParametersChanged()" << endl;
Chris@127 1218 #endif
Chris@127 1219
Chris@1357 1220 m_cacheValid = false;
Chris@127 1221 update();
Chris@127 1222
Chris@127 1223 if (layer) {
Chris@1266 1224 emit propertyContainerPropertyChanged(layer);
Chris@127 1225 }
Chris@127 1226 }
Chris@127 1227
Chris@127 1228 void
Chris@197 1229 View::layerParameterRangesChanged()
Chris@197 1230 {
Chris@197 1231 Layer *layer = dynamic_cast<Layer *>(sender());
Chris@197 1232 if (layer) emit propertyContainerPropertyRangeChanged(layer);
Chris@197 1233 }
Chris@197 1234
Chris@197 1235 void
Chris@268 1236 View::layerMeasurementRectsChanged()
Chris@268 1237 {
Chris@268 1238 Layer *layer = dynamic_cast<Layer *>(sender());
Chris@268 1239 if (layer) update();
Chris@268 1240 }
Chris@268 1241
Chris@268 1242 void
Chris@127 1243 View::layerNameChanged()
Chris@127 1244 {
Chris@127 1245 Layer *layer = dynamic_cast<Layer *>(sender());
Chris@127 1246 if (layer) emit propertyContainerNameChanged(layer);
Chris@127 1247 }
Chris@127 1248
Chris@127 1249 void
Chris@908 1250 View::globalCentreFrameChanged(sv_frame_t rf)
Chris@127 1251 {
Chris@211 1252 if (m_followPan) {
Chris@908 1253 sv_frame_t f = alignFromReference(rf);
Chris@830 1254 #ifdef DEBUG_VIEW
Chris@1506 1255 SVCERR << "View[" << getId() << "]::globalCentreFrameChanged(" << rf
Chris@682 1256 << "): setting centre frame to " << f << endl;
Chris@363 1257 #endif
Chris@333 1258 setCentreFrame(f, false);
Chris@127 1259 }
Chris@127 1260 }
Chris@127 1261
Chris@127 1262 void
Chris@908 1263 View::viewCentreFrameChanged(View *, sv_frame_t )
Chris@211 1264 {
Chris@211 1265 // We do nothing with this, but a subclass might
Chris@211 1266 }
Chris@211 1267
Chris@211 1268 void
Chris@908 1269 View::viewManagerPlaybackFrameChanged(sv_frame_t f)
Chris@127 1270 {
Chris@127 1271 if (m_manager) {
Chris@1266 1272 if (sender() != m_manager) return;
Chris@127 1273 }
Chris@127 1274
Chris@830 1275 #ifdef DEBUG_VIEW
Chris@1506 1276 SVCERR << "View[" << getId() << "]::viewManagerPlaybackFrameChanged(" << f << ")" << endl;
Chris@830 1277 #endif
Chris@830 1278
Chris@301 1279 f = getAlignedPlaybackFrame();
Chris@301 1280
Chris@830 1281 #ifdef DEBUG_VIEW
Chris@1506 1282 SVCERR << " -> aligned frame = " << f << endl;
Chris@830 1283 #endif
Chris@830 1284
Chris@511 1285 movePlayPointer(f);
Chris@511 1286 }
Chris@511 1287
Chris@511 1288 void
Chris@908 1289 View::movePlayPointer(sv_frame_t newFrame)
Chris@511 1290 {
Chris@830 1291 #ifdef DEBUG_VIEW
Chris@1506 1292 SVCERR << "View[" << getId() << "]::movePlayPointer(" << newFrame << ")" << endl;
Chris@830 1293 #endif
Chris@830 1294
Chris@511 1295 if (m_playPointerFrame == newFrame) return;
Chris@511 1296 bool visibleChange =
Chris@511 1297 (getXForFrame(m_playPointerFrame) != getXForFrame(newFrame));
Chris@1506 1298
Chris@1506 1299 #ifdef DEBUG_VIEW_WIDGET_PAINT
Chris@1506 1300 SVCERR << "View[" << getId() << "]::movePlayPointer: moving from "
Chris@1506 1301 << m_playPointerFrame << " to " << newFrame << ", visible = "
Chris@1506 1302 << visibleChange << endl;
Chris@1506 1303 #endif
Chris@1506 1304
Chris@908 1305 sv_frame_t oldPlayPointerFrame = m_playPointerFrame;
Chris@511 1306 m_playPointerFrame = newFrame;
Chris@511 1307 if (!visibleChange) return;
Chris@127 1308
Chris@513 1309 bool somethingGoingOn =
Chris@513 1310 ((QApplication::mouseButtons() != Qt::NoButton) ||
Chris@513 1311 (QApplication::keyboardModifiers() & Qt::AltModifier));
Chris@513 1312
Chris@789 1313 bool pointerInVisibleArea =
Chris@1266 1314 long(m_playPointerFrame) >= getStartFrame() &&
Chris@789 1315 (m_playPointerFrame < getEndFrame() ||
Chris@789 1316 // include old pointer location so we know to refresh when moving out
Chris@789 1317 oldPlayPointerFrame < getEndFrame());
Chris@789 1318
Chris@127 1319 switch (m_followPlay) {
Chris@127 1320
Chris@127 1321 case PlaybackScrollContinuous:
Chris@1266 1322 if (!somethingGoingOn) {
Chris@1266 1323 setCentreFrame(m_playPointerFrame, false);
Chris@1266 1324 }
Chris@1266 1325 break;
Chris@127 1326
Chris@127 1327 case PlaybackScrollPage:
Chris@815 1328 case PlaybackScrollPageWithCentre:
Chris@789 1329
Chris@789 1330 if (!pointerInVisibleArea && somethingGoingOn) {
Chris@789 1331
Chris@789 1332 m_followPlayIsDetached = true;
Chris@789 1333
Chris@789 1334 } else if (!pointerInVisibleArea && m_followPlayIsDetached) {
Chris@789 1335
Chris@789 1336 // do nothing; we aren't tracking until the pointer comes back in
Chris@789 1337
Chris@789 1338 } else {
Chris@789 1339
Chris@789 1340 int xold = getXForFrame(oldPlayPointerFrame);
Chris@789 1341 update(xold - 4, 0, 9, height());
Chris@789 1342
Chris@908 1343 sv_frame_t w = getEndFrame() - getStartFrame();
Chris@789 1344 w -= w/5;
Chris@1416 1345 sv_frame_t sf = m_playPointerFrame;
Chris@1416 1346 if (w > 0) {
Chris@1416 1347 sf = (sf / w) * w - w/8;
Chris@1416 1348 }
Chris@789 1349
Chris@789 1350 if (m_manager &&
Chris@789 1351 m_manager->isPlaying() &&
Chris@789 1352 m_manager->getPlaySelectionMode()) {
Chris@789 1353 MultiSelection::SelectionList selections = m_manager->getSelections();
Chris@789 1354 if (!selections.empty()) {
Chris@908 1355 sv_frame_t selectionStart = selections.begin()->getStartFrame();
Chris@808 1356 if (sf < selectionStart - w / 10) {
Chris@808 1357 sf = selectionStart - w / 10;
Chris@789 1358 }
Chris@789 1359 }
Chris@789 1360 }
Chris@127 1361
Chris@127 1362 #ifdef DEBUG_VIEW_WIDGET_PAINT
Chris@1506 1363 SVCERR << "PlaybackScrollPage: f = " << m_playPointerFrame << ", sf = " << sf << ", start frame "
Chris@789 1364 << getStartFrame() << endl;
Chris@127 1365 #endif
Chris@127 1366
Chris@789 1367 // We don't consider scrolling unless the pointer is outside
Chris@789 1368 // the central visible range already
Chris@789 1369
Chris@789 1370 int xnew = getXForFrame(m_playPointerFrame);
Chris@127 1371
Chris@127 1372 #ifdef DEBUG_VIEW_WIDGET_PAINT
Chris@1506 1373 SVCERR << "xnew = " << xnew << ", width = " << width() << endl;
Chris@127 1374 #endif
Chris@127 1375
Chris@789 1376 bool shouldScroll = (xnew > (width() * 7) / 8);
Chris@789 1377
Chris@789 1378 if (!m_followPlayIsDetached && (xnew < width() / 8)) {
Chris@789 1379 shouldScroll = true;
Chris@789 1380 }
Chris@789 1381
Chris@789 1382 if (xnew > width() / 8) {
Chris@789 1383 m_followPlayIsDetached = false;
Chris@791 1384 } else if (somethingGoingOn) {
Chris@791 1385 m_followPlayIsDetached = true;
Chris@789 1386 }
Chris@789 1387
Chris@789 1388 if (!somethingGoingOn && shouldScroll) {
Chris@908 1389 sv_frame_t offset = getFrameForX(width()/2) - getStartFrame();
Chris@908 1390 sv_frame_t newCentre = sf + offset;
Chris@789 1391 bool changed = setCentreFrame(newCentre, false);
Chris@789 1392 if (changed) {
Chris@789 1393 xold = getXForFrame(oldPlayPointerFrame);
Chris@789 1394 update(xold - 4, 0, 9, height());
Chris@789 1395 }
Chris@789 1396 }
Chris@789 1397
Chris@789 1398 update(xnew - 4, 0, 9, height());
Chris@789 1399 }
Chris@789 1400 break;
Chris@127 1401
Chris@127 1402 case PlaybackIgnore:
Chris@1266 1403 if (m_playPointerFrame >= getStartFrame() &&
Chris@511 1404 m_playPointerFrame < getEndFrame()) {
Chris@1266 1405 update();
Chris@1266 1406 }
Chris@1266 1407 break;
Chris@127 1408 }
Chris@127 1409 }
Chris@127 1410
Chris@127 1411 void
Chris@1183 1412 View::viewZoomLevelChanged(View *p, ZoomLevel z, bool locked)
Chris@127 1413 {
Chris@244 1414 #ifdef DEBUG_VIEW_WIDGET_PAINT
Chris@1506 1415 SVCERR << "View[" << getId() << "]: viewZoomLevelChanged(" << p << ", " << z << ", " << locked << ")" << endl;
Chris@244 1416 #endif
Chris@127 1417 if (m_followZoom && p != this && locked) {
Chris@222 1418 setZoomLevel(z);
Chris@127 1419 }
Chris@127 1420 }
Chris@127 1421
Chris@127 1422 void
Chris@127 1423 View::selectionChanged()
Chris@127 1424 {
Chris@127 1425 if (m_selectionCached) {
Chris@1357 1426 m_cacheValid = false;
Chris@1266 1427 m_selectionCached = false;
Chris@127 1428 }
Chris@127 1429 update();
Chris@127 1430 }
Chris@127 1431
Chris@908 1432 sv_frame_t
Chris@222 1433 View::getFirstVisibleFrame() const
Chris@222 1434 {
Chris@908 1435 sv_frame_t f0 = getStartFrame();
Chris@908 1436 sv_frame_t f = getModelsStartFrame();
Chris@806 1437 if (f0 < 0 || f0 < f) return f;
Chris@222 1438 return f0;
Chris@222 1439 }
Chris@222 1440
Chris@908 1441 sv_frame_t
Chris@222 1442 View::getLastVisibleFrame() const
Chris@222 1443 {
Chris@908 1444 sv_frame_t f0 = getEndFrame();
Chris@908 1445 sv_frame_t f = getModelsEndFrame();
Chris@222 1446 if (f0 > f) return f;
Chris@222 1447 return f0;
Chris@222 1448 }
Chris@222 1449
Chris@908 1450 sv_frame_t
Chris@127 1451 View::getModelsStartFrame() const
Chris@127 1452 {
Chris@127 1453 bool first = true;
Chris@908 1454 sv_frame_t startFrame = 0;
Chris@127 1455
Chris@1475 1456 for (Layer *layer: m_layerStack) {
Chris@1475 1457
Chris@1475 1458 auto model = ModelById::get(layer->getModel());
Chris@1475 1459
Chris@1475 1460 if (model && model->isOK()) {
Chris@1475 1461
Chris@1475 1462 sv_frame_t thisStartFrame = model->getStartFrame();
Chris@1266 1463
Chris@1266 1464 if (first || thisStartFrame < startFrame) {
Chris@1266 1465 startFrame = thisStartFrame;
Chris@1266 1466 }
Chris@1266 1467 first = false;
Chris@1266 1468 }
Chris@127 1469 }
Chris@1475 1470
Chris@127 1471 return startFrame;
Chris@127 1472 }
Chris@127 1473
Chris@908 1474 sv_frame_t
Chris@127 1475 View::getModelsEndFrame() const
Chris@127 1476 {
Chris@127 1477 bool first = true;
Chris@908 1478 sv_frame_t endFrame = 0;
Chris@127 1479
Chris@1475 1480 for (Layer *layer: m_layerStack) {
Chris@1475 1481
Chris@1475 1482 auto model = ModelById::get(layer->getModel());
Chris@1475 1483
Chris@1475 1484 if (model && model->isOK()) {
Chris@1475 1485
Chris@1475 1486 sv_frame_t thisEndFrame = model->getEndFrame();
Chris@1266 1487
Chris@1266 1488 if (first || thisEndFrame > endFrame) {
Chris@1266 1489 endFrame = thisEndFrame;
Chris@1266 1490 }
Chris@1266 1491 first = false;
Chris@1266 1492 }
Chris@127 1493 }
Chris@127 1494
Chris@127 1495 if (first) return getModelsStartFrame();
Chris@127 1496 return endFrame;
Chris@127 1497 }
Chris@127 1498
Chris@908 1499 sv_samplerate_t
Chris@127 1500 View::getModelsSampleRate() const
Chris@127 1501 {
Chris@127 1502 //!!! Just go for the first, for now. If we were supporting
Chris@127 1503 // multiple samplerates, we'd probably want to do frame/time
Chris@127 1504 // conversion in the model
Chris@127 1505
Chris@159 1506 //!!! nah, this wants to always return the sr of the main model!
Chris@159 1507
Chris@1475 1508 for (Layer *layer: m_layerStack) {
Chris@1475 1509
Chris@1475 1510 auto model = ModelById::get(layer->getModel());
Chris@1475 1511
Chris@1475 1512 if (model && model->isOK()) {
Chris@1475 1513 return model->getSampleRate();
Chris@1266 1514 }
Chris@127 1515 }
Chris@1475 1516
Chris@127 1517 return 0;
Chris@127 1518 }
Chris@127 1519
Chris@315 1520 View::ModelSet
Chris@315 1521 View::getModels()
Chris@315 1522 {
Chris@315 1523 ModelSet models;
Chris@315 1524
Chris@315 1525 for (int i = 0; i < getLayerCount(); ++i) {
Chris@315 1526
Chris@315 1527 Layer *layer = getLayer(i);
Chris@315 1528
Chris@315 1529 if (dynamic_cast<TimeRulerLayer *>(layer)) {
Chris@315 1530 continue;
Chris@315 1531 }
Chris@315 1532
Chris@1475 1533 if (layer && !layer->getModel().isNone()) {
Chris@1475 1534 models.insert(layer->getModel());
Chris@315 1535 }
Chris@315 1536 }
Chris@315 1537
Chris@315 1538 return models;
Chris@315 1539 }
Chris@315 1540
Chris@1475 1541 ModelId
Chris@320 1542 View::getAligningModel() const
Chris@301 1543 {
Chris@1490 1544 ModelId aligning, reference;
Chris@1490 1545 getAligningAndReferenceModels(aligning, reference);
Chris@1490 1546 return aligning;
Chris@1490 1547 }
Chris@1490 1548
Chris@1490 1549 void
Chris@1490 1550 View::getAligningAndReferenceModels(ModelId &aligning,
Chris@1490 1551 ModelId &reference) const
Chris@1490 1552 {
Chris@320 1553 if (!m_manager ||
Chris@320 1554 !m_manager->getAlignMode() ||
Chris@1479 1555 m_manager->getPlaybackModel().isNone()) {
Chris@1490 1556 return;
Chris@314 1557 }
Chris@301 1558
Chris@1475 1559 ModelId anyModel;
Chris@1475 1560
Chris@1475 1561 for (auto layer: m_layerStack) {
Chris@320 1562
Chris@320 1563 if (!layer) continue;
Chris@320 1564 if (dynamic_cast<TimeRulerLayer *>(layer)) continue;
Chris@301 1565
Chris@1481 1566 ModelId thisId = layer->getModel();
Chris@1481 1567 auto model = ModelById::get(thisId);
Chris@301 1568 if (!model) continue;
Chris@301 1569
Chris@1481 1570 anyModel = thisId;
Chris@1475 1571
Chris@1475 1572 if (!model->getAlignmentReference().isNone()) {
Chris@1490 1573
Chris@320 1574 if (layer->isLayerOpaque() ||
Chris@1475 1575 std::dynamic_pointer_cast
Chris@1475 1576 <RangeSummarisableTimeValueModel>(model)) {
Chris@1490 1577
Chris@1490 1578 aligning = thisId;
Chris@1490 1579 reference = model->getAlignmentReference();
Chris@1490 1580 return;
Chris@1490 1581
Chris@1490 1582 } else if (aligning.isNone()) {
Chris@1490 1583
Chris@1490 1584 aligning = thisId;
Chris@1490 1585 reference = model->getAlignmentReference();
Chris@320 1586 }
Chris@301 1587 }
Chris@320 1588 }
Chris@301 1589
Chris@1490 1590 if (aligning.isNone()) {
Chris@1490 1591 aligning = anyModel;
Chris@1490 1592 reference = {};
Chris@1490 1593 }
Chris@320 1594 }
Chris@320 1595
Chris@908 1596 sv_frame_t
Chris@908 1597 View::alignFromReference(sv_frame_t f) const
Chris@320 1598 {
Chris@856 1599 if (!m_manager || !m_manager->getAlignMode()) return f;
Chris@1475 1600 auto aligningModel = ModelById::get(getAligningModel());
Chris@320 1601 if (!aligningModel) return f;
Chris@320 1602 return aligningModel->alignFromReference(f);
Chris@320 1603 }
Chris@320 1604
Chris@908 1605 sv_frame_t
Chris@908 1606 View::alignToReference(sv_frame_t f) const
Chris@320 1607 {
Chris@321 1608 if (!m_manager->getAlignMode()) return f;
Chris@1475 1609 auto aligningModel = ModelById::get(getAligningModel());
Chris@320 1610 if (!aligningModel) return f;
Chris@320 1611 return aligningModel->alignToReference(f);
Chris@320 1612 }
Chris@320 1613
Chris@908 1614 sv_frame_t
Chris@320 1615 View::getAlignedPlaybackFrame() const
Chris@320 1616 {
Chris@856 1617 if (!m_manager) return 0;
Chris@908 1618 sv_frame_t pf = m_manager->getPlaybackFrame();
Chris@321 1619 if (!m_manager->getAlignMode()) return pf;
Chris@321 1620
Chris@1475 1621 auto aligningModel = ModelById::get(getAligningModel());
Chris@320 1622 if (!aligningModel) return pf;
Chris@830 1623
Chris@908 1624 sv_frame_t af = aligningModel->alignFromReference(pf);
Chris@301 1625
Chris@301 1626 return af;
Chris@301 1627 }
Chris@301 1628
Chris@127 1629 bool
Chris@127 1630 View::areLayersScrollable() const
Chris@127 1631 {
Chris@127 1632 // True iff all views are scrollable
Chris@835 1633 for (LayerList::const_iterator i = m_layerStack.begin(); i != m_layerStack.end(); ++i) {
Chris@1266 1634 if (!(*i)->isLayerScrollable(this)) return false;
Chris@127 1635 }
Chris@127 1636 return true;
Chris@127 1637 }
Chris@127 1638
Chris@127 1639 View::LayerList
Chris@127 1640 View::getScrollableBackLayers(bool testChanged, bool &changed) const
Chris@127 1641 {
Chris@127 1642 changed = false;
Chris@127 1643
Chris@127 1644 // We want a list of all the scrollable layers that are behind the
Chris@127 1645 // backmost non-scrollable layer.
Chris@127 1646
Chris@127 1647 LayerList scrollables;
Chris@127 1648 bool metUnscrollable = false;
Chris@127 1649
Chris@835 1650 for (LayerList::const_iterator i = m_layerStack.begin(); i != m_layerStack.end(); ++i) {
Chris@587 1651 // SVDEBUG << "View::getScrollableBackLayers: calling isLayerDormant on layer " << *i << endl;
Chris@1506 1652 // SVCERR << "(name is " << (*i)->objectName() << ")"
Chris@682 1653 // << endl;
Chris@1495 1654 // SVDEBUG << "View::getScrollableBackLayers: I am " << getId() << endl;
Chris@1266 1655 if ((*i)->isLayerDormant(this)) continue;
Chris@1266 1656 if ((*i)->isLayerOpaque()) {
Chris@1266 1657 // You can't see anything behind an opaque layer!
Chris@1266 1658 scrollables.clear();
Chris@127 1659 if (metUnscrollable) break;
Chris@1266 1660 }
Chris@1266 1661 if (!metUnscrollable && (*i)->isLayerScrollable(this)) {
Chris@127 1662 scrollables.push_back(*i);
Chris@127 1663 } else {
Chris@127 1664 metUnscrollable = true;
Chris@127 1665 }
Chris@127 1666 }
Chris@127 1667
Chris@127 1668 if (testChanged && scrollables != m_lastScrollableBackLayers) {
Chris@1266 1669 m_lastScrollableBackLayers = scrollables;
Chris@1266 1670 changed = true;
Chris@127 1671 }
Chris@127 1672 return scrollables;
Chris@127 1673 }
Chris@127 1674
Chris@127 1675 View::LayerList
Chris@127 1676 View::getNonScrollableFrontLayers(bool testChanged, bool &changed) const
Chris@127 1677 {
Chris@127 1678 changed = false;
Chris@127 1679 LayerList nonScrollables;
Chris@127 1680
Chris@127 1681 // Everything in front of the first non-scrollable from the back
Chris@127 1682 // should also be considered non-scrollable
Chris@127 1683
Chris@127 1684 bool started = false;
Chris@127 1685
Chris@835 1686 for (LayerList::const_iterator i = m_layerStack.begin(); i != m_layerStack.end(); ++i) {
Chris@1266 1687 if ((*i)->isLayerDormant(this)) continue;
Chris@1266 1688 if (!started && (*i)->isLayerScrollable(this)) {
Chris@1266 1689 continue;
Chris@1266 1690 }
Chris@1266 1691 started = true;
Chris@1266 1692 if ((*i)->isLayerOpaque()) {
Chris@1266 1693 // You can't see anything behind an opaque layer!
Chris@1266 1694 nonScrollables.clear();
Chris@1266 1695 }
Chris@1266 1696 nonScrollables.push_back(*i);
Chris@127 1697 }
Chris@127 1698
Chris@127 1699 if (testChanged && nonScrollables != m_lastNonScrollableBackLayers) {
Chris@1266 1700 m_lastNonScrollableBackLayers = nonScrollables;
Chris@1266 1701 changed = true;
Chris@127 1702 }
Chris@127 1703
Chris@127 1704 return nonScrollables;
Chris@127 1705 }
Chris@127 1706
Chris@1327 1707 ZoomLevel
Chris@1327 1708 View::getZoomConstraintLevel(ZoomLevel zoomLevel,
Chris@1327 1709 ZoomConstraint::RoundingDirection dir)
Chris@127 1710 const
Chris@127 1711 {
Chris@1327 1712 using namespace std::rel_ops;
Chris@1327 1713
Chris@1354 1714 ZoomLevel candidate =
Chris@1354 1715 RelativelyFineZoomConstraint().getNearestZoomLevel(zoomLevel, dir);
Chris@1354 1716
Chris@1354 1717 for (auto i : m_layerStack) {
Chris@1354 1718
Chris@1354 1719 if (i->supportsOtherZoomLevels() || !(i->getZoomConstraint())) {
Chris@1354 1720 continue;
Chris@1354 1721 }
Chris@1354 1722
Chris@1327 1723 ZoomLevel thisLevel =
Chris@1354 1724 i->getZoomConstraint()->getNearestZoomLevel(zoomLevel, dir);
Chris@1266 1725
Chris@1266 1726 // Go for the block size that's furthest from the one
Chris@1266 1727 // passed in. Most of the time, that's what we want.
Chris@1354 1728 if ((thisLevel > zoomLevel && thisLevel > candidate) ||
Chris@1327 1729 (thisLevel < zoomLevel && thisLevel < candidate)) {
Chris@1327 1730 candidate = thisLevel;
Chris@1266 1731 }
Chris@127 1732 }
Chris@127 1733
Chris@127 1734 return candidate;
Chris@127 1735 }
Chris@127 1736
Chris@1354 1737 int
Chris@1354 1738 View::countZoomLevels() const
Chris@1354 1739 {
Chris@1354 1740 int n = 0;
Chris@1354 1741 ZoomLevel min = ZoomConstraint().getMinZoomLevel();
Chris@1354 1742 ZoomLevel max = ZoomConstraint().getMaxZoomLevel();
Chris@1354 1743 ZoomLevel level = min;
Chris@1354 1744 while (true) {
Chris@1354 1745 ++n;
Chris@1354 1746 if (level == max) {
Chris@1354 1747 break;
Chris@1354 1748 }
Chris@1354 1749 level = getZoomConstraintLevel
Chris@1354 1750 (level.incremented(), ZoomConstraint::RoundUp);
Chris@1354 1751 }
Chris@1506 1752 // SVCERR << "View::countZoomLevels: " << n << endl;
Chris@1354 1753 return n;
Chris@1354 1754 }
Chris@1354 1755
Chris@1354 1756 ZoomLevel
Chris@1354 1757 View::getZoomLevelByIndex(int ix) const
Chris@1354 1758 {
Chris@1354 1759 int n = 0;
Chris@1354 1760 ZoomLevel min = ZoomConstraint().getMinZoomLevel();
Chris@1354 1761 ZoomLevel max = ZoomConstraint().getMaxZoomLevel();
Chris@1354 1762 ZoomLevel level = min;
Chris@1354 1763 while (true) {
Chris@1354 1764 if (n == ix) {
Chris@1506 1765 // SVCERR << "View::getZoomLevelByIndex: " << ix << " -> " << level
Chris@1355 1766 // << endl;
Chris@1354 1767 return level;
Chris@1354 1768 }
Chris@1354 1769 ++n;
Chris@1354 1770 if (level == max) {
Chris@1354 1771 break;
Chris@1354 1772 }
Chris@1354 1773 level = getZoomConstraintLevel
Chris@1354 1774 (level.incremented(), ZoomConstraint::RoundUp);
Chris@1354 1775 }
Chris@1506 1776 // SVCERR << "View::getZoomLevelByIndex: " << ix << " -> " << max << " (max)"
Chris@1355 1777 // << endl;
Chris@1354 1778 return max;
Chris@1354 1779 }
Chris@1354 1780
Chris@1354 1781 int
Chris@1354 1782 View::getZoomLevelIndex(ZoomLevel z) const
Chris@1354 1783 {
Chris@1354 1784 int n = 0;
Chris@1354 1785 ZoomLevel min = ZoomConstraint().getMinZoomLevel();
Chris@1354 1786 ZoomLevel max = ZoomConstraint().getMaxZoomLevel();
Chris@1354 1787 ZoomLevel level = min;
Chris@1354 1788 while (true) {
Chris@1354 1789 if (z == level) {
Chris@1506 1790 // SVCERR << "View::getZoomLevelIndex: " << z << " -> " << n
Chris@1355 1791 // << endl;
Chris@1354 1792 return n;
Chris@1354 1793 }
Chris@1354 1794 ++n;
Chris@1354 1795 if (level == max) {
Chris@1354 1796 break;
Chris@1354 1797 }
Chris@1354 1798 level = getZoomConstraintLevel
Chris@1354 1799 (level.incremented(), ZoomConstraint::RoundUp);
Chris@1354 1800 }
Chris@1506 1801 // SVCERR << "View::getZoomLevelIndex: " << z << " -> " << n << " (max)"
Chris@1355 1802 // << endl;
Chris@1354 1803 return n;
Chris@1354 1804 }
Chris@1354 1805
Chris@1401 1806 double
Chris@1401 1807 View::scaleSize(double size) const
Chris@1401 1808 {
Chris@1401 1809 static double ratio = 0.0;
Chris@1401 1810
Chris@1401 1811 if (ratio == 0.0) {
Chris@1401 1812 double baseEm;
Chris@1401 1813 #ifdef Q_OS_MAC
Chris@1401 1814 baseEm = 17.0;
Chris@1401 1815 #else
Chris@1401 1816 baseEm = 15.0;
Chris@1401 1817 #endif
Chris@1401 1818 double em = QFontMetrics(QFont()).height();
Chris@1401 1819 ratio = em / baseEm;
Chris@1401 1820
Chris@1401 1821 SVDEBUG << "View::scaleSize: ratio is " << ratio
Chris@1401 1822 << " (em = " << em << ")" << endl;
Chris@1401 1823
Chris@1401 1824 if (ratio < 1.0) {
Chris@1401 1825 SVDEBUG << "View::scaleSize: rounding ratio up to 1.0" << endl;
Chris@1401 1826 ratio = 1.0;
Chris@1401 1827 }
Chris@1401 1828 }
Chris@1401 1829
Chris@1401 1830 return size * ratio;
Chris@1401 1831 }
Chris@1401 1832
Chris@1402 1833 int
Chris@1402 1834 View::scalePixelSize(int size) const
Chris@1402 1835 {
Chris@1402 1836 double d = scaleSize(size);
Chris@1402 1837 int i = int(d + 0.5);
Chris@1402 1838 if (size != 0 && i == 0) i = 1;
Chris@1402 1839 return i;
Chris@1402 1840 }
Chris@1402 1841
Chris@1401 1842 double
Chris@1401 1843 View::scalePenWidth(double width) const
Chris@1401 1844 {
Chris@1401 1845 if (width <= 0) { // zero-width pen, produce a scaled one-pixel pen
Chris@1401 1846 width = 1;
Chris@1401 1847 }
Chris@1401 1848 double ratio = scaleSize(1.0);
Chris@1401 1849 return width * sqrt(ratio);
Chris@1401 1850 }
Chris@1401 1851
Chris@1401 1852 QPen
Chris@1401 1853 View::scalePen(QPen pen) const
Chris@1401 1854 {
Chris@1401 1855 return QPen(pen.color(), scalePenWidth(pen.width()));
Chris@1401 1856 }
Chris@1401 1857
Chris@183 1858 bool
Chris@183 1859 View::areLayerColoursSignificant() const
Chris@183 1860 {
Chris@835 1861 for (LayerList::const_iterator i = m_layerStack.begin(); i != m_layerStack.end(); ++i) {
Chris@1266 1862 if ((*i)->getLayerColourSignificance() ==
Chris@287 1863 Layer::ColourHasMeaningfulValue) return true;
Chris@183 1864 if ((*i)->isLayerOpaque()) break;
Chris@183 1865 }
Chris@183 1866 return false;
Chris@183 1867 }
Chris@183 1868
Chris@217 1869 bool
Chris@217 1870 View::hasTopLayerTimeXAxis() const
Chris@217 1871 {
Chris@835 1872 LayerList::const_iterator i = m_layerStack.end();
Chris@835 1873 if (i == m_layerStack.begin()) return false;
Chris@217 1874 --i;
Chris@217 1875 return (*i)->hasTimeXAxis();
Chris@217 1876 }
Chris@217 1877
Chris@127 1878 void
Chris@127 1879 View::zoom(bool in)
Chris@127 1880 {
Chris@1183 1881 ZoomLevel newZoomLevel = m_zoomLevel;
Chris@127 1882
Chris@127 1883 if (in) {
Chris@1324 1884 newZoomLevel = getZoomConstraintLevel(m_zoomLevel.decremented(),
Chris@1183 1885 ZoomConstraint::RoundDown);
Chris@127 1886 } else {
Chris@1324 1887 newZoomLevel = getZoomConstraintLevel(m_zoomLevel.incremented(),
Chris@1183 1888 ZoomConstraint::RoundUp);
Chris@127 1889 }
Chris@127 1890
Chris@1327 1891 using namespace std::rel_ops;
Chris@1327 1892
Chris@127 1893 if (newZoomLevel != m_zoomLevel) {
Chris@1266 1894 setZoomLevel(newZoomLevel);
Chris@127 1895 }
Chris@127 1896 }
Chris@127 1897
Chris@127 1898 void
Chris@510 1899 View::scroll(bool right, bool lots, bool e)
Chris@127 1900 {
Chris@908 1901 sv_frame_t delta;
Chris@127 1902 if (lots) {
Chris@1266 1903 delta = (getEndFrame() - getStartFrame()) / 2;
Chris@127 1904 } else {
Chris@1266 1905 delta = (getEndFrame() - getStartFrame()) / 20;
Chris@127 1906 }
Chris@127 1907 if (right) delta = -delta;
Chris@127 1908
Chris@1363 1909 #ifdef DEBUG_VIEW
Chris@1363 1910 SVCERR << "View::scroll(" << right << ", " << lots << ", " << e << "): "
Chris@1363 1911 << "delta = " << delta << ", m_centreFrame = " << m_centreFrame
Chris@1363 1912 << endl;
Chris@1363 1913 #endif
Chris@1363 1914
Chris@908 1915 if (m_centreFrame < delta) {
Chris@1266 1916 setCentreFrame(0, e);
Chris@908 1917 } else if (m_centreFrame - delta >= getModelsEndFrame()) {
Chris@1266 1918 setCentreFrame(getModelsEndFrame(), e);
Chris@127 1919 } else {
Chris@1266 1920 setCentreFrame(m_centreFrame - delta, e);
Chris@127 1921 }
Chris@127 1922 }
Chris@127 1923
Chris@127 1924 void
Chris@797 1925 View::cancelClicked()
Chris@797 1926 {
Chris@797 1927 QPushButton *cancel = qobject_cast<QPushButton *>(sender());
Chris@797 1928 if (!cancel) return;
Chris@797 1929
Chris@1483 1930 Layer *layer = nullptr;
Chris@1483 1931
Chris@797 1932 for (ProgressMap::iterator i = m_progressBars.begin();
Chris@1266 1933 i != m_progressBars.end(); ++i) {
Chris@797 1934 if (i->second.cancel == cancel) {
Chris@1483 1935 layer = i->first;
Chris@1483 1936 break;
Chris@797 1937 }
Chris@797 1938 }
Chris@1483 1939
Chris@1483 1940 if (layer) {
Chris@1483 1941 emit cancelButtonPressed(layer);
Chris@1483 1942 }
Chris@797 1943 }
Chris@797 1944
Chris@797 1945 void
Chris@1481 1946 View::checkProgress(ModelId modelId)
Chris@127 1947 {
Chris@1448 1948 if (!m_showProgress) {
Chris@1448 1949 #ifdef DEBUG_PROGRESS_STUFF
Chris@1495 1950 SVCERR << "View[" << getId() << "]::checkProgress(" << modelId << "): "
Chris@1448 1951 << "m_showProgress is off" << endl;
Chris@1448 1952 #endif
Chris@1448 1953 return;
Chris@1448 1954 }
Chris@127 1955
Chris@1496 1956 #ifdef DEBUG_PROGRESS_STUFF
Chris@1496 1957 SVCERR << "View[" << getId() << "]::checkProgress(" << modelId << ")" << endl;
Chris@1496 1958 #endif
Chris@1496 1959
Chris@1458 1960 QSettings settings;
Chris@1458 1961 settings.beginGroup("View");
Chris@1458 1962 bool showCancelButton = settings.value("showcancelbuttons", true).toBool();
Chris@1458 1963 settings.endGroup();
Chris@1458 1964
Chris@127 1965 int ph = height();
Chris@1448 1966 bool found = false;
Chris@127 1967
Chris@1496 1968 if (m_alignmentProgressBar.bar) {
Chris@1496 1969 ph -= m_alignmentProgressBar.bar->height();
Chris@1496 1970 }
Chris@1496 1971
Chris@555 1972 for (ProgressMap::iterator i = m_progressBars.begin();
Chris@1266 1973 i != m_progressBars.end(); ++i) {
Chris@127 1974
Chris@555 1975 QProgressBar *pb = i->second.bar;
Chris@797 1976 QPushButton *cancel = i->second.cancel;
Chris@555 1977
Chris@1481 1978 if (i->first && i->first->getModel() == modelId) {
Chris@127 1979
Chris@1448 1980 found = true;
Chris@1448 1981
Chris@555 1982 // The timer is used to test for stalls. If the progress
Chris@555 1983 // bar does not get updated for some length of time, the
Chris@555 1984 // timer prompts it to go back into "indeterminate" mode
Chris@1496 1985 QTimer *timer = i->second.stallCheckTimer;
Chris@555 1986
Chris@1266 1987 int completion = i->first->getCompletion(this);
Chris@583 1988 QString error = i->first->getError(this);
Chris@583 1989
Chris@1448 1990 #ifdef DEBUG_PROGRESS_STUFF
Chris@1495 1991 SVCERR << "View[" << getId() << "]::checkProgress(" << modelId << "): "
Chris@1448 1992 << "found progress bar " << pb << " for layer at height " << ph
Chris@1448 1993 << ": completion = " << completion << endl;
Chris@1448 1994 #endif
Chris@1448 1995
Chris@583 1996 if (error != "" && error != m_lastError) {
Chris@583 1997 QMessageBox::critical(this, tr("Layer rendering error"), error);
Chris@583 1998 m_lastError = error;
Chris@583 1999 }
Chris@301 2000
Chris@388 2001 if (completion > 0) {
Chris@555 2002 pb->setMaximum(100); // was 0, for indeterminate start
Chris@388 2003 }
Chris@388 2004
Chris@1496 2005 if (completion < 100 &&
Chris@1496 2006 ModelById::isa<RangeSummarisableTimeValueModel>(modelId)) {
Chris@387 2007 update(); // ensure duration &c gets updated
Chris@301 2008 }
Chris@127 2009
Chris@1266 2010 if (completion >= 100) {
Chris@1266 2011
Chris@1266 2012 pb->hide();
Chris@797 2013 cancel->hide();
Chris@555 2014 timer->stop();
Chris@127 2015
Chris@1496 2016 } else if (i->first->isLayerDormant(this)) {
Chris@1495 2017
Chris@1495 2018 // A dormant (invisible) layer can still be busy
Chris@1495 2019 // generating, but we don't usually want to indicate
Chris@1495 2020 // it because it probably means it's a duplicate of a
Chris@1495 2021 // visible layer
Chris@1495 2022 #ifdef DEBUG_PROGRESS_STUFF
Chris@1495 2023 SVCERR << "View[" << getId() << "]::checkProgress("
Chris@1495 2024 << modelId << "): layer is dormant" << endl;
Chris@1495 2025 #endif
Chris@1495 2026 pb->hide();
Chris@1495 2027 cancel->hide();
Chris@1495 2028 timer->stop();
Chris@1495 2029
Chris@1266 2030 } else {
Chris@127 2031
Chris@555 2032 if (!pb->isVisible()) {
Chris@1496 2033 i->second.lastStallCheckValue = 0;
Chris@555 2034 timer->setInterval(2000);
Chris@555 2035 timer->start();
Chris@555 2036 }
Chris@555 2037
Chris@1458 2038 if (showCancelButton) {
Chris@1458 2039
Chris@1458 2040 int scaled20 = scalePixelSize(20);
Chris@1458 2041
Chris@1458 2042 cancel->move(0, ph - pb->height()/2 - scaled20/2);
Chris@1458 2043 cancel->show();
Chris@1458 2044
Chris@1458 2045 pb->setValue(completion);
Chris@1458 2046 pb->move(scaled20, ph - pb->height());
Chris@1458 2047
Chris@1458 2048 } else {
Chris@1458 2049
Chris@1458 2050 cancel->hide();
Chris@1458 2051
Chris@1458 2052 pb->setValue(completion);
Chris@1458 2053 pb->move(0, ph - pb->height());
Chris@1458 2054 }
Chris@1266 2055
Chris@1266 2056 pb->show();
Chris@1266 2057 pb->update();
Chris@1266 2058
Chris@1495 2059 if (pb->isVisible()) {
Chris@1495 2060 ph -= pb->height();
Chris@1495 2061 }
Chris@1266 2062 }
Chris@1266 2063 } else {
Chris@1266 2064 if (pb->isVisible()) {
Chris@1266 2065 ph -= pb->height();
Chris@1266 2066 }
Chris@1266 2067 }
Chris@127 2068 }
Chris@1448 2069
Chris@1448 2070 if (!found) {
Chris@1448 2071 #ifdef DEBUG_PROGRESS_STUFF
Chris@1495 2072 SVCERR << "View[" << getId() << "]::checkProgress(" << modelId << "): "
Chris@1487 2073 << "failed to find layer for model in progress map"
Chris@1448 2074 << endl;
Chris@1448 2075 #endif
Chris@1448 2076 }
Chris@127 2077 }
Chris@127 2078
Chris@555 2079 void
Chris@1496 2080 View::checkAlignmentProgress(ModelId modelId)
Chris@1496 2081 {
Chris@1496 2082 if (!m_showProgress) {
Chris@1496 2083 #ifdef DEBUG_PROGRESS_STUFF
Chris@1496 2084 SVCERR << "View[" << getId() << "]::checkAlignmentProgress(" << modelId << "): "
Chris@1496 2085 << "m_showProgress is off" << endl;
Chris@1496 2086 #endif
Chris@1496 2087 return;
Chris@1496 2088 }
Chris@1496 2089
Chris@1496 2090 #ifdef DEBUG_PROGRESS_STUFF
Chris@1496 2091 SVCERR << "View[" << getId() << "]::checkAlignmentProgress(" << modelId << ")" << endl;
Chris@1496 2092 #endif
Chris@1496 2093
Chris@1496 2094 if (!m_alignmentProgressBar.alignedModel.isNone() &&
Chris@1496 2095 modelId != m_alignmentProgressBar.alignedModel) {
Chris@1496 2096 #ifdef DEBUG_PROGRESS_STUFF
Chris@1496 2097 SVCERR << "View[" << getId() << "]::checkAlignmentProgress(" << modelId << "): Different model (we're currently showing alignment progress for " << modelId << ", ignoring" << endl;
Chris@1496 2098 #endif
Chris@1496 2099 return;
Chris@1496 2100 }
Chris@1496 2101
Chris@1496 2102 auto model = ModelById::get(modelId);
Chris@1496 2103 if (!model) {
Chris@1496 2104 #ifdef DEBUG_PROGRESS_STUFF
Chris@1496 2105 SVCERR << "View[" << getId() << "]::checkAlignmentProgress(" << modelId << "): Model gone" << endl;
Chris@1496 2106 #endif
Chris@1496 2107 m_alignmentProgressBar.alignedModel = {};
Chris@1496 2108 delete m_alignmentProgressBar.bar;
Chris@1496 2109 m_alignmentProgressBar.bar = nullptr;
Chris@1496 2110 return;
Chris@1496 2111 }
Chris@1496 2112
Chris@1496 2113 int completion = model->getAlignmentCompletion();
Chris@1496 2114
Chris@1496 2115 #ifdef DEBUG_PROGRESS_STUFF
Chris@1496 2116 SVCERR << "View[" << getId() << "]::checkAlignmentProgress(" << modelId << "): Completion is " << completion << endl;
Chris@1496 2117 #endif
Chris@1496 2118
Chris@1496 2119 int ph = height();
Chris@1496 2120
Chris@1496 2121 if (completion >= 100) {
Chris@1496 2122 m_alignmentProgressBar.alignedModel = {};
Chris@1496 2123 delete m_alignmentProgressBar.bar;
Chris@1496 2124 m_alignmentProgressBar.bar = nullptr;
Chris@1496 2125 return;
Chris@1496 2126 }
Chris@1496 2127
Chris@1496 2128 QProgressBar *pb = m_alignmentProgressBar.bar;
Chris@1496 2129 if (!pb) {
Chris@1496 2130 pb = new QProgressBar(this);
Chris@1496 2131 pb->setMinimum(0);
Chris@1496 2132 pb->setMaximum(100);
Chris@1496 2133 pb->setFixedWidth(80);
Chris@1496 2134 pb->setTextVisible(false);
Chris@1496 2135 m_alignmentProgressBar.alignedModel = modelId;
Chris@1496 2136 m_alignmentProgressBar.bar = pb;
Chris@1496 2137 }
Chris@1496 2138
Chris@1496 2139 pb->setValue(completion);
Chris@1496 2140 pb->move(0, ph - pb->height());
Chris@1496 2141 pb->show();
Chris@1496 2142 pb->update();
Chris@1496 2143 }
Chris@1496 2144
Chris@1496 2145 void
Chris@555 2146 View::progressCheckStalledTimerElapsed()
Chris@555 2147 {
Chris@555 2148 QObject *s = sender();
Chris@555 2149 QTimer *t = qobject_cast<QTimer *>(s);
Chris@555 2150 if (!t) return;
Chris@1448 2151
Chris@555 2152 for (ProgressMap::iterator i = m_progressBars.begin();
Chris@555 2153 i != m_progressBars.end(); ++i) {
Chris@1448 2154
Chris@1496 2155 if (i->second.stallCheckTimer == t) {
Chris@1448 2156
Chris@1495 2157 int value = i->second.bar->value();
Chris@1495 2158
Chris@1448 2159 #ifdef DEBUG_PROGRESS_STUFF
Chris@1495 2160 SVCERR << "View[" << getId() << "]::progressCheckStalledTimerElapsed for layer " << i->first << ": value is " << value << endl;
Chris@1448 2161 #endif
Chris@1448 2162
Chris@1496 2163 if (value > 0 && value == i->second.lastStallCheckValue) {
Chris@555 2164 i->second.bar->setMaximum(0); // indeterminate
Chris@555 2165 }
Chris@1496 2166 i->second.lastStallCheckValue = value;
Chris@555 2167 return;
Chris@555 2168 }
Chris@555 2169 }
Chris@555 2170 }
Chris@555 2171
Chris@384 2172 int
Chris@384 2173 View::getProgressBarWidth() const
Chris@384 2174 {
Chris@1496 2175 if (m_alignmentProgressBar.bar) {
Chris@1496 2176 return m_alignmentProgressBar.bar->width();
Chris@1496 2177 }
Chris@1496 2178
Chris@384 2179 for (ProgressMap::const_iterator i = m_progressBars.begin();
Chris@1266 2180 i != m_progressBars.end(); ++i) {
Chris@555 2181 if (i->second.bar && i->second.bar->isVisible()) {
Chris@555 2182 return i->second.bar->width();
Chris@555 2183 }
Chris@384 2184 }
Chris@384 2185
Chris@384 2186 return 0;
Chris@384 2187 }
Chris@384 2188
Chris@127 2189 void
Chris@339 2190 View::setPaintFont(QPainter &paint)
Chris@339 2191 {
Chris@955 2192 int scaleFactor = 1;
Chris@956 2193 int dpratio = effectiveDevicePixelRatio();
Chris@955 2194 if (dpratio > 1) {
Chris@955 2195 QPaintDevice *dev = paint.device();
Chris@955 2196 if (dynamic_cast<QPixmap *>(dev) || dynamic_cast<QImage *>(dev)) {
Chris@955 2197 scaleFactor = dpratio;
Chris@955 2198 }
Chris@955 2199 }
Chris@955 2200
Chris@339 2201 QFont font(paint.font());
Chris@955 2202 font.setPointSize(Preferences::getInstance()->getViewFontSize()
Chris@955 2203 * scaleFactor);
Chris@339 2204 paint.setFont(font);
Chris@339 2205 }
Chris@339 2206
Chris@915 2207 QRect
Chris@915 2208 View::getPaintRect() const
Chris@915 2209 {
Chris@919 2210 return rect();
Chris@915 2211 }
Chris@915 2212
Chris@339 2213 void
Chris@127 2214 View::paintEvent(QPaintEvent *e)
Chris@127 2215 {
Chris@127 2216 // Profiler prof("View::paintEvent", false);
Chris@1506 2217
Chris@1506 2218 #ifdef DEBUG_VIEW_WIDGET_PAINT
Chris@1506 2219 {
Chris@1506 2220 sv_frame_t startFrame = getStartFrame();
Chris@1506 2221 SVCERR << "View[" << getId() << "]::paintEvent: centre frame is " << m_centreFrame << " (start frame " << startFrame << ", height " << height() << ")" << endl;
Chris@1506 2222 }
Chris@1506 2223 #endif
matthiasm@785 2224
Chris@835 2225 if (m_layerStack.empty()) {
Chris@1266 2226 QFrame::paintEvent(e);
Chris@1266 2227 return;
Chris@127 2228 }
Chris@127 2229
Chris@127 2230 // ensure our constraints are met
Chris@1354 2231 m_zoomLevel = getZoomConstraintLevel
Chris@1354 2232 (m_zoomLevel, ZoomConstraint::RoundNearest);
Chris@127 2233
Chris@1357 2234 // We have a cache, which retains the state of scrollable (back)
Chris@1357 2235 // layers from one paint to the next, and a buffer, which we paint
Chris@1357 2236 // onto before copying directly to the widget. Both are at scaled
Chris@1357 2237 // resolution (e.g. 2x on a pixel-doubled display), whereas the
Chris@1357 2238 // paint event always comes in at formal (1x) resolution.
Chris@1357 2239
Chris@1357 2240 // If we touch the cache, we always leave it in a valid state
Chris@1357 2241 // across its whole extent. When another method invalidates the
Chris@1357 2242 // cache, it does so by setting m_cacheValid false, so if that
Chris@1357 2243 // flag is true on entry, then the cache is valid across its whole
Chris@1357 2244 // extent - although it may be valid for a different centre frame,
Chris@1357 2245 // zoom level, or view size from those now in effect.
Chris@1357 2246
Chris@1357 2247 // Our process goes:
Chris@1357 2248 //
Chris@1357 2249 // 1. Check whether we have any scrollable (cacheable) layers. If
Chris@1357 2250 // we don't, then invalidate and ignore the cache and go to
Chris@1357 2251 // step 5. Otherwise:
Chris@1357 2252 //
Chris@1357 2253 // 2. Check the cache, scroll as necessary, identify any area that
Chris@1357 2254 // needs to be refreshed (this might be the whole cache).
Chris@1357 2255 //
Chris@1357 2256 // 3. Paint to cache the area that needs to be refreshed, from the
Chris@1357 2257 // stack of scrollable layers.
Chris@1357 2258 //
Chris@1357 2259 // 4. Paint to buffer from cache: if there are no non-cached areas
Chris@1357 2260 // or selections and the cache has not scrolled, then paint the
Chris@1357 2261 // union of the area of cache that has changed and the area
Chris@1357 2262 // that the paint event reported as exposed; otherwise paint
Chris@1357 2263 // the whole.
Chris@1357 2264 //
Chris@1357 2265 // 5. Paint the exposed area to the buffer from the cache plus all
Chris@1357 2266 // the layers that haven't been cached, plus selections etc.
Chris@1357 2267 //
Chris@1357 2268 // 6. Paint the exposed rect from the buffer.
Chris@1357 2269 //
Chris@1357 2270 // Note that all rects except the target for the final step are at
Chris@1357 2271 // cache (scaled, 2x as applicable) resolution.
Chris@1357 2272
Chris@1357 2273 int dpratio = effectiveDevicePixelRatio();
Chris@1357 2274
Chris@1357 2275 QRect requestedPaintArea(scaledRect(rect(), dpratio));
Chris@127 2276 if (e) {
Chris@1357 2277 // cut down to only the area actually exposed
Chris@1357 2278 requestedPaintArea &= scaledRect(e->rect(), dpratio);
Chris@127 2279 }
Chris@127 2280
Chris@127 2281 // If not all layers are scrollable, but some of the back layers
Chris@127 2282 // are, we should store only those in the cache.
Chris@127 2283
Chris@127 2284 bool layersChanged = false;
Chris@127 2285 LayerList scrollables = getScrollableBackLayers(true, layersChanged);
Chris@127 2286 LayerList nonScrollables = getNonScrollableFrontLayers(true, layersChanged);
Chris@127 2287
Chris@127 2288 #ifdef DEBUG_VIEW_WIDGET_PAINT
Chris@1506 2289 SVCERR << "View[" << getId() << "]::paintEvent: have " << scrollables.size()
Chris@1266 2290 << " scrollable back layers and " << nonScrollables.size()
Chris@1266 2291 << " non-scrollable front layers" << endl;
Chris@127 2292 #endif
Chris@127 2293
Chris@1357 2294 if (layersChanged || scrollables.empty()) {
Chris@1357 2295 m_cacheValid = false;
Chris@127 2296 }
Chris@127 2297
Chris@1357 2298 QRect wholeArea(scaledRect(rect(), dpratio));
Chris@1357 2299 QSize wholeSize(scaledSize(size(), dpratio));
Chris@1357 2300
Chris@1357 2301 if (!m_buffer || wholeSize != m_buffer->size()) {
Chris@952 2302 delete m_buffer;
Chris@1357 2303 m_buffer = new QPixmap(wholeSize);
Chris@952 2304 }
Chris@1357 2305
Chris@1357 2306 bool shouldUseCache = false;
Chris@1357 2307 bool shouldRepaintCache = false;
Chris@1357 2308 QRect cacheAreaToRepaint;
Chris@952 2309
Chris@1357 2310 static HitCount count("View cache");
Chris@1357 2311
Chris@127 2312 if (!scrollables.empty()) {
Chris@244 2313
Chris@1357 2314 shouldUseCache = true;
Chris@1357 2315 shouldRepaintCache = true;
Chris@1357 2316 cacheAreaToRepaint = wholeArea;
Chris@1357 2317
Chris@244 2318 #ifdef DEBUG_VIEW_WIDGET_PAINT
Chris@1506 2319 SVCERR << "View[" << getId() << "]: cache " << m_cache << ", cache zoom "
Chris@682 2320 << m_cacheZoomLevel << ", zoom " << m_zoomLevel << endl;
Chris@244 2321 #endif
Chris@244 2322
Chris@1327 2323 using namespace std::rel_ops;
Chris@1327 2324
Chris@1357 2325 if (!m_cacheValid ||
Chris@1357 2326 !m_cache ||
Chris@1266 2327 m_cacheZoomLevel != m_zoomLevel ||
Chris@1357 2328 m_cache->size() != wholeSize) {
Chris@1357 2329
Chris@1357 2330 // cache is not valid at all
Chris@1357 2331
Chris@1357 2332 if (requestedPaintArea.width() < wholeSize.width() / 10) {
Chris@1357 2333
Chris@1357 2334 m_cacheValid = false;
Chris@1357 2335 shouldUseCache = false;
Chris@1357 2336 shouldRepaintCache = false;
Chris@1357 2337
Chris@127 2338 #ifdef DEBUG_VIEW_WIDGET_PAINT
Chris@1506 2339 SVCERR << "View[" << getId() << "]::paintEvent: cache is invalid but only small area requested, will repaint directly instead" << endl;
Chris@127 2340 #endif
Chris@1266 2341 } else {
Chris@1357 2342
Chris@1357 2343 if (!m_cache ||
Chris@1357 2344 m_cache->size() != wholeSize) {
Chris@1357 2345 delete m_cache;
Chris@1357 2346 m_cache = new QPixmap(wholeSize);
Chris@1357 2347 }
Chris@1357 2348
Chris@127 2349 #ifdef DEBUG_VIEW_WIDGET_PAINT
Chris@1506 2350 SVCERR << "View[" << getId() << "]::paintEvent: cache is invalid, will repaint whole" << endl;
Chris@127 2351 #endif
Chris@1266 2352 }
Chris@1266 2353
Chris@1357 2354 count.miss();
Chris@1357 2355
Chris@1266 2356 } else if (m_cacheCentreFrame != m_centreFrame) {
Chris@1266 2357
Chris@1506 2358 #ifdef DEBUG_VIEW_WIDGET_PAINT
Chris@1506 2359 SVCERR << "View[" << getId() << "]::paintEvent: cache centre frame is " << m_cacheCentreFrame << endl;
Chris@1506 2360 #endif
Chris@1506 2361
Chris@1357 2362 int dx = dpratio * (getXForFrame(m_cacheCentreFrame) -
Chris@1357 2363 getXForFrame(m_centreFrame));
Chris@1357 2364
Chris@1357 2365 if (dx > -m_cache->width() && dx < m_cache->width()) {
Chris@1357 2366
Chris@1408 2367 m_cache->scroll(dx, 0, m_cache->rect(), nullptr);
Chris@1357 2368
Chris@1357 2369 if (dx < 0) {
Chris@1357 2370 cacheAreaToRepaint =
Chris@1357 2371 QRect(m_cache->width() + dx, 0, -dx, m_cache->height());
Chris@1357 2372 } else {
Chris@1357 2373 cacheAreaToRepaint =
Chris@1357 2374 QRect(0, 0, dx, m_cache->height());
Chris@1266 2375 }
Chris@1357 2376
Chris@1357 2377 count.partial();
Chris@1357 2378
Chris@127 2379 #ifdef DEBUG_VIEW_WIDGET_PAINT
Chris@1506 2380 SVCERR << "View[" << getId() << "]::paintEvent: scrolled cache by " << dx << endl;
Chris@127 2381 #endif
Chris@1266 2382 } else {
Chris@1357 2383 count.miss();
Chris@127 2384 #ifdef DEBUG_VIEW_WIDGET_PAINT
Chris@1506 2385 SVCERR << "View[" << getId() << "]::paintEvent: scrolling too far" << endl;
Chris@127 2386 #endif
Chris@1266 2387 }
Chris@1266 2388
Chris@1266 2389 } else {
Chris@127 2390 #ifdef DEBUG_VIEW_WIDGET_PAINT
Chris@1506 2391 SVCERR << "View[" << getId() << "]::paintEvent: cache is good" << endl;
Chris@127 2392 #endif
Chris@1357 2393 count.hit();
Chris@1357 2394 shouldRepaintCache = false;
Chris@1266 2395 }
Chris@1357 2396 }
Chris@1357 2397
Chris@1357 2398 #ifdef DEBUG_VIEW_WIDGET_PAINT
Chris@1506 2399 SVCERR << "View[" << getId() << "]::paintEvent: m_cacheValid = " << m_cacheValid << ", shouldUseCache = " << shouldUseCache << ", shouldRepaintCache = " << shouldRepaintCache << ", cacheAreaToRepaint = " << cacheAreaToRepaint.x() << "," << cacheAreaToRepaint.y() << " " << cacheAreaToRepaint.width() << "x" << cacheAreaToRepaint.height() << endl;
Chris@1357 2400 #endif
Chris@1357 2401
Chris@1357 2402 if (shouldRepaintCache && !shouldUseCache) {
Chris@1357 2403 // If we are repainting the cache, then we paint the
Chris@1357 2404 // scrollables only to the cache, not to the buffer. So if
Chris@1357 2405 // shouldUseCache is also false, then the scrollables can't
Chris@1357 2406 // appear because they will only be on the cache
Chris@1357 2407 throw std::logic_error("ERROR: shouldRepaintCache is true, but shouldUseCache is false: this can't lead to the correct result");
Chris@1357 2408 }
Chris@1357 2409
Chris@1490 2410 // Create the ViewProxy for geometry provision, using the
Chris@1490 2411 // device-pixel ratio for pixel-doubled hi-dpi rendering as
Chris@1490 2412 // appropriate.
Chris@1490 2413
Chris@1490 2414 ViewProxy proxy(this, dpratio);
Chris@1490 2415
Chris@1490 2416 // Some layers may need an aligning proxy. If a layer's model has
Chris@1490 2417 // a source model that is the reference model for the aligning
Chris@1508 2418 // model, and the layer is tagged as to be aligned, then we might
Chris@1508 2419 // use an aligning proxy. Note this is actually made use of only
Chris@1508 2420 // if m_useAligningProxy is true further down.
Chris@1490 2421
Chris@1490 2422 ModelId alignmentModelId;
Chris@1490 2423 ModelId alignmentReferenceId;
Chris@1490 2424 auto aligningModel = ModelById::get(getAligningModel());
Chris@1490 2425 if (aligningModel) {
Chris@1490 2426 alignmentModelId = aligningModel->getAlignment();
Chris@1490 2427 alignmentReferenceId = aligningModel->getAlignmentReference();
Chris@1493 2428 #ifdef DEBUG_VIEW_WIDGET_PAINT
Chris@1492 2429 SVCERR << "alignmentModelId = " << alignmentModelId << " (reference = " << alignmentReferenceId << ")" << endl;
Chris@1493 2430 #endif
Chris@1490 2431 } else {
Chris@1493 2432 #ifdef DEBUG_VIEW_WIDGET_PAINT
Chris@1490 2433 SVCERR << "no aligningModel" << endl;
Chris@1493 2434 #endif
Chris@1490 2435 }
Chris@1490 2436 ViewProxy aligningProxy(this, dpratio, alignmentModelId);
Chris@1490 2437
Chris@1357 2438 // Scrollable (cacheable) items first. If we are repainting the
Chris@1357 2439 // cache, then we paint these to the cache; otherwise straight to
Chris@1357 2440 // the buffer.
Chris@1357 2441 QRect areaToPaint;
Chris@1357 2442 QPainter paint;
Chris@1357 2443
Chris@1357 2444 if (shouldRepaintCache) {
Chris@1357 2445 paint.begin(m_cache);
Chris@1357 2446 areaToPaint = cacheAreaToRepaint;
Chris@1357 2447 } else {
Chris@1357 2448 paint.begin(m_buffer);
Chris@1357 2449 areaToPaint = requestedPaintArea;
Chris@1357 2450 }
Chris@1357 2451
Chris@1357 2452 setPaintFont(paint);
Chris@1357 2453 paint.setClipRect(areaToPaint);
Chris@1357 2454
Chris@1357 2455 paint.setPen(getBackground());
Chris@1357 2456 paint.setBrush(getBackground());
Chris@1357 2457 paint.drawRect(areaToPaint);
Chris@1357 2458
Chris@1357 2459 paint.setPen(getForeground());
Chris@1357 2460 paint.setBrush(Qt::NoBrush);
Chris@1357 2461
Chris@1357 2462 for (LayerList::iterator i = scrollables.begin();
Chris@1357 2463 i != scrollables.end(); ++i) {
Chris@1357 2464
Chris@1357 2465 paint.setRenderHint(QPainter::Antialiasing, false);
Chris@1357 2466 paint.save();
Chris@1357 2467
Chris@1490 2468 Layer *layer = *i;
Chris@1490 2469
Chris@1490 2470 bool useAligningProxy = false;
Chris@1490 2471 if (m_useAligningProxy) {
Chris@1490 2472 if (layer->getModel() == alignmentReferenceId ||
Chris@1490 2473 layer->getSourceModel() == alignmentReferenceId) {
Chris@1490 2474 useAligningProxy = true;
Chris@1490 2475 }
Chris@1490 2476 }
Chris@1490 2477
Chris@1357 2478 #ifdef DEBUG_VIEW_WIDGET_PAINT
Chris@1506 2479 SVCERR << "Painting scrollable layer " << layer << " (model " << layer->getModel() << ", source model " << layer->getSourceModel() << ") with shouldRepaintCache = " << shouldRepaintCache << ", useAligningProxy = " << useAligningProxy << ", dpratio = " << dpratio << ", areaToPaint = " << areaToPaint.x() << "," << areaToPaint.y() << " " << areaToPaint.width() << "x" << areaToPaint.height() << endl;
Chris@1357 2480 #endif
Chris@1490 2481
Chris@1490 2482 layer->paint(useAligningProxy ? &aligningProxy : &proxy,
Chris@1490 2483 paint, areaToPaint);
Chris@1357 2484
Chris@1357 2485 paint.restore();
Chris@1357 2486 }
Chris@1357 2487
Chris@1357 2488 paint.end();
Chris@1357 2489
Chris@1357 2490 if (shouldRepaintCache) {
Chris@1357 2491 // and now we have
Chris@1357 2492 m_cacheValid = true;
Chris@1266 2493 m_cacheCentreFrame = m_centreFrame;
Chris@1266 2494 m_cacheZoomLevel = m_zoomLevel;
Chris@127 2495 }
Chris@127 2496
Chris@1357 2497 if (shouldUseCache) {
Chris@1357 2498 paint.begin(m_buffer);
Chris@1357 2499 paint.drawPixmap(requestedPaintArea, *m_cache, requestedPaintArea);
Chris@1266 2500 paint.end();
Chris@127 2501 }
Chris@127 2502
Chris@1357 2503 // Now non-cacheable items.
Chris@1357 2504
Chris@952 2505 paint.begin(m_buffer);
Chris@1357 2506 paint.setClipRect(requestedPaintArea);
Chris@339 2507 setPaintFont(paint);
Chris@127 2508 if (scrollables.empty()) {
Chris@287 2509 paint.setPen(getBackground());
Chris@287 2510 paint.setBrush(getBackground());
Chris@1357 2511 paint.drawRect(requestedPaintArea);
Chris@127 2512 }
Chris@1266 2513
Chris@287 2514 paint.setPen(getForeground());
Chris@127 2515 paint.setBrush(Qt::NoBrush);
Chris@1266 2516
Chris@1357 2517 for (LayerList::iterator i = nonScrollables.begin();
Chris@1357 2518 i != nonScrollables.end(); ++i) {
Chris@1490 2519
Chris@1490 2520 Layer *layer = *i;
Chris@1490 2521
Chris@1490 2522 bool useAligningProxy = false;
Chris@1490 2523 if (m_useAligningProxy) {
Chris@1490 2524 if (layer->getModel() == alignmentReferenceId ||
Chris@1490 2525 layer->getSourceModel() == alignmentReferenceId) {
Chris@1490 2526 useAligningProxy = true;
Chris@1490 2527 }
Chris@1490 2528 }
Chris@1490 2529
Chris@951 2530 #ifdef DEBUG_VIEW_WIDGET_PAINT
Chris@1506 2531 SVCERR << "Painting non-scrollable layer " << layer << " (model " << layer->getModel() << ", source model " << layer->getSourceModel() << ") with shouldRepaintCache = " << shouldRepaintCache << ", useAligningProxy = " << useAligningProxy << ", dpratio = " << dpratio << ", requestedPaintArea = " << requestedPaintArea.x() << "," << requestedPaintArea.y() << " " << requestedPaintArea.width() << "x" << requestedPaintArea.height() << endl;
Chris@951 2532 #endif
Chris@1490 2533
Chris@1490 2534 layer->paint(useAligningProxy ? &aligningProxy : &proxy,
Chris@1490 2535 paint, requestedPaintArea);
Chris@127 2536 }
Chris@1266 2537
Chris@127 2538 paint.end();
Chris@1357 2539
Chris@1357 2540 // Now paint to widget from buffer: target rects from here on,
Chris@1357 2541 // unlike all the preceding, are at formal (1x) resolution
Chris@953 2542
Chris@953 2543 paint.begin(this);
Chris@339 2544 setPaintFont(paint);
Chris@953 2545 if (e) paint.setClipRect(e->rect());
Chris@1357 2546
Chris@1357 2547 QRect finalPaintRect = e ? e->rect() : rect();
Chris@1357 2548 paint.drawPixmap(finalPaintRect, *m_buffer,
Chris@1357 2549 scaledRect(finalPaintRect, dpratio));
Chris@1357 2550
Chris@1357 2551 drawSelections(paint);
Chris@1357 2552 drawPlayPointer(paint);
Chris@1357 2553
Chris@127 2554 paint.end();
Chris@127 2555
Chris@127 2556 QFrame::paintEvent(e);
Chris@127 2557 }
Chris@127 2558
Chris@127 2559 void
Chris@127 2560 View::drawSelections(QPainter &paint)
Chris@127 2561 {
Chris@217 2562 if (!hasTopLayerTimeXAxis()) return;
Chris@217 2563
Chris@127 2564 MultiSelection::SelectionList selections;
Chris@127 2565
Chris@127 2566 if (m_manager) {
Chris@1266 2567 selections = m_manager->getSelections();
Chris@1266 2568 if (m_manager->haveInProgressSelection()) {
Chris@1266 2569 bool exclusive;
Chris@1266 2570 Selection inProgressSelection =
Chris@1266 2571 m_manager->getInProgressSelection(exclusive);
Chris@1266 2572 if (exclusive) selections.clear();
Chris@1266 2573 selections.insert(inProgressSelection);
Chris@1266 2574 }
Chris@127 2575 }
Chris@127 2576
Chris@127 2577 paint.save();
Chris@183 2578
Chris@183 2579 bool translucent = !areLayerColoursSignificant();
Chris@183 2580
Chris@183 2581 if (translucent) {
Chris@183 2582 paint.setBrush(QColor(150, 150, 255, 80));
Chris@183 2583 } else {
Chris@183 2584 paint.setBrush(Qt::NoBrush);
Chris@183 2585 }
Chris@127 2586
Chris@908 2587 sv_samplerate_t sampleRate = getModelsSampleRate();
Chris@127 2588
Chris@127 2589 QPoint localPos;
Chris@908 2590 sv_frame_t illuminateFrame = -1;
Chris@127 2591 bool closeToLeft, closeToRight;
Chris@127 2592
Chris@127 2593 if (shouldIlluminateLocalSelection(localPos, closeToLeft, closeToRight)) {
Chris@1266 2594 illuminateFrame = getFrameForX(localPos.x());
Chris@127 2595 }
Chris@127 2596
Chris@127 2597 const QFontMetrics &metrics = paint.fontMetrics();
Chris@127 2598
Chris@127 2599 for (MultiSelection::SelectionList::iterator i = selections.begin();
Chris@1266 2600 i != selections.end(); ++i) {
Chris@1266 2601
Chris@1266 2602 int p0 = getXForFrame(alignFromReference(i->getStartFrame()));
Chris@1266 2603 int p1 = getXForFrame(alignFromReference(i->getEndFrame()));
Chris@1266 2604
Chris@1266 2605 if (p1 < 0 || p0 > width()) continue;
Chris@127 2606
Chris@127 2607 #ifdef DEBUG_VIEW_WIDGET_PAINT
Chris@1266 2608 SVDEBUG << "View::drawSelections: " << p0 << ",-1 [" << (p1-p0) << "x" << (height()+1) << "]" << endl;
Chris@127 2609 #endif
Chris@127 2610
Chris@1266 2611 bool illuminateThis =
Chris@1266 2612 (illuminateFrame >= 0 && i->contains(illuminateFrame));
Chris@1266 2613
Chris@1270 2614 double h = height();
Chris@1401 2615 double penWidth = scalePenWidth(1.0);
Chris@1270 2616 double half = penWidth/2.0;
Chris@1270 2617
Chris@1270 2618 paint.setPen(QPen(QColor(150, 150, 255), penWidth));
Chris@183 2619
Chris@183 2620 if (translucent && shouldLabelSelections()) {
Chris@1270 2621 paint.drawRect(QRectF(p0, -penWidth, p1 - p0, h + 2*penWidth));
Chris@183 2622 } else {
Chris@183 2623 // Make the top & bottom lines of the box visible if we
Chris@183 2624 // are lacking some of the other visual cues. There's no
Chris@183 2625 // particular logic to this, it's just a question of what
Chris@183 2626 // I happen to think looks nice.
Chris@1270 2627 paint.drawRect(QRectF(p0, half, p1 - p0, h - penWidth));
Chris@183 2628 }
Chris@127 2629
Chris@1266 2630 if (illuminateThis) {
Chris@1266 2631 paint.save();
Chris@1401 2632 penWidth = scalePenWidth(2.0);
Chris@1270 2633 half = penWidth/2.0;
Chris@1270 2634 paint.setPen(QPen(getForeground(), penWidth));
Chris@1266 2635 if (closeToLeft) {
Chris@1270 2636 paint.drawLine(QLineF(p0, half, p1, half));
Chris@1270 2637 paint.drawLine(QLineF(p0, half, p0, h - half));
Chris@1270 2638 paint.drawLine(QLineF(p0, h - half, p1, h - half));
Chris@1266 2639 } else if (closeToRight) {
Chris@1270 2640 paint.drawLine(QLineF(p0, half, p1, half));
Chris@1270 2641 paint.drawLine(QLineF(p1, half, p1, h - half));
Chris@1270 2642 paint.drawLine(QLineF(p0, h - half, p1, h - half));
Chris@1266 2643 } else {
Chris@1266 2644 paint.setBrush(Qt::NoBrush);
Chris@1270 2645 paint.drawRect(QRectF(p0, half, p1 - p0, h - penWidth));
Chris@1266 2646 }
Chris@1266 2647 paint.restore();
Chris@1266 2648 }
Chris@1266 2649
Chris@1266 2650 if (sampleRate && shouldLabelSelections() && m_manager &&
Chris@189 2651 m_manager->shouldShowSelectionExtents()) {
Chris@1266 2652
Chris@1266 2653 QString startText = QString("%1 / %2")
Chris@1266 2654 .arg(QString::fromStdString
Chris@1266 2655 (RealTime::frame2RealTime
Chris@1266 2656 (i->getStartFrame(), sampleRate).toText(true)))
Chris@1266 2657 .arg(i->getStartFrame());
Chris@1266 2658
Chris@1266 2659 QString endText = QString(" %1 / %2")
Chris@1266 2660 .arg(QString::fromStdString
Chris@1266 2661 (RealTime::frame2RealTime
Chris@1266 2662 (i->getEndFrame(), sampleRate).toText(true)))
Chris@1266 2663 .arg(i->getEndFrame());
Chris@1266 2664
Chris@1266 2665 QString durationText = QString("(%1 / %2) ")
Chris@1266 2666 .arg(QString::fromStdString
Chris@1266 2667 (RealTime::frame2RealTime
Chris@1266 2668 (i->getEndFrame() - i->getStartFrame(), sampleRate)
Chris@1266 2669 .toText(true)))
Chris@1266 2670 .arg(i->getEndFrame() - i->getStartFrame());
Chris@1476 2671
Chris@1476 2672 // Qt 5.13 deprecates QFontMetrics::width(), but its suggested
Chris@1476 2673 // replacement (horizontalAdvance) was only added in Qt 5.11
Chris@1476 2674 // which is too new for us
Chris@1476 2675 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
Chris@1266 2676
Chris@1266 2677 int sw = metrics.width(startText),
Chris@1266 2678 ew = metrics.width(endText),
Chris@1266 2679 dw = metrics.width(durationText);
Chris@1266 2680
Chris@1266 2681 int sy = metrics.ascent() + metrics.height() + 4;
Chris@1266 2682 int ey = sy;
Chris@1266 2683 int dy = sy + metrics.height();
Chris@1266 2684
Chris@1266 2685 int sx = p0 + 2;
Chris@1266 2686 int ex = sx;
Chris@1266 2687 int dx = sx;
Chris@127 2688
Chris@501 2689 bool durationBothEnds = true;
Chris@501 2690
Chris@1266 2691 if (sw + ew > (p1 - p0)) {
Chris@1266 2692 ey += metrics.height();
Chris@1266 2693 dy += metrics.height();
Chris@501 2694 durationBothEnds = false;
Chris@1266 2695 }
Chris@1266 2696
Chris@1266 2697 if (ew < (p1 - p0)) {
Chris@1266 2698 ex = p1 - 2 - ew;
Chris@1266 2699 }
Chris@1266 2700
Chris@1266 2701 if (dw < (p1 - p0)) {
Chris@1266 2702 dx = p1 - 2 - dw;
Chris@1266 2703 }
Chris@127 2704
Chris@1193 2705 PaintAssistant::drawVisibleText(this, paint, sx, sy, startText,
Chris@1193 2706 PaintAssistant::OutlinedText);
Chris@1193 2707 PaintAssistant::drawVisibleText(this, paint, ex, ey, endText,
Chris@1193 2708 PaintAssistant::OutlinedText);
Chris@1193 2709 PaintAssistant::drawVisibleText(this, paint, dx, dy, durationText,
Chris@1193 2710 PaintAssistant::OutlinedText);
Chris@501 2711 if (durationBothEnds) {
Chris@1193 2712 PaintAssistant::drawVisibleText(this, paint, sx, dy, durationText,
Chris@1193 2713 PaintAssistant::OutlinedText);
Chris@501 2714 }
Chris@1266 2715 }
Chris@127 2716 }
Chris@127 2717
Chris@127 2718 paint.restore();
Chris@127 2719 }
Chris@127 2720
Chris@267 2721 void
Chris@1357 2722 View::drawPlayPointer(QPainter &paint)
Chris@1357 2723 {
Chris@1357 2724 bool showPlayPointer = true;
Chris@1357 2725
Chris@1357 2726 if (m_followPlay == PlaybackScrollContinuous) {
Chris@1357 2727 showPlayPointer = false;
Chris@1357 2728 } else if (m_playPointerFrame <= getStartFrame() ||
Chris@1357 2729 m_playPointerFrame >= getEndFrame()) {
Chris@1357 2730 showPlayPointer = false;
Chris@1357 2731 } else if (m_manager && !m_manager->isPlaying()) {
Chris@1357 2732 if (m_playPointerFrame == getCentreFrame() &&
Chris@1357 2733 m_manager->shouldShowCentreLine() &&
Chris@1357 2734 m_followPlay != PlaybackIgnore) {
Chris@1357 2735 // Don't show the play pointer when it is redundant with
Chris@1357 2736 // the centre line
Chris@1357 2737 showPlayPointer = false;
Chris@1357 2738 }
Chris@1357 2739 }
Chris@1357 2740
Chris@1357 2741 if (showPlayPointer) {
Chris@1357 2742
Chris@1357 2743 int playx = getXForFrame(m_playPointerFrame);
Chris@1357 2744
Chris@1357 2745 paint.setPen(getForeground());
Chris@1357 2746 paint.drawLine(playx - 1, 0, playx - 1, height() - 1);
Chris@1357 2747 paint.drawLine(playx + 1, 0, playx + 1, height() - 1);
Chris@1357 2748 paint.drawPoint(playx, 0);
Chris@1357 2749 paint.drawPoint(playx, height() - 1);
Chris@1357 2750 paint.setPen(getBackground());
Chris@1357 2751 paint.drawLine(playx, 1, playx, height() - 2);
Chris@1357 2752 }
Chris@1357 2753 }
Chris@1357 2754
Chris@1357 2755 void
Chris@270 2756 View::drawMeasurementRect(QPainter &paint, const Layer *topLayer, QRect r,
Chris@270 2757 bool focus) const
Chris@267 2758 {
Chris@587 2759 // SVDEBUG << "View::drawMeasurementRect(" << r.x() << "," << r.y() << " "
Chris@585 2760 // << r.width() << "x" << r.height() << ")" << endl;
Chris@268 2761
Chris@267 2762 if (r.x() + r.width() < 0 || r.x() >= width()) return;
Chris@267 2763
Chris@270 2764 if (r.width() != 0 || r.height() != 0) {
Chris@270 2765 paint.save();
Chris@270 2766 if (focus) {
Chris@270 2767 paint.setPen(Qt::NoPen);
Chris@272 2768 QColor brushColour(Qt::black);
Chris@272 2769 brushColour.setAlpha(hasLightBackground() ? 15 : 40);
Chris@270 2770 paint.setBrush(brushColour);
Chris@270 2771 if (r.x() > 0) {
Chris@270 2772 paint.drawRect(0, 0, r.x(), height());
Chris@270 2773 }
Chris@270 2774 if (r.x() + r.width() < width()) {
Chris@270 2775 paint.drawRect(r.x() + r.width(), 0, width()-r.x()-r.width(), height());
Chris@270 2776 }
Chris@270 2777 if (r.y() > 0) {
Chris@270 2778 paint.drawRect(r.x(), 0, r.width(), r.y());
Chris@270 2779 }
Chris@270 2780 if (r.y() + r.height() < height()) {
Chris@270 2781 paint.drawRect(r.x(), r.y() + r.height(), r.width(), height()-r.y()-r.height());
Chris@270 2782 }
Chris@270 2783 paint.setBrush(Qt::NoBrush);
Chris@270 2784 }
Chris@270 2785 paint.setPen(Qt::green);
Chris@270 2786 paint.drawRect(r);
Chris@270 2787 paint.restore();
Chris@270 2788 } else {
Chris@270 2789 paint.save();
Chris@270 2790 paint.setPen(Qt::green);
Chris@270 2791 paint.drawPoint(r.x(), r.y());
Chris@270 2792 paint.restore();
Chris@270 2793 }
Chris@270 2794
Chris@270 2795 if (!focus) return;
Chris@270 2796
Chris@278 2797 paint.save();
Chris@278 2798 QFont fn = paint.font();
Chris@278 2799 if (fn.pointSize() > 8) {
Chris@278 2800 fn.setPointSize(fn.pointSize() - 1);
Chris@278 2801 paint.setFont(fn);
Chris@278 2802 }
Chris@278 2803
Chris@267 2804 int fontHeight = paint.fontMetrics().height();
Chris@267 2805 int fontAscent = paint.fontMetrics().ascent();
Chris@267 2806
Chris@904 2807 double v0, v1;
Chris@267 2808 QString u0, u1;
Chris@267 2809 bool b0 = false, b1 = false;
Chris@267 2810
Chris@267 2811 QString axs, ays, bxs, bys, dxs, dys;
Chris@267 2812
Chris@267 2813 int axx, axy, bxx, bxy, dxx, dxy;
Chris@267 2814 int aw = 0, bw = 0, dw = 0;
Chris@267 2815
Chris@267 2816 int labelCount = 0;
Chris@267 2817
Chris@362 2818 // top-left point, x-coord
Chris@362 2819
Chris@267 2820 if ((b0 = topLayer->getXScaleValue(this, r.x(), v0, u0))) {
Chris@267 2821 axs = QString("%1 %2").arg(v0).arg(u0);
Chris@278 2822 if (u0 == "Hz" && Pitch::isFrequencyInMidiRange(v0)) {
Chris@278 2823 axs = QString("%1 (%2)").arg(axs)
Chris@278 2824 .arg(Pitch::getPitchLabelForFrequency(v0));
Chris@278 2825 }
Chris@267 2826 aw = paint.fontMetrics().width(axs);
Chris@267 2827 ++labelCount;
Chris@267 2828 }
Chris@362 2829
Chris@362 2830 // bottom-right point, x-coord
Chris@267 2831
Chris@267 2832 if (r.width() > 0) {
Chris@267 2833 if ((b1 = topLayer->getXScaleValue(this, r.x() + r.width(), v1, u1))) {
Chris@267 2834 bxs = QString("%1 %2").arg(v1).arg(u1);
Chris@278 2835 if (u1 == "Hz" && Pitch::isFrequencyInMidiRange(v1)) {
Chris@278 2836 bxs = QString("%1 (%2)").arg(bxs)
Chris@278 2837 .arg(Pitch::getPitchLabelForFrequency(v1));
Chris@278 2838 }
Chris@267 2839 bw = paint.fontMetrics().width(bxs);
Chris@267 2840 }
Chris@267 2841 }
Chris@362 2842
Chris@362 2843 // dimension, width
Chris@267 2844
Chris@283 2845 if (b0 && b1 && v1 != v0 && u0 == u1) {
Chris@362 2846 dxs = QString("[%1 %2]").arg(fabs(v1 - v0)).arg(u1);
Chris@267 2847 dw = paint.fontMetrics().width(dxs);
Chris@267 2848 }
Chris@267 2849
Chris@267 2850 b0 = false;
Chris@267 2851 b1 = false;
Chris@267 2852
Chris@362 2853 // top-left point, y-coord
Chris@362 2854
Chris@267 2855 if ((b0 = topLayer->getYScaleValue(this, r.y(), v0, u0))) {
Chris@267 2856 ays = QString("%1 %2").arg(v0).arg(u0);
Chris@278 2857 if (u0 == "Hz" && Pitch::isFrequencyInMidiRange(v0)) {
Chris@278 2858 ays = QString("%1 (%2)").arg(ays)
Chris@278 2859 .arg(Pitch::getPitchLabelForFrequency(v0));
Chris@278 2860 }
Chris@267 2861 aw = std::max(aw, paint.fontMetrics().width(ays));
Chris@267 2862 ++labelCount;
Chris@267 2863 }
Chris@267 2864
Chris@362 2865 // bottom-right point, y-coord
Chris@362 2866
Chris@267 2867 if (r.height() > 0) {
Chris@267 2868 if ((b1 = topLayer->getYScaleValue(this, r.y() + r.height(), v1, u1))) {
Chris@267 2869 bys = QString("%1 %2").arg(v1).arg(u1);
Chris@278 2870 if (u1 == "Hz" && Pitch::isFrequencyInMidiRange(v1)) {
Chris@278 2871 bys = QString("%1 (%2)").arg(bys)
Chris@278 2872 .arg(Pitch::getPitchLabelForFrequency(v1));
Chris@278 2873 }
Chris@267 2874 bw = std::max(bw, paint.fontMetrics().width(bys));
Chris@267 2875 }
Chris@267 2876 }
Chris@274 2877
Chris@274 2878 bool bd = false;
Chris@904 2879 double dy = 0.f;
Chris@274 2880 QString du;
Chris@274 2881
Chris@362 2882 // dimension, height
Chris@362 2883
Chris@274 2884 if ((bd = topLayer->getYScaleDifference(this, r.y(), r.y() + r.height(),
Chris@283 2885 dy, du)) &&
Chris@283 2886 dy != 0) {
Chris@274 2887 if (du != "") {
Chris@362 2888 if (du == "Hz") {
Chris@362 2889 int semis;
Chris@904 2890 double cents;
Chris@362 2891 semis = Pitch::getPitchForFrequencyDifference(v0, v1, &cents);
Chris@362 2892 dys = QString("[%1 %2 (%3)]")
Chris@362 2893 .arg(dy).arg(du)
Chris@362 2894 .arg(Pitch::getLabelForPitchRange(semis, cents));
Chris@362 2895 } else {
Chris@362 2896 dys = QString("[%1 %2]").arg(dy).arg(du);
Chris@362 2897 }
Chris@274 2898 } else {
Chris@362 2899 dys = QString("[%1]").arg(dy);
Chris@274 2900 }
Chris@267 2901 dw = std::max(dw, paint.fontMetrics().width(dys));
Chris@267 2902 }
Chris@267 2903
Chris@267 2904 int mw = r.width();
Chris@267 2905 int mh = r.height();
Chris@267 2906
Chris@267 2907 bool edgeLabelsInside = false;
Chris@267 2908 bool sizeLabelsInside = false;
Chris@267 2909
Chris@267 2910 if (mw < std::max(aw, std::max(bw, dw)) + 4) {
Chris@267 2911 // defaults stand
Chris@267 2912 } else if (mw < aw + bw + 4) {
Chris@267 2913 if (mh > fontHeight * labelCount * 3 + 4) {
Chris@267 2914 edgeLabelsInside = true;
Chris@267 2915 sizeLabelsInside = true;
Chris@267 2916 } else if (mh > fontHeight * labelCount * 2 + 4) {
Chris@267 2917 edgeLabelsInside = true;
Chris@267 2918 }
Chris@267 2919 } else if (mw < aw + bw + dw + 4) {
Chris@267 2920 if (mh > fontHeight * labelCount * 3 + 4) {
Chris@267 2921 edgeLabelsInside = true;
Chris@267 2922 sizeLabelsInside = true;
Chris@267 2923 } else if (mh > fontHeight * labelCount + 4) {
Chris@267 2924 edgeLabelsInside = true;
Chris@267 2925 }
Chris@267 2926 } else {
Chris@267 2927 if (mh > fontHeight * labelCount + 4) {
Chris@267 2928 edgeLabelsInside = true;
Chris@267 2929 sizeLabelsInside = true;
Chris@267 2930 }
Chris@267 2931 }
Chris@267 2932
Chris@267 2933 if (edgeLabelsInside) {
Chris@267 2934
Chris@267 2935 axx = r.x() + 2;
Chris@267 2936 axy = r.y() + fontAscent + 2;
Chris@267 2937
Chris@267 2938 bxx = r.x() + r.width() - bw - 2;
Chris@267 2939 bxy = r.y() + r.height() - (labelCount-1) * fontHeight - 2;
Chris@267 2940
Chris@267 2941 } else {
Chris@267 2942
Chris@267 2943 axx = r.x() - aw - 2;
Chris@267 2944 axy = r.y() + fontAscent;
Chris@267 2945
Chris@267 2946 bxx = r.x() + r.width() + 2;
Chris@267 2947 bxy = r.y() + r.height() - (labelCount-1) * fontHeight;
Chris@267 2948 }
Chris@267 2949
Chris@267 2950 dxx = r.width()/2 + r.x() - dw/2;
Chris@267 2951
Chris@267 2952 if (sizeLabelsInside) {
Chris@267 2953
Chris@267 2954 dxy = r.height()/2 + r.y() - (labelCount * fontHeight)/2 + fontAscent;
Chris@267 2955
Chris@267 2956 } else {
Chris@267 2957
Chris@267 2958 dxy = r.y() + r.height() + fontAscent + 2;
Chris@267 2959 }
Chris@267 2960
Chris@267 2961 if (axs != "") {
Chris@1078 2962 PaintAssistant::drawVisibleText(this, paint, axx, axy, axs, PaintAssistant::OutlinedText);
Chris@267 2963 axy += fontHeight;
Chris@267 2964 }
Chris@267 2965
Chris@267 2966 if (ays != "") {
Chris@1078 2967 PaintAssistant::drawVisibleText(this, paint, axx, axy, ays, PaintAssistant::OutlinedText);
Chris@267 2968 axy += fontHeight;
Chris@267 2969 }
Chris@267 2970
Chris@267 2971 if (bxs != "") {
Chris@1078 2972 PaintAssistant::drawVisibleText(this, paint, bxx, bxy, bxs, PaintAssistant::OutlinedText);
Chris@267 2973 bxy += fontHeight;
Chris@267 2974 }
Chris@267 2975
Chris@267 2976 if (bys != "") {
Chris@1078 2977 PaintAssistant::drawVisibleText(this, paint, bxx, bxy, bys, PaintAssistant::OutlinedText);
Chris@267 2978 bxy += fontHeight;
Chris@267 2979 }
Chris@267 2980
Chris@267 2981 if (dxs != "") {
Chris@1078 2982 PaintAssistant::drawVisibleText(this, paint, dxx, dxy, dxs, PaintAssistant::OutlinedText);
Chris@267 2983 dxy += fontHeight;
Chris@267 2984 }
Chris@267 2985
Chris@267 2986 if (dys != "") {
Chris@1078 2987 PaintAssistant::drawVisibleText(this, paint, dxx, dxy, dys, PaintAssistant::OutlinedText);
Chris@267 2988 dxy += fontHeight;
Chris@267 2989 }
Chris@278 2990
Chris@278 2991 paint.restore();
Chris@267 2992 }
Chris@267 2993
Chris@227 2994 bool
Chris@908 2995 View::render(QPainter &paint, int xorigin, sv_frame_t f0, sv_frame_t f1)
Chris@227 2996 {
Chris@1327 2997 int x0 = int(round(m_zoomLevel.framesToPixels(double(f0))));
Chris@1327 2998 int x1 = int(round(m_zoomLevel.framesToPixels(double(f1))));
Chris@806 2999
Chris@806 3000 int w = x1 - x0;
Chris@806 3001
Chris@908 3002 sv_frame_t origCentreFrame = m_centreFrame;
Chris@227 3003
Chris@227 3004 bool someLayersIncomplete = false;
Chris@227 3005
Chris@835 3006 for (LayerList::iterator i = m_layerStack.begin();
Chris@835 3007 i != m_layerStack.end(); ++i) {
Chris@227 3008
Chris@227 3009 int c = (*i)->getCompletion(this);
Chris@227 3010 if (c < 100) {
Chris@227 3011 someLayersIncomplete = true;
Chris@227 3012 break;
Chris@227 3013 }
Chris@227 3014 }
Chris@227 3015
Chris@227 3016 if (someLayersIncomplete) {
Chris@227 3017
Chris@227 3018 QProgressDialog progress(tr("Waiting for layers to be ready..."),
Chris@227 3019 tr("Cancel"), 0, 100, this);
Chris@227 3020
Chris@227 3021 int layerCompletion = 0;
Chris@227 3022
Chris@227 3023 while (layerCompletion < 100) {
Chris@227 3024
Chris@835 3025 for (LayerList::iterator i = m_layerStack.begin();
Chris@835 3026 i != m_layerStack.end(); ++i) {
Chris@227 3027
Chris@227 3028 int c = (*i)->getCompletion(this);
Chris@835 3029 if (i == m_layerStack.begin() || c < layerCompletion) {
Chris@227 3030 layerCompletion = c;
Chris@227 3031 }
Chris@227 3032 }
Chris@227 3033
Chris@227 3034 if (layerCompletion >= 100) break;
Chris@227 3035
Chris@227 3036 progress.setValue(layerCompletion);
Chris@227 3037 qApp->processEvents();
Chris@227 3038 if (progress.wasCanceled()) {
Chris@227 3039 update();
Chris@227 3040 return false;
Chris@227 3041 }
Chris@227 3042
Chris@227 3043 usleep(50000);
Chris@227 3044 }
Chris@227 3045 }
Chris@227 3046
Chris@227 3047 QProgressDialog progress(tr("Rendering image..."),
Chris@227 3048 tr("Cancel"), 0, w / width(), this);
Chris@227 3049
Chris@806 3050 for (int x = 0; x < w; x += width()) {
Chris@227 3051
Chris@227 3052 progress.setValue(x / width());
Chris@227 3053 qApp->processEvents();
Chris@227 3054 if (progress.wasCanceled()) {
Chris@227 3055 m_centreFrame = origCentreFrame;
Chris@227 3056 update();
Chris@227 3057 return false;
Chris@227 3058 }
Chris@227 3059
Chris@1327 3060 m_centreFrame = f0 + sv_frame_t(round(m_zoomLevel.pixelsToFrames
Chris@1327 3061 (x + width()/2)));
Chris@227 3062
Chris@227 3063 QRect chunk(0, 0, width(), height());
Chris@227 3064
Chris@287 3065 paint.setPen(getBackground());
Chris@287 3066 paint.setBrush(getBackground());
Chris@227 3067
Chris@1266 3068 paint.drawRect(QRect(xorigin + x, 0, width(), height()));
Chris@1266 3069
Chris@1266 3070 paint.setPen(getForeground());
Chris@1266 3071 paint.setBrush(Qt::NoBrush);
Chris@1266 3072
Chris@1266 3073 for (LayerList::iterator i = m_layerStack.begin();
Chris@835 3074 i != m_layerStack.end(); ++i) {
Chris@919 3075 if (!((*i)->isLayerDormant(this))){
Chris@919 3076
Chris@919 3077 paint.setRenderHint(QPainter::Antialiasing, false);
Chris@919 3078
Chris@919 3079 paint.save();
Chris@919 3080 paint.translate(xorigin + x, 0);
Chris@919 3081
Chris@1506 3082 SVCERR << "Centre frame now: " << m_centreFrame << " drawing to " << chunk.x() + x + xorigin << ", " << chunk.width() << endl;
Chris@919 3083
Chris@919 3084 (*i)->setSynchronousPainting(true);
Chris@919 3085
Chris@919 3086 (*i)->paint(this, paint, chunk);
Chris@919 3087
Chris@919 3088 (*i)->setSynchronousPainting(false);
Chris@919 3089
Chris@919 3090 paint.restore();
Chris@919 3091 }
Chris@1266 3092 }
Chris@227 3093 }
Chris@227 3094
Chris@227 3095 m_centreFrame = origCentreFrame;
Chris@227 3096 update();
Chris@227 3097 return true;
Chris@227 3098 }
Chris@227 3099
Chris@227 3100 QImage *
Chris@1202 3101 View::renderToNewImage()
Chris@227 3102 {
Chris@908 3103 sv_frame_t f0 = getModelsStartFrame();
Chris@908 3104 sv_frame_t f1 = getModelsEndFrame();
Chris@227 3105
Chris@1202 3106 return renderPartToNewImage(f0, f1);
Chris@229 3107 }
Chris@229 3108
Chris@229 3109 QImage *
Chris@1202 3110 View::renderPartToNewImage(sv_frame_t f0, sv_frame_t f1)
Chris@229 3111 {
Chris@1327 3112 int x0 = int(round(getZoomLevel().framesToPixels(double(f0))));
Chris@1327 3113 int x1 = int(round(getZoomLevel().framesToPixels(double(f1))));
Chris@227 3114
Chris@227 3115 QImage *image = new QImage(x1 - x0, height(), QImage::Format_RGB32);
Chris@227 3116
Chris@227 3117 QPainter *paint = new QPainter(image);
Chris@229 3118 if (!render(*paint, 0, f0, f1)) {
Chris@227 3119 delete paint;
Chris@227 3120 delete image;
Chris@1408 3121 return nullptr;
Chris@227 3122 } else {
Chris@227 3123 delete paint;
Chris@227 3124 return image;
Chris@227 3125 }
Chris@227 3126 }
Chris@227 3127
Chris@229 3128 QSize
Chris@1202 3129 View::getRenderedImageSize()
Chris@229 3130 {
Chris@908 3131 sv_frame_t f0 = getModelsStartFrame();
Chris@908 3132 sv_frame_t f1 = getModelsEndFrame();
Chris@229 3133
Chris@1202 3134 return getRenderedPartImageSize(f0, f1);
Chris@229 3135 }
Chris@229 3136
Chris@229 3137 QSize
Chris@1202 3138 View::getRenderedPartImageSize(sv_frame_t f0, sv_frame_t f1)
Chris@229 3139 {
Chris@1327 3140 int x0 = int(round(getZoomLevel().framesToPixels(double(f0))));
Chris@1327 3141 int x1 = int(round(getZoomLevel().framesToPixels(double(f1))));
Chris@229 3142
Chris@229 3143 return QSize(x1 - x0, height());
Chris@229 3144 }
Chris@229 3145
Chris@1202 3146 bool
Chris@1202 3147 View::renderToSvgFile(QString filename)
Chris@1202 3148 {
Chris@1202 3149 sv_frame_t f0 = getModelsStartFrame();
Chris@1202 3150 sv_frame_t f1 = getModelsEndFrame();
Chris@1202 3151
Chris@1202 3152 return renderPartToSvgFile(filename, f0, f1);
Chris@1202 3153 }
Chris@1202 3154
Chris@1202 3155 bool
Chris@1202 3156 View::renderPartToSvgFile(QString filename, sv_frame_t f0, sv_frame_t f1)
Chris@1202 3157 {
Chris@1327 3158 int x0 = int(round(getZoomLevel().framesToPixels(double(f0))));
Chris@1327 3159 int x1 = int(round(getZoomLevel().framesToPixels(double(f1))));
Chris@1202 3160
Chris@1202 3161 QSvgGenerator generator;
Chris@1202 3162 generator.setFileName(filename);
Chris@1202 3163 generator.setSize(QSize(x1 - x0, height()));
Chris@1202 3164 generator.setViewBox(QRect(0, 0, x1 - x0, height()));
Chris@1202 3165 generator.setTitle(tr("Exported image from %1")
Chris@1202 3166 .arg(QApplication::applicationName()));
Chris@1202 3167
Chris@1202 3168 QPainter paint;
Chris@1202 3169 paint.begin(&generator);
Chris@1202 3170 bool result = render(paint, 0, f0, f1);
Chris@1202 3171 paint.end();
Chris@1202 3172 return result;
Chris@1202 3173 }
Chris@1202 3174
Chris@316 3175 void
Chris@316 3176 View::toXml(QTextStream &stream,
Chris@316 3177 QString indent, QString extraAttributes) const
Chris@127 3178 {
Chris@316 3179 stream << indent;
Chris@127 3180
Chris@1327 3181 int classicZoomValue, deepZoomValue;
Chris@1327 3182
Chris@1327 3183 if (m_zoomLevel.zone == ZoomLevel::FramesPerPixel) {
Chris@1327 3184 classicZoomValue = m_zoomLevel.level;
Chris@1327 3185 deepZoomValue = 1;
Chris@1327 3186 } else {
Chris@1327 3187 classicZoomValue = 1;
Chris@1327 3188 deepZoomValue = m_zoomLevel.level;
Chris@1327 3189 }
Chris@1327 3190
Chris@316 3191 stream << QString("<view "
Chris@316 3192 "centre=\"%1\" "
Chris@316 3193 "zoom=\"%2\" "
Chris@1327 3194 "deepZoom=\"%3\" "
Chris@1327 3195 "followPan=\"%4\" "
Chris@1327 3196 "followZoom=\"%5\" "
Chris@1327 3197 "tracking=\"%6\" "
Chris@1331 3198 " %7>\n")
Chris@1266 3199 .arg(m_centreFrame)
Chris@1327 3200 .arg(classicZoomValue)
Chris@1327 3201 .arg(deepZoomValue)
Chris@1266 3202 .arg(m_followPan)
Chris@1266 3203 .arg(m_followZoom)
Chris@1266 3204 .arg(m_followPlay == PlaybackScrollContinuous ? "scroll" :
Chris@1266 3205 m_followPlay == PlaybackScrollPageWithCentre ? "page" :
Chris@1266 3206 m_followPlay == PlaybackScrollPage ? "daw" :
Chris@815 3207 "ignore")
Chris@1266 3208 .arg(extraAttributes);
Chris@127 3209
Chris@838 3210 for (int i = 0; i < (int)m_fixedOrderLayers.size(); ++i) {
Chris@838 3211 bool visible = !m_fixedOrderLayers[i]->isLayerDormant(this);
Chris@838 3212 m_fixedOrderLayers[i]->toBriefXml(stream, indent + " ",
Chris@838 3213 QString("visible=\"%1\"")
Chris@838 3214 .arg(visible ? "true" : "false"));
Chris@127 3215 }
Chris@127 3216
Chris@316 3217 stream << indent + "</view>\n";
Chris@127 3218 }
Chris@127 3219
Chris@127 3220 ViewPropertyContainer::ViewPropertyContainer(View *v) :
Chris@127 3221 m_v(v)
Chris@127 3222 {
Chris@1506 3223 // SVCERR << "ViewPropertyContainer: " << getId() << " is owned by View " << v << endl;
Chris@127 3224 connect(m_v, SIGNAL(propertyChanged(PropertyContainer::PropertyName)),
Chris@1266 3225 this, SIGNAL(propertyChanged(PropertyContainer::PropertyName)));
Chris@127 3226 }
Chris@127 3227
Chris@728 3228 ViewPropertyContainer::~ViewPropertyContainer()
Chris@728 3229 {
Chris@728 3230 }