annotate view/View.cpp @ 1363:bbeffb29bf09

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