annotate view/View.cpp @ 1403:10e768adaee5

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