annotate view/View.cpp @ 1534:bfd8b22fd67c

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