annotate view/View.cpp @ 1457:160e6d010141 single-point

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