annotate view/View.cpp @ 1496:d09345e578a7

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