annotate view/View.cpp @ 1615:911330a28a7c

Where the "below" view represents only a subset of the "above" view, cut off the feature mappings at the outer edges of the "below" view - don't map everything outside this (it would all just map onto the same single points at beginning and end, which is excessive, confusing and not useful)
author Chris Cannam
date Thu, 02 Jul 2020 15:37:43 +0100
parents 52d4bfba5b3d
children
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@1560 865 pbr.stallCheckTimer = new QTimer(this);
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@1607 2202 int pointSize = Preferences::getInstance()->getViewFontSize() * scaleFactor;
Chris@1607 2203 font.setPointSize(pointSize);
Chris@1607 2204
Chris@1607 2205 int h = height();
Chris@1607 2206 int fh = QFontMetrics(font).height();
Chris@1607 2207 if (pointSize > 6) {
Chris@1607 2208 if (h < fh * 2.1) {
Chris@1607 2209 font.setPointSize(pointSize - 2);
Chris@1607 2210 } else if (h < fh * 3.1) {
Chris@1607 2211 font.setPointSize(pointSize - 1);
Chris@1607 2212 }
Chris@1607 2213 }
Chris@1607 2214
Chris@339 2215 paint.setFont(font);
Chris@339 2216 }
Chris@339 2217
Chris@915 2218 QRect
Chris@915 2219 View::getPaintRect() const
Chris@915 2220 {
Chris@919 2221 return rect();
Chris@915 2222 }
Chris@915 2223
Chris@339 2224 void
Chris@127 2225 View::paintEvent(QPaintEvent *e)
Chris@127 2226 {
Chris@127 2227 // Profiler prof("View::paintEvent", false);
Chris@1506 2228
Chris@1506 2229 #ifdef DEBUG_VIEW_WIDGET_PAINT
Chris@1506 2230 {
Chris@1506 2231 sv_frame_t startFrame = getStartFrame();
Chris@1506 2232 SVCERR << "View[" << getId() << "]::paintEvent: centre frame is " << m_centreFrame << " (start frame " << startFrame << ", height " << height() << ")" << endl;
Chris@1506 2233 }
Chris@1506 2234 #endif
matthiasm@785 2235
Chris@835 2236 if (m_layerStack.empty()) {
Chris@1266 2237 QFrame::paintEvent(e);
Chris@1266 2238 return;
Chris@127 2239 }
Chris@127 2240
Chris@127 2241 // ensure our constraints are met
Chris@1354 2242 m_zoomLevel = getZoomConstraintLevel
Chris@1354 2243 (m_zoomLevel, ZoomConstraint::RoundNearest);
Chris@127 2244
Chris@1357 2245 // We have a cache, which retains the state of scrollable (back)
Chris@1357 2246 // layers from one paint to the next, and a buffer, which we paint
Chris@1357 2247 // onto before copying directly to the widget. Both are at scaled
Chris@1357 2248 // resolution (e.g. 2x on a pixel-doubled display), whereas the
Chris@1357 2249 // paint event always comes in at formal (1x) resolution.
Chris@1357 2250
Chris@1357 2251 // If we touch the cache, we always leave it in a valid state
Chris@1357 2252 // across its whole extent. When another method invalidates the
Chris@1357 2253 // cache, it does so by setting m_cacheValid false, so if that
Chris@1357 2254 // flag is true on entry, then the cache is valid across its whole
Chris@1357 2255 // extent - although it may be valid for a different centre frame,
Chris@1357 2256 // zoom level, or view size from those now in effect.
Chris@1357 2257
Chris@1357 2258 // Our process goes:
Chris@1357 2259 //
Chris@1357 2260 // 1. Check whether we have any scrollable (cacheable) layers. If
Chris@1357 2261 // we don't, then invalidate and ignore the cache and go to
Chris@1357 2262 // step 5. Otherwise:
Chris@1357 2263 //
Chris@1357 2264 // 2. Check the cache, scroll as necessary, identify any area that
Chris@1357 2265 // needs to be refreshed (this might be the whole cache).
Chris@1357 2266 //
Chris@1357 2267 // 3. Paint to cache the area that needs to be refreshed, from the
Chris@1357 2268 // stack of scrollable layers.
Chris@1357 2269 //
Chris@1357 2270 // 4. Paint to buffer from cache: if there are no non-cached areas
Chris@1357 2271 // or selections and the cache has not scrolled, then paint the
Chris@1357 2272 // union of the area of cache that has changed and the area
Chris@1357 2273 // that the paint event reported as exposed; otherwise paint
Chris@1357 2274 // the whole.
Chris@1357 2275 //
Chris@1357 2276 // 5. Paint the exposed area to the buffer from the cache plus all
Chris@1357 2277 // the layers that haven't been cached, plus selections etc.
Chris@1357 2278 //
Chris@1357 2279 // 6. Paint the exposed rect from the buffer.
Chris@1357 2280 //
Chris@1357 2281 // Note that all rects except the target for the final step are at
Chris@1357 2282 // cache (scaled, 2x as applicable) resolution.
Chris@1357 2283
Chris@1357 2284 int dpratio = effectiveDevicePixelRatio();
Chris@1357 2285
Chris@1357 2286 QRect requestedPaintArea(scaledRect(rect(), dpratio));
Chris@127 2287 if (e) {
Chris@1357 2288 // cut down to only the area actually exposed
Chris@1357 2289 requestedPaintArea &= scaledRect(e->rect(), dpratio);
Chris@127 2290 }
Chris@127 2291
Chris@127 2292 // If not all layers are scrollable, but some of the back layers
Chris@127 2293 // are, we should store only those in the cache.
Chris@127 2294
Chris@127 2295 bool layersChanged = false;
Chris@127 2296 LayerList scrollables = getScrollableBackLayers(true, layersChanged);
Chris@127 2297 LayerList nonScrollables = getNonScrollableFrontLayers(true, layersChanged);
Chris@127 2298
Chris@127 2299 #ifdef DEBUG_VIEW_WIDGET_PAINT
Chris@1506 2300 SVCERR << "View[" << getId() << "]::paintEvent: have " << scrollables.size()
Chris@1266 2301 << " scrollable back layers and " << nonScrollables.size()
Chris@1266 2302 << " non-scrollable front layers" << endl;
Chris@127 2303 #endif
Chris@127 2304
Chris@1357 2305 if (layersChanged || scrollables.empty()) {
Chris@1357 2306 m_cacheValid = false;
Chris@127 2307 }
Chris@127 2308
Chris@1357 2309 QRect wholeArea(scaledRect(rect(), dpratio));
Chris@1357 2310 QSize wholeSize(scaledSize(size(), dpratio));
Chris@1357 2311
Chris@1357 2312 if (!m_buffer || wholeSize != m_buffer->size()) {
Chris@952 2313 delete m_buffer;
Chris@1357 2314 m_buffer = new QPixmap(wholeSize);
Chris@952 2315 }
Chris@1357 2316
Chris@1357 2317 bool shouldUseCache = false;
Chris@1357 2318 bool shouldRepaintCache = false;
Chris@1357 2319 QRect cacheAreaToRepaint;
Chris@952 2320
Chris@1357 2321 static HitCount count("View cache");
Chris@1357 2322
Chris@127 2323 if (!scrollables.empty()) {
Chris@244 2324
Chris@1357 2325 shouldUseCache = true;
Chris@1357 2326 shouldRepaintCache = true;
Chris@1357 2327 cacheAreaToRepaint = wholeArea;
Chris@1357 2328
Chris@244 2329 #ifdef DEBUG_VIEW_WIDGET_PAINT
Chris@1506 2330 SVCERR << "View[" << getId() << "]: cache " << m_cache << ", cache zoom "
Chris@682 2331 << m_cacheZoomLevel << ", zoom " << m_zoomLevel << endl;
Chris@244 2332 #endif
Chris@244 2333
Chris@1327 2334 using namespace std::rel_ops;
Chris@1327 2335
Chris@1357 2336 if (!m_cacheValid ||
Chris@1357 2337 !m_cache ||
Chris@1266 2338 m_cacheZoomLevel != m_zoomLevel ||
Chris@1357 2339 m_cache->size() != wholeSize) {
Chris@1357 2340
Chris@1357 2341 // cache is not valid at all
Chris@1357 2342
Chris@1357 2343 if (requestedPaintArea.width() < wholeSize.width() / 10) {
Chris@1357 2344
Chris@1357 2345 m_cacheValid = false;
Chris@1357 2346 shouldUseCache = false;
Chris@1357 2347 shouldRepaintCache = false;
Chris@1357 2348
Chris@127 2349 #ifdef DEBUG_VIEW_WIDGET_PAINT
Chris@1506 2350 SVCERR << "View[" << getId() << "]::paintEvent: cache is invalid but only small area requested, will repaint directly instead" << endl;
Chris@127 2351 #endif
Chris@1266 2352 } else {
Chris@1357 2353
Chris@1357 2354 if (!m_cache ||
Chris@1357 2355 m_cache->size() != wholeSize) {
Chris@1357 2356 delete m_cache;
Chris@1357 2357 m_cache = new QPixmap(wholeSize);
Chris@1357 2358 }
Chris@1357 2359
Chris@127 2360 #ifdef DEBUG_VIEW_WIDGET_PAINT
Chris@1506 2361 SVCERR << "View[" << getId() << "]::paintEvent: cache is invalid, will repaint whole" << endl;
Chris@127 2362 #endif
Chris@1266 2363 }
Chris@1266 2364
Chris@1357 2365 count.miss();
Chris@1357 2366
Chris@1266 2367 } else if (m_cacheCentreFrame != m_centreFrame) {
Chris@1266 2368
Chris@1506 2369 #ifdef DEBUG_VIEW_WIDGET_PAINT
Chris@1506 2370 SVCERR << "View[" << getId() << "]::paintEvent: cache centre frame is " << m_cacheCentreFrame << endl;
Chris@1506 2371 #endif
Chris@1506 2372
Chris@1357 2373 int dx = dpratio * (getXForFrame(m_cacheCentreFrame) -
Chris@1357 2374 getXForFrame(m_centreFrame));
Chris@1357 2375
Chris@1357 2376 if (dx > -m_cache->width() && dx < m_cache->width()) {
Chris@1357 2377
Chris@1408 2378 m_cache->scroll(dx, 0, m_cache->rect(), nullptr);
Chris@1357 2379
Chris@1357 2380 if (dx < 0) {
Chris@1357 2381 cacheAreaToRepaint =
Chris@1357 2382 QRect(m_cache->width() + dx, 0, -dx, m_cache->height());
Chris@1357 2383 } else {
Chris@1357 2384 cacheAreaToRepaint =
Chris@1357 2385 QRect(0, 0, dx, m_cache->height());
Chris@1266 2386 }
Chris@1357 2387
Chris@1357 2388 count.partial();
Chris@1357 2389
Chris@127 2390 #ifdef DEBUG_VIEW_WIDGET_PAINT
Chris@1506 2391 SVCERR << "View[" << getId() << "]::paintEvent: scrolled cache by " << dx << endl;
Chris@127 2392 #endif
Chris@1266 2393 } else {
Chris@1357 2394 count.miss();
Chris@127 2395 #ifdef DEBUG_VIEW_WIDGET_PAINT
Chris@1506 2396 SVCERR << "View[" << getId() << "]::paintEvent: scrolling too far" << endl;
Chris@127 2397 #endif
Chris@1266 2398 }
Chris@1266 2399
Chris@1266 2400 } else {
Chris@127 2401 #ifdef DEBUG_VIEW_WIDGET_PAINT
Chris@1506 2402 SVCERR << "View[" << getId() << "]::paintEvent: cache is good" << endl;
Chris@127 2403 #endif
Chris@1357 2404 count.hit();
Chris@1357 2405 shouldRepaintCache = false;
Chris@1266 2406 }
Chris@1357 2407 }
Chris@1357 2408
Chris@1357 2409 #ifdef DEBUG_VIEW_WIDGET_PAINT
Chris@1506 2410 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 2411 #endif
Chris@1357 2412
Chris@1357 2413 if (shouldRepaintCache && !shouldUseCache) {
Chris@1357 2414 // If we are repainting the cache, then we paint the
Chris@1357 2415 // scrollables only to the cache, not to the buffer. So if
Chris@1357 2416 // shouldUseCache is also false, then the scrollables can't
Chris@1357 2417 // appear because they will only be on the cache
Chris@1357 2418 throw std::logic_error("ERROR: shouldRepaintCache is true, but shouldUseCache is false: this can't lead to the correct result");
Chris@1357 2419 }
Chris@1357 2420
Chris@1490 2421 // Create the ViewProxy for geometry provision, using the
Chris@1490 2422 // device-pixel ratio for pixel-doubled hi-dpi rendering as
Chris@1490 2423 // appropriate.
Chris@1490 2424
Chris@1490 2425 ViewProxy proxy(this, dpratio);
Chris@1490 2426
Chris@1490 2427 // Some layers may need an aligning proxy. If a layer's model has
Chris@1490 2428 // a source model that is the reference model for the aligning
Chris@1508 2429 // model, and the layer is tagged as to be aligned, then we might
Chris@1508 2430 // use an aligning proxy. Note this is actually made use of only
Chris@1508 2431 // if m_useAligningProxy is true further down.
Chris@1490 2432
Chris@1490 2433 ModelId alignmentModelId;
Chris@1490 2434 ModelId alignmentReferenceId;
Chris@1490 2435 auto aligningModel = ModelById::get(getAligningModel());
Chris@1490 2436 if (aligningModel) {
Chris@1490 2437 alignmentModelId = aligningModel->getAlignment();
Chris@1490 2438 alignmentReferenceId = aligningModel->getAlignmentReference();
Chris@1493 2439 #ifdef DEBUG_VIEW_WIDGET_PAINT
Chris@1492 2440 SVCERR << "alignmentModelId = " << alignmentModelId << " (reference = " << alignmentReferenceId << ")" << endl;
Chris@1493 2441 #endif
Chris@1490 2442 } else {
Chris@1493 2443 #ifdef DEBUG_VIEW_WIDGET_PAINT
Chris@1490 2444 SVCERR << "no aligningModel" << endl;
Chris@1493 2445 #endif
Chris@1490 2446 }
Chris@1490 2447 ViewProxy aligningProxy(this, dpratio, alignmentModelId);
Chris@1490 2448
Chris@1357 2449 // Scrollable (cacheable) items first. If we are repainting the
Chris@1357 2450 // cache, then we paint these to the cache; otherwise straight to
Chris@1357 2451 // the buffer.
Chris@1357 2452 QRect areaToPaint;
Chris@1357 2453 QPainter paint;
Chris@1357 2454
Chris@1357 2455 if (shouldRepaintCache) {
Chris@1357 2456 paint.begin(m_cache);
Chris@1357 2457 areaToPaint = cacheAreaToRepaint;
Chris@1357 2458 } else {
Chris@1357 2459 paint.begin(m_buffer);
Chris@1357 2460 areaToPaint = requestedPaintArea;
Chris@1357 2461 }
Chris@1357 2462
Chris@1357 2463 setPaintFont(paint);
Chris@1357 2464 paint.setClipRect(areaToPaint);
Chris@1357 2465
Chris@1357 2466 paint.setPen(getBackground());
Chris@1357 2467 paint.setBrush(getBackground());
Chris@1357 2468 paint.drawRect(areaToPaint);
Chris@1357 2469
Chris@1357 2470 paint.setPen(getForeground());
Chris@1357 2471 paint.setBrush(Qt::NoBrush);
Chris@1357 2472
Chris@1357 2473 for (LayerList::iterator i = scrollables.begin();
Chris@1357 2474 i != scrollables.end(); ++i) {
Chris@1357 2475
Chris@1357 2476 paint.setRenderHint(QPainter::Antialiasing, false);
Chris@1357 2477 paint.save();
Chris@1357 2478
Chris@1490 2479 Layer *layer = *i;
Chris@1490 2480
Chris@1490 2481 bool useAligningProxy = false;
Chris@1490 2482 if (m_useAligningProxy) {
Chris@1490 2483 if (layer->getModel() == alignmentReferenceId ||
Chris@1490 2484 layer->getSourceModel() == alignmentReferenceId) {
Chris@1490 2485 useAligningProxy = true;
Chris@1490 2486 }
Chris@1490 2487 }
Chris@1490 2488
Chris@1357 2489 #ifdef DEBUG_VIEW_WIDGET_PAINT
Chris@1506 2490 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 2491 #endif
Chris@1490 2492
Chris@1490 2493 layer->paint(useAligningProxy ? &aligningProxy : &proxy,
Chris@1490 2494 paint, areaToPaint);
Chris@1357 2495
Chris@1357 2496 paint.restore();
Chris@1357 2497 }
Chris@1357 2498
Chris@1357 2499 paint.end();
Chris@1357 2500
Chris@1357 2501 if (shouldRepaintCache) {
Chris@1357 2502 // and now we have
Chris@1357 2503 m_cacheValid = true;
Chris@1266 2504 m_cacheCentreFrame = m_centreFrame;
Chris@1266 2505 m_cacheZoomLevel = m_zoomLevel;
Chris@127 2506 }
Chris@127 2507
Chris@1357 2508 if (shouldUseCache) {
Chris@1357 2509 paint.begin(m_buffer);
Chris@1357 2510 paint.drawPixmap(requestedPaintArea, *m_cache, requestedPaintArea);
Chris@1266 2511 paint.end();
Chris@127 2512 }
Chris@127 2513
Chris@1357 2514 // Now non-cacheable items.
Chris@1357 2515
Chris@952 2516 paint.begin(m_buffer);
Chris@1357 2517 paint.setClipRect(requestedPaintArea);
Chris@339 2518 setPaintFont(paint);
Chris@127 2519 if (scrollables.empty()) {
Chris@287 2520 paint.setPen(getBackground());
Chris@287 2521 paint.setBrush(getBackground());
Chris@1357 2522 paint.drawRect(requestedPaintArea);
Chris@127 2523 }
Chris@1266 2524
Chris@287 2525 paint.setPen(getForeground());
Chris@127 2526 paint.setBrush(Qt::NoBrush);
Chris@1266 2527
Chris@1357 2528 for (LayerList::iterator i = nonScrollables.begin();
Chris@1357 2529 i != nonScrollables.end(); ++i) {
Chris@1490 2530
Chris@1490 2531 Layer *layer = *i;
Chris@1490 2532
Chris@1490 2533 bool useAligningProxy = false;
Chris@1490 2534 if (m_useAligningProxy) {
Chris@1490 2535 if (layer->getModel() == alignmentReferenceId ||
Chris@1490 2536 layer->getSourceModel() == alignmentReferenceId) {
Chris@1490 2537 useAligningProxy = true;
Chris@1490 2538 }
Chris@1490 2539 }
Chris@1490 2540
Chris@951 2541 #ifdef DEBUG_VIEW_WIDGET_PAINT
Chris@1506 2542 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 2543 #endif
Chris@1490 2544
Chris@1490 2545 layer->paint(useAligningProxy ? &aligningProxy : &proxy,
Chris@1490 2546 paint, requestedPaintArea);
Chris@127 2547 }
Chris@1266 2548
Chris@127 2549 paint.end();
Chris@1357 2550
Chris@1357 2551 // Now paint to widget from buffer: target rects from here on,
Chris@1357 2552 // unlike all the preceding, are at formal (1x) resolution
Chris@953 2553
Chris@953 2554 paint.begin(this);
Chris@339 2555 setPaintFont(paint);
Chris@953 2556 if (e) paint.setClipRect(e->rect());
Chris@1357 2557
Chris@1357 2558 QRect finalPaintRect = e ? e->rect() : rect();
Chris@1357 2559 paint.drawPixmap(finalPaintRect, *m_buffer,
Chris@1357 2560 scaledRect(finalPaintRect, dpratio));
Chris@1357 2561
Chris@1357 2562 drawSelections(paint);
Chris@1357 2563 drawPlayPointer(paint);
Chris@1357 2564
Chris@127 2565 paint.end();
Chris@127 2566
Chris@127 2567 QFrame::paintEvent(e);
Chris@127 2568 }
Chris@127 2569
Chris@127 2570 void
Chris@127 2571 View::drawSelections(QPainter &paint)
Chris@127 2572 {
Chris@217 2573 if (!hasTopLayerTimeXAxis()) return;
Chris@217 2574
Chris@127 2575 MultiSelection::SelectionList selections;
Chris@127 2576
Chris@127 2577 if (m_manager) {
Chris@1266 2578 selections = m_manager->getSelections();
Chris@1266 2579 if (m_manager->haveInProgressSelection()) {
Chris@1266 2580 bool exclusive;
Chris@1266 2581 Selection inProgressSelection =
Chris@1266 2582 m_manager->getInProgressSelection(exclusive);
Chris@1266 2583 if (exclusive) selections.clear();
Chris@1266 2584 selections.insert(inProgressSelection);
Chris@1266 2585 }
Chris@127 2586 }
Chris@127 2587
Chris@127 2588 paint.save();
Chris@183 2589
Chris@183 2590 bool translucent = !areLayerColoursSignificant();
Chris@183 2591
Chris@183 2592 if (translucent) {
Chris@183 2593 paint.setBrush(QColor(150, 150, 255, 80));
Chris@183 2594 } else {
Chris@183 2595 paint.setBrush(Qt::NoBrush);
Chris@183 2596 }
Chris@127 2597
Chris@908 2598 sv_samplerate_t sampleRate = getModelsSampleRate();
Chris@127 2599
Chris@127 2600 QPoint localPos;
Chris@908 2601 sv_frame_t illuminateFrame = -1;
Chris@127 2602 bool closeToLeft, closeToRight;
Chris@127 2603
Chris@127 2604 if (shouldIlluminateLocalSelection(localPos, closeToLeft, closeToRight)) {
Chris@1266 2605 illuminateFrame = getFrameForX(localPos.x());
Chris@127 2606 }
Chris@127 2607
Chris@127 2608 const QFontMetrics &metrics = paint.fontMetrics();
Chris@127 2609
Chris@127 2610 for (MultiSelection::SelectionList::iterator i = selections.begin();
Chris@1266 2611 i != selections.end(); ++i) {
Chris@1266 2612
Chris@1266 2613 int p0 = getXForFrame(alignFromReference(i->getStartFrame()));
Chris@1266 2614 int p1 = getXForFrame(alignFromReference(i->getEndFrame()));
Chris@1266 2615
Chris@1266 2616 if (p1 < 0 || p0 > width()) continue;
Chris@127 2617
Chris@127 2618 #ifdef DEBUG_VIEW_WIDGET_PAINT
Chris@1266 2619 SVDEBUG << "View::drawSelections: " << p0 << ",-1 [" << (p1-p0) << "x" << (height()+1) << "]" << endl;
Chris@127 2620 #endif
Chris@127 2621
Chris@1266 2622 bool illuminateThis =
Chris@1266 2623 (illuminateFrame >= 0 && i->contains(illuminateFrame));
Chris@1266 2624
Chris@1270 2625 double h = height();
Chris@1401 2626 double penWidth = scalePenWidth(1.0);
Chris@1270 2627 double half = penWidth/2.0;
Chris@1270 2628
Chris@1270 2629 paint.setPen(QPen(QColor(150, 150, 255), penWidth));
Chris@183 2630
Chris@183 2631 if (translucent && shouldLabelSelections()) {
Chris@1270 2632 paint.drawRect(QRectF(p0, -penWidth, p1 - p0, h + 2*penWidth));
Chris@183 2633 } else {
Chris@183 2634 // Make the top & bottom lines of the box visible if we
Chris@183 2635 // are lacking some of the other visual cues. There's no
Chris@183 2636 // particular logic to this, it's just a question of what
Chris@183 2637 // I happen to think looks nice.
Chris@1270 2638 paint.drawRect(QRectF(p0, half, p1 - p0, h - penWidth));
Chris@183 2639 }
Chris@127 2640
Chris@1266 2641 if (illuminateThis) {
Chris@1266 2642 paint.save();
Chris@1401 2643 penWidth = scalePenWidth(2.0);
Chris@1270 2644 half = penWidth/2.0;
Chris@1270 2645 paint.setPen(QPen(getForeground(), penWidth));
Chris@1266 2646 if (closeToLeft) {
Chris@1270 2647 paint.drawLine(QLineF(p0, half, p1, half));
Chris@1270 2648 paint.drawLine(QLineF(p0, half, p0, h - half));
Chris@1270 2649 paint.drawLine(QLineF(p0, h - half, p1, h - half));
Chris@1266 2650 } else if (closeToRight) {
Chris@1270 2651 paint.drawLine(QLineF(p0, half, p1, half));
Chris@1270 2652 paint.drawLine(QLineF(p1, half, p1, h - half));
Chris@1270 2653 paint.drawLine(QLineF(p0, h - half, p1, h - half));
Chris@1266 2654 } else {
Chris@1266 2655 paint.setBrush(Qt::NoBrush);
Chris@1270 2656 paint.drawRect(QRectF(p0, half, p1 - p0, h - penWidth));
Chris@1266 2657 }
Chris@1266 2658 paint.restore();
Chris@1266 2659 }
Chris@1266 2660
Chris@1266 2661 if (sampleRate && shouldLabelSelections() && m_manager &&
Chris@189 2662 m_manager->shouldShowSelectionExtents()) {
Chris@1266 2663
Chris@1266 2664 QString startText = QString("%1 / %2")
Chris@1266 2665 .arg(QString::fromStdString
Chris@1266 2666 (RealTime::frame2RealTime
Chris@1266 2667 (i->getStartFrame(), sampleRate).toText(true)))
Chris@1266 2668 .arg(i->getStartFrame());
Chris@1266 2669
Chris@1266 2670 QString endText = QString(" %1 / %2")
Chris@1266 2671 .arg(QString::fromStdString
Chris@1266 2672 (RealTime::frame2RealTime
Chris@1266 2673 (i->getEndFrame(), sampleRate).toText(true)))
Chris@1266 2674 .arg(i->getEndFrame());
Chris@1266 2675
Chris@1266 2676 QString durationText = QString("(%1 / %2) ")
Chris@1266 2677 .arg(QString::fromStdString
Chris@1266 2678 (RealTime::frame2RealTime
Chris@1266 2679 (i->getEndFrame() - i->getStartFrame(), sampleRate)
Chris@1266 2680 .toText(true)))
Chris@1266 2681 .arg(i->getEndFrame() - i->getStartFrame());
Chris@1476 2682
Chris@1476 2683 // Qt 5.13 deprecates QFontMetrics::width(), but its suggested
Chris@1476 2684 // replacement (horizontalAdvance) was only added in Qt 5.11
Chris@1476 2685 // which is too new for us
Chris@1476 2686 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
Chris@1266 2687
Chris@1266 2688 int sw = metrics.width(startText),
Chris@1266 2689 ew = metrics.width(endText),
Chris@1266 2690 dw = metrics.width(durationText);
Chris@1266 2691
Chris@1266 2692 int sy = metrics.ascent() + metrics.height() + 4;
Chris@1266 2693 int ey = sy;
Chris@1266 2694 int dy = sy + metrics.height();
Chris@1266 2695
Chris@1266 2696 int sx = p0 + 2;
Chris@1266 2697 int ex = sx;
Chris@1266 2698 int dx = sx;
Chris@127 2699
Chris@501 2700 bool durationBothEnds = true;
Chris@501 2701
Chris@1266 2702 if (sw + ew > (p1 - p0)) {
Chris@1266 2703 ey += metrics.height();
Chris@1266 2704 dy += metrics.height();
Chris@501 2705 durationBothEnds = false;
Chris@1266 2706 }
Chris@1266 2707
Chris@1266 2708 if (ew < (p1 - p0)) {
Chris@1266 2709 ex = p1 - 2 - ew;
Chris@1266 2710 }
Chris@1266 2711
Chris@1266 2712 if (dw < (p1 - p0)) {
Chris@1266 2713 dx = p1 - 2 - dw;
Chris@1266 2714 }
Chris@127 2715
Chris@1193 2716 PaintAssistant::drawVisibleText(this, paint, sx, sy, startText,
Chris@1193 2717 PaintAssistant::OutlinedText);
Chris@1193 2718 PaintAssistant::drawVisibleText(this, paint, ex, ey, endText,
Chris@1193 2719 PaintAssistant::OutlinedText);
Chris@1193 2720 PaintAssistant::drawVisibleText(this, paint, dx, dy, durationText,
Chris@1193 2721 PaintAssistant::OutlinedText);
Chris@501 2722 if (durationBothEnds) {
Chris@1193 2723 PaintAssistant::drawVisibleText(this, paint, sx, dy, durationText,
Chris@1193 2724 PaintAssistant::OutlinedText);
Chris@501 2725 }
Chris@1266 2726 }
Chris@127 2727 }
Chris@127 2728
Chris@127 2729 paint.restore();
Chris@127 2730 }
Chris@127 2731
Chris@267 2732 void
Chris@1357 2733 View::drawPlayPointer(QPainter &paint)
Chris@1357 2734 {
Chris@1357 2735 bool showPlayPointer = true;
Chris@1357 2736
Chris@1357 2737 if (m_followPlay == PlaybackScrollContinuous) {
Chris@1357 2738 showPlayPointer = false;
Chris@1357 2739 } else if (m_playPointerFrame <= getStartFrame() ||
Chris@1357 2740 m_playPointerFrame >= getEndFrame()) {
Chris@1357 2741 showPlayPointer = false;
Chris@1357 2742 } else if (m_manager && !m_manager->isPlaying()) {
Chris@1357 2743 if (m_playPointerFrame == getCentreFrame() &&
Chris@1357 2744 m_manager->shouldShowCentreLine() &&
Chris@1357 2745 m_followPlay != PlaybackIgnore) {
Chris@1357 2746 // Don't show the play pointer when it is redundant with
Chris@1357 2747 // the centre line
Chris@1357 2748 showPlayPointer = false;
Chris@1357 2749 }
Chris@1357 2750 }
Chris@1357 2751
Chris@1357 2752 if (showPlayPointer) {
Chris@1357 2753
Chris@1357 2754 int playx = getXForFrame(m_playPointerFrame);
Chris@1357 2755
Chris@1357 2756 paint.setPen(getForeground());
Chris@1357 2757 paint.drawLine(playx - 1, 0, playx - 1, height() - 1);
Chris@1357 2758 paint.drawLine(playx + 1, 0, playx + 1, height() - 1);
Chris@1357 2759 paint.drawPoint(playx, 0);
Chris@1357 2760 paint.drawPoint(playx, height() - 1);
Chris@1357 2761 paint.setPen(getBackground());
Chris@1357 2762 paint.drawLine(playx, 1, playx, height() - 2);
Chris@1357 2763 }
Chris@1357 2764 }
Chris@1357 2765
Chris@1357 2766 void
Chris@270 2767 View::drawMeasurementRect(QPainter &paint, const Layer *topLayer, QRect r,
Chris@270 2768 bool focus) const
Chris@267 2769 {
Chris@587 2770 // SVDEBUG << "View::drawMeasurementRect(" << r.x() << "," << r.y() << " "
Chris@585 2771 // << r.width() << "x" << r.height() << ")" << endl;
Chris@268 2772
Chris@267 2773 if (r.x() + r.width() < 0 || r.x() >= width()) return;
Chris@267 2774
Chris@270 2775 if (r.width() != 0 || r.height() != 0) {
Chris@270 2776 paint.save();
Chris@270 2777 if (focus) {
Chris@270 2778 paint.setPen(Qt::NoPen);
Chris@272 2779 QColor brushColour(Qt::black);
Chris@272 2780 brushColour.setAlpha(hasLightBackground() ? 15 : 40);
Chris@270 2781 paint.setBrush(brushColour);
Chris@270 2782 if (r.x() > 0) {
Chris@270 2783 paint.drawRect(0, 0, r.x(), height());
Chris@270 2784 }
Chris@270 2785 if (r.x() + r.width() < width()) {
Chris@270 2786 paint.drawRect(r.x() + r.width(), 0, width()-r.x()-r.width(), height());
Chris@270 2787 }
Chris@270 2788 if (r.y() > 0) {
Chris@270 2789 paint.drawRect(r.x(), 0, r.width(), r.y());
Chris@270 2790 }
Chris@270 2791 if (r.y() + r.height() < height()) {
Chris@270 2792 paint.drawRect(r.x(), r.y() + r.height(), r.width(), height()-r.y()-r.height());
Chris@270 2793 }
Chris@270 2794 paint.setBrush(Qt::NoBrush);
Chris@270 2795 }
Chris@270 2796 paint.setPen(Qt::green);
Chris@270 2797 paint.drawRect(r);
Chris@270 2798 paint.restore();
Chris@270 2799 } else {
Chris@270 2800 paint.save();
Chris@270 2801 paint.setPen(Qt::green);
Chris@270 2802 paint.drawPoint(r.x(), r.y());
Chris@270 2803 paint.restore();
Chris@270 2804 }
Chris@270 2805
Chris@270 2806 if (!focus) return;
Chris@270 2807
Chris@278 2808 paint.save();
Chris@278 2809 QFont fn = paint.font();
Chris@278 2810 if (fn.pointSize() > 8) {
Chris@278 2811 fn.setPointSize(fn.pointSize() - 1);
Chris@278 2812 paint.setFont(fn);
Chris@278 2813 }
Chris@278 2814
Chris@267 2815 int fontHeight = paint.fontMetrics().height();
Chris@267 2816 int fontAscent = paint.fontMetrics().ascent();
Chris@267 2817
Chris@904 2818 double v0, v1;
Chris@267 2819 QString u0, u1;
Chris@267 2820 bool b0 = false, b1 = false;
Chris@267 2821
Chris@267 2822 QString axs, ays, bxs, bys, dxs, dys;
Chris@267 2823
Chris@267 2824 int axx, axy, bxx, bxy, dxx, dxy;
Chris@267 2825 int aw = 0, bw = 0, dw = 0;
Chris@267 2826
Chris@267 2827 int labelCount = 0;
Chris@267 2828
Chris@362 2829 // top-left point, x-coord
Chris@362 2830
Chris@267 2831 if ((b0 = topLayer->getXScaleValue(this, r.x(), v0, u0))) {
Chris@267 2832 axs = QString("%1 %2").arg(v0).arg(u0);
Chris@278 2833 if (u0 == "Hz" && Pitch::isFrequencyInMidiRange(v0)) {
Chris@278 2834 axs = QString("%1 (%2)").arg(axs)
Chris@278 2835 .arg(Pitch::getPitchLabelForFrequency(v0));
Chris@278 2836 }
Chris@267 2837 aw = paint.fontMetrics().width(axs);
Chris@267 2838 ++labelCount;
Chris@267 2839 }
Chris@362 2840
Chris@362 2841 // bottom-right point, x-coord
Chris@267 2842
Chris@267 2843 if (r.width() > 0) {
Chris@267 2844 if ((b1 = topLayer->getXScaleValue(this, r.x() + r.width(), v1, u1))) {
Chris@267 2845 bxs = QString("%1 %2").arg(v1).arg(u1);
Chris@278 2846 if (u1 == "Hz" && Pitch::isFrequencyInMidiRange(v1)) {
Chris@278 2847 bxs = QString("%1 (%2)").arg(bxs)
Chris@278 2848 .arg(Pitch::getPitchLabelForFrequency(v1));
Chris@278 2849 }
Chris@267 2850 bw = paint.fontMetrics().width(bxs);
Chris@267 2851 }
Chris@267 2852 }
Chris@362 2853
Chris@362 2854 // dimension, width
Chris@267 2855
Chris@283 2856 if (b0 && b1 && v1 != v0 && u0 == u1) {
Chris@362 2857 dxs = QString("[%1 %2]").arg(fabs(v1 - v0)).arg(u1);
Chris@267 2858 dw = paint.fontMetrics().width(dxs);
Chris@267 2859 }
Chris@267 2860
Chris@267 2861 b0 = false;
Chris@267 2862 b1 = false;
Chris@267 2863
Chris@362 2864 // top-left point, y-coord
Chris@362 2865
Chris@267 2866 if ((b0 = topLayer->getYScaleValue(this, r.y(), v0, u0))) {
Chris@267 2867 ays = QString("%1 %2").arg(v0).arg(u0);
Chris@278 2868 if (u0 == "Hz" && Pitch::isFrequencyInMidiRange(v0)) {
Chris@278 2869 ays = QString("%1 (%2)").arg(ays)
Chris@278 2870 .arg(Pitch::getPitchLabelForFrequency(v0));
Chris@278 2871 }
Chris@267 2872 aw = std::max(aw, paint.fontMetrics().width(ays));
Chris@267 2873 ++labelCount;
Chris@267 2874 }
Chris@267 2875
Chris@362 2876 // bottom-right point, y-coord
Chris@362 2877
Chris@267 2878 if (r.height() > 0) {
Chris@267 2879 if ((b1 = topLayer->getYScaleValue(this, r.y() + r.height(), v1, u1))) {
Chris@267 2880 bys = QString("%1 %2").arg(v1).arg(u1);
Chris@278 2881 if (u1 == "Hz" && Pitch::isFrequencyInMidiRange(v1)) {
Chris@278 2882 bys = QString("%1 (%2)").arg(bys)
Chris@278 2883 .arg(Pitch::getPitchLabelForFrequency(v1));
Chris@278 2884 }
Chris@267 2885 bw = std::max(bw, paint.fontMetrics().width(bys));
Chris@267 2886 }
Chris@267 2887 }
Chris@274 2888
Chris@274 2889 bool bd = false;
Chris@904 2890 double dy = 0.f;
Chris@274 2891 QString du;
Chris@274 2892
Chris@362 2893 // dimension, height
Chris@362 2894
Chris@274 2895 if ((bd = topLayer->getYScaleDifference(this, r.y(), r.y() + r.height(),
Chris@283 2896 dy, du)) &&
Chris@283 2897 dy != 0) {
Chris@274 2898 if (du != "") {
Chris@362 2899 if (du == "Hz") {
Chris@362 2900 int semis;
Chris@904 2901 double cents;
Chris@362 2902 semis = Pitch::getPitchForFrequencyDifference(v0, v1, &cents);
Chris@362 2903 dys = QString("[%1 %2 (%3)]")
Chris@362 2904 .arg(dy).arg(du)
Chris@362 2905 .arg(Pitch::getLabelForPitchRange(semis, cents));
Chris@362 2906 } else {
Chris@362 2907 dys = QString("[%1 %2]").arg(dy).arg(du);
Chris@362 2908 }
Chris@274 2909 } else {
Chris@362 2910 dys = QString("[%1]").arg(dy);
Chris@274 2911 }
Chris@267 2912 dw = std::max(dw, paint.fontMetrics().width(dys));
Chris@267 2913 }
Chris@267 2914
Chris@267 2915 int mw = r.width();
Chris@267 2916 int mh = r.height();
Chris@267 2917
Chris@267 2918 bool edgeLabelsInside = false;
Chris@267 2919 bool sizeLabelsInside = false;
Chris@267 2920
Chris@267 2921 if (mw < std::max(aw, std::max(bw, dw)) + 4) {
Chris@267 2922 // defaults stand
Chris@267 2923 } else if (mw < aw + bw + 4) {
Chris@267 2924 if (mh > fontHeight * labelCount * 3 + 4) {
Chris@267 2925 edgeLabelsInside = true;
Chris@267 2926 sizeLabelsInside = true;
Chris@267 2927 } else if (mh > fontHeight * labelCount * 2 + 4) {
Chris@267 2928 edgeLabelsInside = true;
Chris@267 2929 }
Chris@267 2930 } else if (mw < aw + bw + dw + 4) {
Chris@267 2931 if (mh > fontHeight * labelCount * 3 + 4) {
Chris@267 2932 edgeLabelsInside = true;
Chris@267 2933 sizeLabelsInside = true;
Chris@267 2934 } else if (mh > fontHeight * labelCount + 4) {
Chris@267 2935 edgeLabelsInside = true;
Chris@267 2936 }
Chris@267 2937 } else {
Chris@267 2938 if (mh > fontHeight * labelCount + 4) {
Chris@267 2939 edgeLabelsInside = true;
Chris@267 2940 sizeLabelsInside = true;
Chris@267 2941 }
Chris@267 2942 }
Chris@267 2943
Chris@267 2944 if (edgeLabelsInside) {
Chris@267 2945
Chris@267 2946 axx = r.x() + 2;
Chris@267 2947 axy = r.y() + fontAscent + 2;
Chris@267 2948
Chris@267 2949 bxx = r.x() + r.width() - bw - 2;
Chris@267 2950 bxy = r.y() + r.height() - (labelCount-1) * fontHeight - 2;
Chris@267 2951
Chris@267 2952 } else {
Chris@267 2953
Chris@267 2954 axx = r.x() - aw - 2;
Chris@267 2955 axy = r.y() + fontAscent;
Chris@267 2956
Chris@267 2957 bxx = r.x() + r.width() + 2;
Chris@267 2958 bxy = r.y() + r.height() - (labelCount-1) * fontHeight;
Chris@267 2959 }
Chris@267 2960
Chris@267 2961 dxx = r.width()/2 + r.x() - dw/2;
Chris@267 2962
Chris@267 2963 if (sizeLabelsInside) {
Chris@267 2964
Chris@267 2965 dxy = r.height()/2 + r.y() - (labelCount * fontHeight)/2 + fontAscent;
Chris@267 2966
Chris@267 2967 } else {
Chris@267 2968
Chris@267 2969 dxy = r.y() + r.height() + fontAscent + 2;
Chris@267 2970 }
Chris@267 2971
Chris@267 2972 if (axs != "") {
Chris@1078 2973 PaintAssistant::drawVisibleText(this, paint, axx, axy, axs, PaintAssistant::OutlinedText);
Chris@267 2974 axy += fontHeight;
Chris@267 2975 }
Chris@267 2976
Chris@267 2977 if (ays != "") {
Chris@1078 2978 PaintAssistant::drawVisibleText(this, paint, axx, axy, ays, PaintAssistant::OutlinedText);
Chris@267 2979 axy += fontHeight;
Chris@267 2980 }
Chris@267 2981
Chris@267 2982 if (bxs != "") {
Chris@1078 2983 PaintAssistant::drawVisibleText(this, paint, bxx, bxy, bxs, PaintAssistant::OutlinedText);
Chris@267 2984 bxy += fontHeight;
Chris@267 2985 }
Chris@267 2986
Chris@267 2987 if (bys != "") {
Chris@1078 2988 PaintAssistant::drawVisibleText(this, paint, bxx, bxy, bys, PaintAssistant::OutlinedText);
Chris@267 2989 bxy += fontHeight;
Chris@267 2990 }
Chris@267 2991
Chris@267 2992 if (dxs != "") {
Chris@1078 2993 PaintAssistant::drawVisibleText(this, paint, dxx, dxy, dxs, PaintAssistant::OutlinedText);
Chris@267 2994 dxy += fontHeight;
Chris@267 2995 }
Chris@267 2996
Chris@267 2997 if (dys != "") {
Chris@1078 2998 PaintAssistant::drawVisibleText(this, paint, dxx, dxy, dys, PaintAssistant::OutlinedText);
Chris@267 2999 dxy += fontHeight;
Chris@267 3000 }
Chris@278 3001
Chris@278 3002 paint.restore();
Chris@267 3003 }
Chris@267 3004
Chris@227 3005 bool
Chris@1605 3006 View::waitForLayersToBeReady()
Chris@227 3007 {
Chris@227 3008 bool someLayersIncomplete = false;
Chris@227 3009
Chris@1603 3010 #ifdef DEBUG_VIEW
Chris@1605 3011 SVDEBUG << "View::waitForLayersToBeReady: checking completion" << endl;
Chris@1603 3012 #endif
Chris@1603 3013
Chris@835 3014 for (LayerList::iterator i = m_layerStack.begin();
Chris@835 3015 i != m_layerStack.end(); ++i) {
Chris@227 3016
Chris@227 3017 int c = (*i)->getCompletion(this);
Chris@1603 3018
Chris@1603 3019 #ifdef DEBUG_VIEW
Chris@1603 3020 SVDEBUG << "layer " << (*i)->getLayerPresentationName() << " says "
Chris@1603 3021 << c << endl;
Chris@1603 3022 #endif
Chris@1603 3023
Chris@227 3024 if (c < 100) {
Chris@227 3025 someLayersIncomplete = true;
Chris@227 3026 break;
Chris@227 3027 }
Chris@227 3028 }
Chris@227 3029
Chris@227 3030 if (someLayersIncomplete) {
Chris@227 3031
Chris@227 3032 QProgressDialog progress(tr("Waiting for layers to be ready..."),
Chris@227 3033 tr("Cancel"), 0, 100, this);
Chris@227 3034
Chris@227 3035 int layerCompletion = 0;
Chris@227 3036
Chris@227 3037 while (layerCompletion < 100) {
Chris@227 3038
Chris@1603 3039 #ifdef DEBUG_VIEW
Chris@1603 3040 SVDEBUG << "View::render: checking completion (again)" << endl;
Chris@1603 3041 #endif
Chris@1603 3042
Chris@835 3043 for (LayerList::iterator i = m_layerStack.begin();
Chris@835 3044 i != m_layerStack.end(); ++i) {
Chris@227 3045
Chris@227 3046 int c = (*i)->getCompletion(this);
Chris@835 3047 if (i == m_layerStack.begin() || c < layerCompletion) {
Chris@227 3048 layerCompletion = c;
Chris@227 3049 }
Chris@1603 3050
Chris@1603 3051 #ifdef DEBUG_VIEW
Chris@1603 3052 SVDEBUG << "layer " << (*i)->getLayerPresentationName() << " says "
Chris@1603 3053 << c << ", layerCompletion now " << layerCompletion << endl;
Chris@1603 3054 #endif
Chris@227 3055 }
Chris@227 3056
Chris@227 3057 if (layerCompletion >= 100) break;
Chris@227 3058
Chris@227 3059 progress.setValue(layerCompletion);
Chris@227 3060 qApp->processEvents();
Chris@227 3061 if (progress.wasCanceled()) {
Chris@227 3062 update();
Chris@227 3063 return false;
Chris@227 3064 }
Chris@227 3065
Chris@227 3066 usleep(50000);
Chris@227 3067 }
Chris@227 3068 }
Chris@227 3069
Chris@1603 3070 #ifdef DEBUG_VIEW
Chris@1605 3071 SVDEBUG << "View::waitForLayersToBeReady: ok, we're ready" << endl;
Chris@1603 3072 #endif
Chris@1605 3073
Chris@1605 3074 return true;
Chris@1605 3075 }
Chris@1605 3076
Chris@1605 3077 bool
Chris@1605 3078 View::render(QPainter &paint, int xorigin, sv_frame_t f0, sv_frame_t f1)
Chris@1605 3079 {
Chris@1605 3080 int x0 = int(round(m_zoomLevel.framesToPixels(double(f0))));
Chris@1605 3081 int x1 = int(round(m_zoomLevel.framesToPixels(double(f1))));
Chris@1605 3082
Chris@1605 3083 int w = x1 - x0;
Chris@1605 3084
Chris@1605 3085 #ifdef DEBUG_VIEW
Chris@1605 3086 SVDEBUG << "View::render: Render request is for frames " << f0
Chris@1605 3087 << " to " << f1 << " (pixels " << x0 << " to " << x1
Chris@1605 3088 << ", width " << w << ")" << endl;
Chris@1605 3089 #endif
Chris@1605 3090
Chris@1605 3091 if (!waitForLayersToBeReady()) {
Chris@1605 3092 return false;
Chris@1605 3093 }
Chris@1605 3094
Chris@1605 3095 sv_frame_t origCentreFrame = m_centreFrame;
Chris@1603 3096
Chris@227 3097 QProgressDialog progress(tr("Rendering image..."),
Chris@227 3098 tr("Cancel"), 0, w / width(), this);
Chris@227 3099
Chris@806 3100 for (int x = 0; x < w; x += width()) {
Chris@227 3101
Chris@227 3102 progress.setValue(x / width());
Chris@227 3103 qApp->processEvents();
Chris@227 3104 if (progress.wasCanceled()) {
Chris@227 3105 m_centreFrame = origCentreFrame;
Chris@227 3106 update();
Chris@227 3107 return false;
Chris@227 3108 }
Chris@227 3109
Chris@1327 3110 m_centreFrame = f0 + sv_frame_t(round(m_zoomLevel.pixelsToFrames
Chris@1327 3111 (x + width()/2)));
Chris@227 3112
Chris@227 3113 QRect chunk(0, 0, width(), height());
Chris@227 3114
Chris@287 3115 paint.setPen(getBackground());
Chris@287 3116 paint.setBrush(getBackground());
Chris@227 3117
Chris@1266 3118 paint.drawRect(QRect(xorigin + x, 0, width(), height()));
Chris@1266 3119
Chris@1266 3120 paint.setPen(getForeground());
Chris@1266 3121 paint.setBrush(Qt::NoBrush);
Chris@1266 3122
Chris@1266 3123 for (LayerList::iterator i = m_layerStack.begin();
Chris@835 3124 i != m_layerStack.end(); ++i) {
Chris@919 3125 if (!((*i)->isLayerDormant(this))){
Chris@919 3126
Chris@919 3127 paint.setRenderHint(QPainter::Antialiasing, false);
Chris@919 3128
Chris@919 3129 paint.save();
Chris@919 3130 paint.translate(xorigin + x, 0);
Chris@919 3131
Chris@1506 3132 SVCERR << "Centre frame now: " << m_centreFrame << " drawing to " << chunk.x() + x + xorigin << ", " << chunk.width() << endl;
Chris@919 3133
Chris@919 3134 (*i)->setSynchronousPainting(true);
Chris@919 3135
Chris@919 3136 (*i)->paint(this, paint, chunk);
Chris@919 3137
Chris@919 3138 (*i)->setSynchronousPainting(false);
Chris@919 3139
Chris@919 3140 paint.restore();
Chris@919 3141 }
Chris@1266 3142 }
Chris@227 3143 }
Chris@227 3144
Chris@227 3145 m_centreFrame = origCentreFrame;
Chris@227 3146 update();
Chris@227 3147 return true;
Chris@227 3148 }
Chris@227 3149
Chris@227 3150 QImage *
Chris@1202 3151 View::renderToNewImage()
Chris@227 3152 {
Chris@1605 3153 if (!waitForLayersToBeReady()) {
Chris@1605 3154 return nullptr;
Chris@1605 3155 }
Chris@1605 3156
Chris@908 3157 sv_frame_t f0 = getModelsStartFrame();
Chris@908 3158 sv_frame_t f1 = getModelsEndFrame();
Chris@227 3159
Chris@1202 3160 return renderPartToNewImage(f0, f1);
Chris@229 3161 }
Chris@229 3162
Chris@229 3163 QImage *
Chris@1202 3164 View::renderPartToNewImage(sv_frame_t f0, sv_frame_t f1)
Chris@229 3165 {
Chris@1327 3166 int x0 = int(round(getZoomLevel().framesToPixels(double(f0))));
Chris@1327 3167 int x1 = int(round(getZoomLevel().framesToPixels(double(f1))));
Chris@227 3168
Chris@227 3169 QImage *image = new QImage(x1 - x0, height(), QImage::Format_RGB32);
Chris@227 3170
Chris@227 3171 QPainter *paint = new QPainter(image);
Chris@229 3172 if (!render(*paint, 0, f0, f1)) {
Chris@227 3173 delete paint;
Chris@227 3174 delete image;
Chris@1408 3175 return nullptr;
Chris@227 3176 } else {
Chris@227 3177 delete paint;
Chris@227 3178 return image;
Chris@227 3179 }
Chris@227 3180 }
Chris@227 3181
Chris@229 3182 QSize
Chris@1202 3183 View::getRenderedImageSize()
Chris@229 3184 {
Chris@908 3185 sv_frame_t f0 = getModelsStartFrame();
Chris@908 3186 sv_frame_t f1 = getModelsEndFrame();
Chris@229 3187
Chris@1202 3188 return getRenderedPartImageSize(f0, f1);
Chris@229 3189 }
Chris@229 3190
Chris@229 3191 QSize
Chris@1202 3192 View::getRenderedPartImageSize(sv_frame_t f0, sv_frame_t f1)
Chris@229 3193 {
Chris@1327 3194 int x0 = int(round(getZoomLevel().framesToPixels(double(f0))));
Chris@1327 3195 int x1 = int(round(getZoomLevel().framesToPixels(double(f1))));
Chris@229 3196
Chris@229 3197 return QSize(x1 - x0, height());
Chris@229 3198 }
Chris@229 3199
Chris@1202 3200 bool
Chris@1202 3201 View::renderToSvgFile(QString filename)
Chris@1202 3202 {
Chris@1202 3203 sv_frame_t f0 = getModelsStartFrame();
Chris@1202 3204 sv_frame_t f1 = getModelsEndFrame();
Chris@1202 3205
Chris@1202 3206 return renderPartToSvgFile(filename, f0, f1);
Chris@1202 3207 }
Chris@1202 3208
Chris@1202 3209 bool
Chris@1202 3210 View::renderPartToSvgFile(QString filename, sv_frame_t f0, sv_frame_t f1)
Chris@1202 3211 {
Chris@1327 3212 int x0 = int(round(getZoomLevel().framesToPixels(double(f0))));
Chris@1327 3213 int x1 = int(round(getZoomLevel().framesToPixels(double(f1))));
Chris@1202 3214
Chris@1202 3215 QSvgGenerator generator;
Chris@1202 3216 generator.setFileName(filename);
Chris@1202 3217 generator.setSize(QSize(x1 - x0, height()));
Chris@1202 3218 generator.setViewBox(QRect(0, 0, x1 - x0, height()));
Chris@1202 3219 generator.setTitle(tr("Exported image from %1")
Chris@1202 3220 .arg(QApplication::applicationName()));
Chris@1202 3221
Chris@1202 3222 QPainter paint;
Chris@1202 3223 paint.begin(&generator);
Chris@1202 3224 bool result = render(paint, 0, f0, f1);
Chris@1202 3225 paint.end();
Chris@1202 3226 return result;
Chris@1202 3227 }
Chris@1202 3228
Chris@316 3229 void
Chris@316 3230 View::toXml(QTextStream &stream,
Chris@316 3231 QString indent, QString extraAttributes) const
Chris@127 3232 {
Chris@316 3233 stream << indent;
Chris@127 3234
Chris@1327 3235 int classicZoomValue, deepZoomValue;
Chris@1327 3236
Chris@1327 3237 if (m_zoomLevel.zone == ZoomLevel::FramesPerPixel) {
Chris@1327 3238 classicZoomValue = m_zoomLevel.level;
Chris@1327 3239 deepZoomValue = 1;
Chris@1327 3240 } else {
Chris@1327 3241 classicZoomValue = 1;
Chris@1327 3242 deepZoomValue = m_zoomLevel.level;
Chris@1327 3243 }
Chris@1327 3244
Chris@316 3245 stream << QString("<view "
Chris@316 3246 "centre=\"%1\" "
Chris@316 3247 "zoom=\"%2\" "
Chris@1327 3248 "deepZoom=\"%3\" "
Chris@1327 3249 "followPan=\"%4\" "
Chris@1327 3250 "followZoom=\"%5\" "
Chris@1327 3251 "tracking=\"%6\" "
Chris@1331 3252 " %7>\n")
Chris@1266 3253 .arg(m_centreFrame)
Chris@1327 3254 .arg(classicZoomValue)
Chris@1327 3255 .arg(deepZoomValue)
Chris@1266 3256 .arg(m_followPan)
Chris@1266 3257 .arg(m_followZoom)
Chris@1266 3258 .arg(m_followPlay == PlaybackScrollContinuous ? "scroll" :
Chris@1266 3259 m_followPlay == PlaybackScrollPageWithCentre ? "page" :
Chris@1266 3260 m_followPlay == PlaybackScrollPage ? "daw" :
Chris@815 3261 "ignore")
Chris@1266 3262 .arg(extraAttributes);
Chris@127 3263
Chris@838 3264 for (int i = 0; i < (int)m_fixedOrderLayers.size(); ++i) {
Chris@838 3265 bool visible = !m_fixedOrderLayers[i]->isLayerDormant(this);
Chris@838 3266 m_fixedOrderLayers[i]->toBriefXml(stream, indent + " ",
Chris@838 3267 QString("visible=\"%1\"")
Chris@838 3268 .arg(visible ? "true" : "false"));
Chris@127 3269 }
Chris@127 3270
Chris@316 3271 stream << indent + "</view>\n";
Chris@127 3272 }
Chris@127 3273
Chris@127 3274 ViewPropertyContainer::ViewPropertyContainer(View *v) :
Chris@127 3275 m_v(v)
Chris@127 3276 {
Chris@1506 3277 // SVCERR << "ViewPropertyContainer: " << getId() << " is owned by View " << v << endl;
Chris@127 3278 connect(m_v, SIGNAL(propertyChanged(PropertyContainer::PropertyName)),
Chris@1266 3279 this, SIGNAL(propertyChanged(PropertyContainer::PropertyName)));
Chris@127 3280 }
Chris@127 3281
Chris@728 3282 ViewPropertyContainer::~ViewPropertyContainer()
Chris@728 3283 {
Chris@728 3284 }