annotate view/View.cpp @ 1447:8afea53332f3 single-point

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