annotate base/View.cpp @ 117:c30728d5625c sv1-v0.9rc1

* Make vertical scale alignment modes work in note layer as well as time-value layer, and several significant fixes to it * Make it possible to draw notes properly on the note layer * Show units (and frequencies etc in note layer's case) in the time-value and note layer description boxes * Minor fix to item edit dialog layout * Some minor menu rearrangement * Comment out a lot of debug output * Add SV website and reference URLs to Help menu, and add code to (attempt to) open them in the user's preferred browser
author Chris Cannam
date Fri, 12 May 2006 14:40:43 +0000
parents 1be256ac7681
children e388730429b5
rev   line source
Chris@49 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@0 2
Chris@0 3 /*
Chris@52 4 Sonic Visualiser
Chris@52 5 An audio file viewer and annotation editor.
Chris@52 6 Centre for Digital Music, Queen Mary, University of London.
Chris@52 7 This file copyright 2006 Chris Cannam.
Chris@0 8
Chris@52 9 This program is free software; you can redistribute it and/or
Chris@52 10 modify it under the terms of the GNU General Public License as
Chris@52 11 published by the Free Software Foundation; either version 2 of the
Chris@52 12 License, or (at your option) any later version. See the file
Chris@52 13 COPYING included with this distribution for more information.
Chris@0 14 */
Chris@0 15
Chris@0 16 #include "base/View.h"
Chris@0 17 #include "base/Layer.h"
Chris@0 18 #include "base/Model.h"
Chris@0 19 #include "base/ZoomConstraint.h"
Chris@0 20 #include "base/Profiler.h"
Chris@0 21
Chris@0 22 #include "layer/TimeRulerLayer.h" //!!! damn, shouldn't be including that here
Chris@25 23 #include "model/PowerOfSqrtTwoZoomConstraint.h" //!!! likewise
Chris@0 24
Chris@0 25 #include <QPainter>
Chris@0 26 #include <QPaintEvent>
Chris@0 27 #include <QRect>
Chris@0 28 #include <QApplication>
Chris@0 29
Chris@0 30 #include <iostream>
Chris@16 31 #include <cassert>
Martin@37 32 #include <math.h>
Chris@0 33
Chris@109 34 //#define DEBUG_VIEW_WIDGET_PAINT 1
Chris@0 35
Chris@0 36 using std::cerr;
Chris@0 37 using std::endl;
Chris@0 38
Chris@0 39 View::View(QWidget *w, bool showProgress) :
Chris@0 40 QFrame(w),
Chris@0 41 m_centreFrame(0),
Chris@0 42 m_zoomLevel(1024),
Chris@0 43 m_followPan(true),
Chris@0 44 m_followZoom(true),
Chris@0 45 m_followPlay(PlaybackScrollPage),
Chris@0 46 m_lightBackground(true),
Chris@0 47 m_showProgress(showProgress),
Chris@0 48 m_cache(0),
Chris@0 49 m_cacheCentreFrame(0),
Chris@0 50 m_cacheZoomLevel(1024),
Chris@9 51 m_selectionCached(false),
Chris@0 52 m_deleting(false),
Chris@8 53 m_haveSelectedLayer(false),
Chris@29 54 m_manager(0),
Chris@29 55 m_propertyContainer(new ViewPropertyContainer(this))
Chris@0 56 {
Chris@0 57 // QWidget::setAttribute(Qt::WA_PaintOnScreen);
Chris@0 58 }
Chris@0 59
Chris@0 60 View::~View()
Chris@0 61 {
Chris@117 62 // std::cerr << "View::~View(" << this << ")" << std::endl;
Chris@43 63
Chris@0 64 m_deleting = true;
Chris@29 65 delete m_propertyContainer;
Chris@0 66 }
Chris@0 67
Chris@0 68 PropertyContainer::PropertyList
Chris@0 69 View::getProperties() const
Chris@0 70 {
Chris@29 71 PropertyContainer::PropertyList list;
Chris@94 72 list.push_back("Global Scroll");
Chris@94 73 list.push_back("Global Zoom");
Chris@94 74 list.push_back("Follow Playback");
Chris@0 75 return list;
Chris@0 76 }
Chris@0 77
Chris@94 78 QString
Chris@94 79 View::getPropertyLabel(const PropertyName &pn) const
Chris@94 80 {
Chris@94 81 if (pn == "Global Scroll") return tr("Global Scroll");
Chris@94 82 if (pn == "Global Zoom") return tr("Global Zoom");
Chris@94 83 if (pn == "Follow Playback") return tr("Follow Playback");
Chris@94 84 return "";
Chris@94 85 }
Chris@94 86
Chris@0 87 PropertyContainer::PropertyType
Chris@29 88 View::getPropertyType(const PropertyContainer::PropertyName &name) const
Chris@0 89 {
Chris@94 90 if (name == "Global Scroll") return PropertyContainer::ToggleProperty;
Chris@94 91 if (name == "Global Zoom") return PropertyContainer::ToggleProperty;
Chris@94 92 if (name == "Follow Playback") return PropertyContainer::ValueProperty;
Chris@29 93 return PropertyContainer::InvalidProperty;
Chris@0 94 }
Chris@0 95
Chris@0 96 int
Chris@29 97 View::getPropertyRangeAndValue(const PropertyContainer::PropertyName &name,
Chris@29 98 int *min, int *max) const
Chris@0 99 {
Chris@94 100 if (name == "Global Scroll") return m_followPan;
Chris@94 101 if (name == "Global Zoom") return m_followZoom;
Chris@94 102 if (name == "Follow Playback") {
Chris@29 103 if (min) *min = 0;
Chris@29 104 if (max) *max = 2;
Chris@29 105 return int(m_followPlay);
Chris@29 106 }
Chris@29 107 if (min) *min = 0;
Chris@29 108 if (max) *max = 0;
Chris@29 109 return 0;
Chris@0 110 }
Chris@0 111
Chris@0 112 QString
Chris@29 113 View::getPropertyValueLabel(const PropertyContainer::PropertyName &name,
Chris@29 114 int value) const
Chris@0 115 {
Chris@94 116 if (name == "Follow Playback") {
Chris@0 117 switch (value) {
Chris@0 118 default:
Chris@0 119 case 0: return tr("Scroll");
Chris@0 120 case 1: return tr("Page");
Chris@0 121 case 2: return tr("Off");
Chris@0 122 }
Chris@0 123 }
Chris@0 124 return tr("<unknown>");
Chris@0 125 }
Chris@0 126
Chris@0 127 void
Chris@29 128 View::setProperty(const PropertyContainer::PropertyName &name, int value)
Chris@0 129 {
Chris@94 130 if (name == "Global Scroll") {
Chris@0 131 setFollowGlobalPan(value != 0);
Chris@94 132 } else if (name == "Global Zoom") {
Chris@0 133 setFollowGlobalZoom(value != 0);
Chris@94 134 } else if (name == "Follow Playback") {
Chris@0 135 switch (value) {
Chris@0 136 default:
Chris@0 137 case 0: setPlaybackFollow(PlaybackScrollContinuous); break;
Chris@0 138 case 1: setPlaybackFollow(PlaybackScrollPage); break;
Chris@0 139 case 2: setPlaybackFollow(PlaybackIgnore); break;
Chris@0 140 }
Chris@0 141 }
Chris@0 142 }
Chris@0 143
Chris@0 144 size_t
Chris@0 145 View::getPropertyContainerCount() const
Chris@0 146 {
Chris@0 147 return m_layers.size() + 1; // the 1 is for me
Chris@0 148 }
Chris@0 149
Chris@0 150 const PropertyContainer *
Chris@0 151 View::getPropertyContainer(size_t i) const
Chris@0 152 {
Chris@0 153 return (const PropertyContainer *)(((View *)this)->
Chris@0 154 getPropertyContainer(i));
Chris@0 155 }
Chris@0 156
Chris@0 157 PropertyContainer *
Chris@0 158 View::getPropertyContainer(size_t i)
Chris@0 159 {
Chris@29 160 if (i == 0) return m_propertyContainer;
Chris@0 161 return m_layers[i-1];
Chris@0 162 }
Chris@0 163
Chris@78 164 bool
Chris@117 165 View::getValueExtents(QString unit, float &min, float &max, bool &log) const
Chris@78 166 {
Chris@78 167 bool have = false;
Chris@78 168
Chris@78 169 for (LayerList::const_iterator i = m_layers.begin();
Chris@78 170 i != m_layers.end(); ++i) {
Chris@78 171
Chris@78 172 QString layerUnit;
Chris@117 173 float layerMin = 0.0, layerMax = 0.0;
Chris@117 174 float displayMin = 0.0, displayMax = 0.0;
Chris@117 175 bool layerLog = false;
Chris@78 176
Chris@117 177 if ((*i)->getValueExtents(layerMin, layerMax, layerLog, layerUnit) &&
Chris@78 178 layerUnit.toLower() == unit.toLower()) {
Chris@78 179
Chris@117 180 if ((*i)->getDisplayExtents(displayMin, displayMax)) {
Chris@117 181
Chris@117 182 min = displayMin;
Chris@117 183 max = displayMax;
Chris@117 184 log = layerLog;
Chris@117 185 have = true;
Chris@117 186 break;
Chris@117 187
Chris@117 188 } else {
Chris@117 189
Chris@117 190 if (!have || layerMin < min) min = layerMin;
Chris@117 191 if (!have || layerMax > max) max = layerMax;
Chris@117 192 if (layerLog) log = true;
Chris@117 193 have = true;
Chris@117 194 }
Chris@78 195 }
Chris@78 196 }
Chris@78 197
Chris@78 198 return have;
Chris@78 199 }
Chris@78 200
Chris@78 201 int
Chris@78 202 View::getTextLabelHeight(const Layer *layer, QPainter &paint) const
Chris@78 203 {
Chris@85 204 std::map<int, Layer *> sortedLayers;
Chris@78 205
Chris@78 206 for (LayerList::const_iterator i = m_layers.begin();
Chris@78 207 i != m_layers.end(); ++i) {
Chris@85 208 if ((*i)->needsTextLabelHeight()) {
Chris@85 209 sortedLayers[getObjectExportId(*i)] = *i;
Chris@85 210 }
Chris@85 211 }
Chris@78 212
Chris@85 213 int y = 15 + paint.fontMetrics().ascent();
Chris@78 214
Chris@85 215 for (std::map<int, Layer *>::const_iterator i = sortedLayers.begin();
Chris@85 216 i != sortedLayers.end(); ++i) {
Chris@85 217 if (i->second == layer) return y;
Chris@85 218 y += paint.fontMetrics().height();
Chris@78 219 }
Chris@78 220
Chris@78 221 return y;
Chris@78 222 }
Chris@78 223
Chris@0 224 void
Chris@44 225 View::propertyContainerSelected(View *client, PropertyContainer *pc)
Chris@0 226 {
Chris@44 227 if (client != this) return;
Chris@44 228
Chris@29 229 if (pc == m_propertyContainer) {
Chris@8 230 if (m_haveSelectedLayer) {
Chris@8 231 m_haveSelectedLayer = false;
Chris@8 232 update();
Chris@8 233 }
Chris@8 234 return;
Chris@8 235 }
Chris@0 236
Chris@0 237 delete m_cache;
Chris@0 238 m_cache = 0;
Chris@0 239
Chris@0 240 Layer *selectedLayer = 0;
Chris@0 241
Chris@0 242 for (LayerList::iterator i = m_layers.begin(); i != m_layers.end(); ++i) {
Chris@0 243 if (*i == pc) {
Chris@0 244 selectedLayer = *i;
Chris@0 245 m_layers.erase(i);
Chris@0 246 break;
Chris@0 247 }
Chris@0 248 }
Chris@0 249
Chris@0 250 if (selectedLayer) {
Chris@8 251 m_haveSelectedLayer = true;
Chris@0 252 m_layers.push_back(selectedLayer);
Chris@0 253 update();
Chris@8 254 } else {
Chris@8 255 m_haveSelectedLayer = false;
Chris@0 256 }
Chris@0 257 }
Chris@0 258
Chris@8 259 void
Chris@8 260 View::toolModeChanged()
Chris@8 261 {
Chris@117 262 // std::cerr << "View::toolModeChanged(" << m_manager->getToolMode() << ")" << std::endl;
Chris@8 263 }
Chris@8 264
Chris@0 265 long
Chris@0 266 View::getStartFrame() const
Chris@0 267 {
Chris@0 268 size_t w2 = (width() / 2) * m_zoomLevel;
Chris@0 269 size_t frame = m_centreFrame;
Chris@0 270 if (frame >= w2) {
Chris@0 271 frame -= w2;
Chris@0 272 return (frame / m_zoomLevel * m_zoomLevel);
Chris@0 273 } else {
Chris@0 274 frame = w2 - frame;
Chris@0 275 frame = frame / m_zoomLevel * m_zoomLevel;
Chris@0 276 return -(long)frame - m_zoomLevel;
Chris@0 277 }
Chris@0 278 }
Chris@0 279
Chris@0 280 size_t
Chris@0 281 View::getEndFrame() const
Chris@0 282 {
Chris@16 283 return getFrameForX(width()) - 1;
Chris@0 284 }
Chris@0 285
Chris@0 286 void
Chris@0 287 View::setStartFrame(long f)
Chris@0 288 {
Chris@0 289 setCentreFrame(f + m_zoomLevel * (width() / 2));
Chris@0 290 }
Chris@0 291
Chris@10 292 bool
Chris@0 293 View::setCentreFrame(size_t f, bool e)
Chris@0 294 {
Chris@10 295 bool changeVisible = false;
Chris@10 296
Chris@0 297 if (m_centreFrame != f) {
Chris@0 298
Chris@0 299 int formerPixel = m_centreFrame / m_zoomLevel;
Chris@0 300
Chris@0 301 m_centreFrame = f;
Chris@0 302
Chris@0 303 int newPixel = m_centreFrame / m_zoomLevel;
Chris@0 304
Chris@0 305 if (newPixel != formerPixel) {
Chris@0 306
Chris@0 307 #ifdef DEBUG_VIEW_WIDGET_PAINT
Chris@0 308 std::cout << "View(" << this << ")::setCentreFrame: newPixel " << newPixel << ", formerPixel " << formerPixel << std::endl;
Chris@0 309 #endif
Chris@0 310 update();
Chris@10 311
Chris@10 312 changeVisible = true;
Chris@0 313 }
Chris@0 314
Chris@0 315 if (e) emit centreFrameChanged(this, f, m_followPan);
Chris@0 316 }
Chris@10 317
Chris@10 318 return changeVisible;
Chris@0 319 }
Chris@0 320
Chris@15 321 int
Chris@15 322 View::getXForFrame(long frame) const
Chris@15 323 {
Chris@15 324 return (frame - getStartFrame()) / m_zoomLevel;
Chris@15 325 }
Chris@15 326
Chris@15 327 long
Chris@15 328 View::getFrameForX(int x) const
Chris@15 329 {
Chris@15 330 return (long(x) * long(m_zoomLevel)) + getStartFrame();
Chris@15 331 }
Chris@15 332
Chris@33 333 float
Chris@33 334 View::getYForFrequency(float frequency,
Chris@33 335 float minf,
Chris@33 336 float maxf,
Chris@33 337 bool logarithmic) const
Chris@33 338 {
Chris@33 339 int h = height();
Chris@33 340
Chris@33 341 if (logarithmic) {
Chris@33 342
Chris@33 343 static float lastminf = 0.0, lastmaxf = 0.0;
Chris@33 344 static float logminf = 0.0, logmaxf = 0.0;
Chris@33 345
Chris@33 346 if (lastminf != minf) {
Chris@33 347 lastminf = (minf == 0.0 ? 1.0 : minf);
Chris@33 348 logminf = log10f(minf);
Chris@33 349 }
Chris@33 350 if (lastmaxf != maxf) {
Chris@33 351 lastmaxf = (maxf < lastminf ? lastminf : maxf);
Chris@33 352 logmaxf = log10f(maxf);
Chris@33 353 }
Chris@33 354
Chris@33 355 if (logminf == logmaxf) return 0;
Chris@33 356 return h - (h * (log10f(frequency) - logminf)) / (logmaxf - logminf);
Chris@33 357
Chris@33 358 } else {
Chris@33 359
Chris@33 360 if (minf == maxf) return 0;
Chris@33 361 return h - (h * (frequency - minf)) / (maxf - minf);
Chris@33 362 }
Chris@33 363 }
Chris@33 364
Chris@33 365 float
Chris@33 366 View::getFrequencyForY(int y,
Chris@33 367 float minf,
Chris@33 368 float maxf,
Chris@33 369 bool logarithmic) const
Chris@33 370 {
Chris@33 371 int h = height();
Chris@33 372
Chris@33 373 if (logarithmic) {
Chris@33 374
Chris@33 375 static float lastminf = 0.0, lastmaxf = 0.0;
Chris@33 376 static float logminf = 0.0, logmaxf = 0.0;
Chris@33 377
Chris@33 378 if (lastminf != minf) {
Chris@33 379 lastminf = (minf == 0.0 ? 1.0 : minf);
Chris@33 380 logminf = log10f(minf);
Chris@33 381 }
Chris@33 382 if (lastmaxf != maxf) {
Chris@33 383 lastmaxf = (maxf < lastminf ? lastminf : maxf);
Chris@33 384 logmaxf = log10f(maxf);
Chris@33 385 }
Chris@33 386
Chris@33 387 if (logminf == logmaxf) return 0;
Chris@33 388 return pow(10.f, logminf + ((logmaxf - logminf) * (h - y)) / h);
Chris@33 389
Chris@33 390 } else {
Chris@33 391
Chris@33 392 if (minf == maxf) return 0;
Chris@33 393 return minf + ((h - y) * (maxf - minf)) / h;
Chris@33 394 }
Chris@33 395 }
Chris@33 396
Chris@22 397 int
Chris@22 398 View::getZoomLevel() const
Chris@22 399 {
Martin@54 400 #ifdef DEBUG_VIEW_WIDGET_PAINT
Martin@54 401 std::cout << "zoom level: " << m_zoomLevel << std::endl;
Martin@54 402 #endif
Chris@22 403 return m_zoomLevel;
Chris@22 404 }
Chris@22 405
Chris@0 406 void
Chris@0 407 View::setZoomLevel(size_t z)
Chris@0 408 {
Chris@0 409 if (m_zoomLevel != int(z)) {
Chris@0 410 m_zoomLevel = z;
Chris@0 411 emit zoomLevelChanged(this, z, m_followZoom);
Chris@0 412 update();
Chris@0 413 }
Chris@0 414 }
Chris@0 415
Chris@16 416 View::LayerProgressBar::LayerProgressBar(QWidget *parent) :
Chris@16 417 QProgressBar(parent)
Chris@16 418 {
Chris@16 419 QFont f(font());
Chris@16 420 f.setPointSize(f.pointSize() * 8 / 10);
Chris@16 421 setFont(f);
Chris@16 422 }
Chris@16 423
Chris@0 424 void
Chris@0 425 View::addLayer(Layer *layer)
Chris@0 426 {
Chris@0 427 delete m_cache;
Chris@0 428 m_cache = 0;
Chris@0 429
Chris@0 430 m_layers.push_back(layer);
Chris@0 431
Chris@0 432 m_progressBars[layer] = new LayerProgressBar(this);
Chris@0 433 m_progressBars[layer]->setMinimum(0);
Chris@0 434 m_progressBars[layer]->setMaximum(100);
Chris@0 435 m_progressBars[layer]->setMinimumWidth(80);
Chris@0 436 m_progressBars[layer]->hide();
Chris@16 437
Chris@0 438 connect(layer, SIGNAL(layerParametersChanged()),
Chris@0 439 this, SLOT(layerParametersChanged()));
Chris@0 440 connect(layer, SIGNAL(layerNameChanged()),
Chris@0 441 this, SLOT(layerNameChanged()));
Chris@0 442 connect(layer, SIGNAL(modelChanged()),
Chris@0 443 this, SLOT(modelChanged()));
Chris@0 444 connect(layer, SIGNAL(modelCompletionChanged()),
Chris@0 445 this, SLOT(modelCompletionChanged()));
Chris@0 446 connect(layer, SIGNAL(modelChanged(size_t, size_t)),
Chris@0 447 this, SLOT(modelChanged(size_t, size_t)));
Chris@0 448 connect(layer, SIGNAL(modelReplaced()),
Chris@0 449 this, SLOT(modelReplaced()));
Chris@0 450
Chris@0 451 update();
Chris@0 452
Chris@0 453 emit propertyContainerAdded(layer);
Chris@0 454 }
Chris@0 455
Chris@0 456 void
Chris@0 457 View::removeLayer(Layer *layer)
Chris@0 458 {
Chris@0 459 if (m_deleting) {
Chris@0 460 return;
Chris@0 461 }
Chris@0 462
Chris@0 463 delete m_cache;
Chris@0 464 m_cache = 0;
Chris@0 465
Chris@0 466 for (LayerList::iterator i = m_layers.begin(); i != m_layers.end(); ++i) {
Chris@0 467 if (*i == layer) {
Chris@0 468 m_layers.erase(i);
Chris@0 469 if (m_progressBars.find(layer) != m_progressBars.end()) {
Chris@0 470 delete m_progressBars[layer];
Chris@0 471 m_progressBars.erase(layer);
Chris@0 472 }
Chris@0 473 break;
Chris@0 474 }
Chris@0 475 }
Chris@0 476
Chris@0 477 update();
Chris@0 478
Chris@0 479 emit propertyContainerRemoved(layer);
Chris@0 480 }
Chris@0 481
Chris@8 482 Layer *
Chris@8 483 View::getSelectedLayer()
Chris@8 484 {
Chris@8 485 if (m_haveSelectedLayer && !m_layers.empty()) {
Chris@8 486 return getLayer(getLayerCount() - 1);
Chris@8 487 } else {
Chris@8 488 return 0;
Chris@8 489 }
Chris@8 490 }
Chris@8 491
Chris@36 492 const Layer *
Chris@36 493 View::getSelectedLayer() const
Chris@36 494 {
Chris@36 495 return const_cast<const Layer *>(const_cast<View *>(this)->getSelectedLayer());
Chris@36 496 }
Chris@36 497
Chris@0 498 void
Chris@0 499 View::setViewManager(ViewManager *manager)
Chris@0 500 {
Chris@0 501 if (m_manager) {
Chris@0 502 m_manager->disconnect(this, SLOT(viewManagerCentreFrameChanged(void *, unsigned long, bool)));
Chris@0 503 m_manager->disconnect(this, SLOT(viewManagerZoomLevelChanged(void *, unsigned long, bool)));
Chris@0 504 disconnect(m_manager, SIGNAL(centreFrameChanged(void *, unsigned long, bool)));
Chris@0 505 disconnect(m_manager, SIGNAL(zoomLevelChanged(void *, unsigned long, bool)));
Chris@8 506 disconnect(m_manager, SIGNAL(toolModeChanged()));
Chris@8 507 disconnect(m_manager, SIGNAL(selectionChanged()));
Chris@9 508 disconnect(m_manager, SIGNAL(inProgressSelectionChanged()));
Chris@0 509 }
Chris@0 510
Chris@0 511 m_manager = manager;
Chris@0 512 if (m_followPan) setCentreFrame(m_manager->getGlobalCentreFrame(), false);
Chris@0 513 if (m_followZoom) setZoomLevel(m_manager->getGlobalZoom());
Chris@0 514
Chris@0 515 connect(m_manager, SIGNAL(centreFrameChanged(void *, unsigned long, bool)),
Chris@0 516 this, SLOT(viewManagerCentreFrameChanged(void *, unsigned long, bool)));
Chris@0 517 connect(m_manager, SIGNAL(playbackFrameChanged(unsigned long)),
Chris@0 518 this, SLOT(viewManagerPlaybackFrameChanged(unsigned long)));
Chris@0 519 connect(m_manager, SIGNAL(zoomLevelChanged(void *, unsigned long, bool)),
Chris@0 520 this, SLOT(viewManagerZoomLevelChanged(void *, unsigned long, bool)));
Chris@8 521 connect(m_manager, SIGNAL(toolModeChanged()),
Chris@8 522 this, SLOT(toolModeChanged()));
Chris@8 523 connect(m_manager, SIGNAL(selectionChanged()),
Chris@9 524 this, SLOT(selectionChanged()));
Chris@9 525 connect(m_manager, SIGNAL(inProgressSelectionChanged()),
Chris@9 526 this, SLOT(selectionChanged()));
Chris@65 527 connect(m_manager, SIGNAL(overlayModeChanged()),
Chris@65 528 this, SLOT(update()));
Chris@0 529
Chris@0 530 connect(this, SIGNAL(centreFrameChanged(void *, unsigned long, bool)),
Chris@0 531 m_manager, SIGNAL(centreFrameChanged(void *, unsigned long, bool)));
Chris@0 532 connect(this, SIGNAL(zoomLevelChanged(void *, unsigned long, bool)),
Chris@0 533 m_manager, SIGNAL(zoomLevelChanged(void *, unsigned long, bool)));
Chris@8 534
Chris@8 535 toolModeChanged();
Chris@0 536 }
Chris@0 537
Chris@0 538 void
Chris@0 539 View::setFollowGlobalPan(bool f)
Chris@0 540 {
Chris@0 541 m_followPan = f;
Chris@29 542 emit propertyContainerPropertyChanged(m_propertyContainer);
Chris@0 543 }
Chris@0 544
Chris@0 545 void
Chris@0 546 View::setFollowGlobalZoom(bool f)
Chris@0 547 {
Chris@0 548 m_followZoom = f;
Chris@29 549 emit propertyContainerPropertyChanged(m_propertyContainer);
Chris@0 550 }
Chris@0 551
Chris@0 552 void
Chris@46 553 View::drawVisibleText(QPainter &paint, int x, int y, QString text, TextStyle style)
Chris@45 554 {
Chris@46 555 if (style == OutlinedText) {
Chris@46 556
Chris@46 557 QColor origPenColour = paint.pen().color();
Chris@46 558 QColor penColour = origPenColour;
Chris@46 559 QColor surroundColour = Qt::white; //palette().background().color();
Chris@46 560
Chris@46 561 if (!hasLightBackground()) {
Chris@46 562 int h, s, v;
Chris@46 563 penColour.getHsv(&h, &s, &v);
Chris@46 564 penColour = QColor::fromHsv(h, s, 255 - v);
Chris@46 565 surroundColour = Qt::black;
Chris@46 566 }
Chris@46 567
Chris@46 568 paint.setPen(surroundColour);
Chris@46 569
Chris@46 570 for (int dx = -1; dx <= 1; ++dx) {
Chris@46 571 for (int dy = -1; dy <= 1; ++dy) {
Chris@46 572 if (!(dx || dy)) continue;
Chris@46 573 paint.drawText(x + dx, y + dy, text);
Chris@46 574 }
Chris@46 575 }
Chris@46 576
Chris@46 577 paint.setPen(penColour);
Chris@46 578
Chris@46 579 paint.drawText(x, y, text);
Chris@46 580
Chris@46 581 paint.setPen(origPenColour);
Chris@46 582
Chris@46 583 } else {
Chris@46 584
Chris@46 585 std::cerr << "ERROR: View::drawVisibleText: Boxed style not yet implemented!" << std::endl;
Chris@46 586 }
Chris@45 587 }
Chris@45 588
Chris@45 589 void
Chris@0 590 View::setPlaybackFollow(PlaybackFollowMode m)
Chris@0 591 {
Chris@0 592 m_followPlay = m;
Chris@29 593 emit propertyContainerPropertyChanged(m_propertyContainer);
Chris@0 594 }
Chris@0 595
Chris@0 596 void
Chris@0 597 View::modelChanged()
Chris@0 598 {
Chris@0 599 QObject *obj = sender();
Chris@0 600
Chris@0 601 #ifdef DEBUG_VIEW_WIDGET_PAINT
Chris@1 602 std::cerr << "View(" << this << ")::modelChanged()" << std::endl;
Chris@0 603 #endif
Chris@31 604
Chris@31 605 // If the model that has changed is not used by any of the cached
Chris@31 606 // layers, we won't need to recreate the cache
Chris@31 607
Chris@31 608 bool recreate = false;
Chris@31 609
Chris@31 610 bool discard;
Chris@31 611 LayerList scrollables = getScrollableBackLayers(false, discard);
Chris@31 612 for (LayerList::const_iterator i = scrollables.begin();
Chris@31 613 i != scrollables.end(); ++i) {
Chris@31 614 if (*i == obj || (*i)->getModel() == obj) {
Chris@31 615 recreate = true;
Chris@31 616 break;
Chris@31 617 }
Chris@31 618 }
Chris@31 619
Chris@31 620 if (recreate) {
Chris@31 621 delete m_cache;
Chris@31 622 m_cache = 0;
Chris@31 623 }
Chris@0 624
Chris@0 625 checkProgress(obj);
Chris@0 626
Chris@0 627 update();
Chris@0 628 }
Chris@0 629
Chris@0 630 void
Chris@0 631 View::modelChanged(size_t startFrame, size_t endFrame)
Chris@0 632 {
Chris@0 633 QObject *obj = sender();
Chris@0 634
Chris@0 635 long myStartFrame = getStartFrame();
Chris@0 636 size_t myEndFrame = getEndFrame();
Chris@0 637
Chris@1 638 #ifdef DEBUG_VIEW_WIDGET_PAINT
Chris@1 639 std::cerr << "View(" << this << ")::modelChanged(" << startFrame << "," << endFrame << ") [me " << myStartFrame << "," << myEndFrame << "]" << std::endl;
Chris@1 640 #endif
Chris@1 641
Chris@0 642 if (myStartFrame > 0 && endFrame < size_t(myStartFrame)) {
Chris@0 643 checkProgress(obj);
Chris@0 644 return;
Chris@0 645 }
Chris@0 646 if (startFrame > myEndFrame) {
Chris@0 647 checkProgress(obj);
Chris@0 648 return;
Chris@0 649 }
Chris@0 650
Chris@31 651 // If the model that has changed is not used by any of the cached
Chris@31 652 // layers, we won't need to recreate the cache
Chris@31 653
Chris@31 654 bool recreate = false;
Chris@31 655
Chris@31 656 bool discard;
Chris@31 657 LayerList scrollables = getScrollableBackLayers(false, discard);
Chris@31 658 for (LayerList::const_iterator i = scrollables.begin();
Chris@31 659 i != scrollables.end(); ++i) {
Chris@31 660 if (*i == obj || (*i)->getModel() == obj) {
Chris@31 661 recreate = true;
Chris@31 662 break;
Chris@31 663 }
Chris@31 664 }
Chris@31 665
Chris@31 666 if (recreate) {
Chris@31 667 delete m_cache;
Chris@31 668 m_cache = 0;
Chris@31 669 }
Chris@0 670
Chris@0 671 if (long(startFrame) < myStartFrame) startFrame = myStartFrame;
Chris@0 672 if (endFrame > myEndFrame) endFrame = myEndFrame;
Chris@0 673
Chris@15 674 int x0 = getXForFrame(startFrame);
Chris@15 675 int x1 = getXForFrame(endFrame + 1);
Chris@15 676 if (x1 < x0) x1 = x0;
Chris@0 677
Chris@0 678 checkProgress(obj);
Chris@0 679
Chris@31 680 update();
Chris@31 681 //!! update(x0, 0, x1 - x0 + 1, height());
Chris@0 682 }
Chris@0 683
Chris@0 684 void
Chris@0 685 View::modelCompletionChanged()
Chris@0 686 {
Chris@0 687 QObject *obj = sender();
Chris@0 688 checkProgress(obj);
Chris@0 689 }
Chris@0 690
Chris@0 691 void
Chris@0 692 View::modelReplaced()
Chris@0 693 {
Chris@0 694 #ifdef DEBUG_VIEW_WIDGET_PAINT
Chris@1 695 std::cerr << "View(" << this << ")::modelReplaced()" << std::endl;
Chris@0 696 #endif
Chris@1 697 delete m_cache;
Chris@1 698 m_cache = 0;
Chris@1 699
Chris@0 700 update();
Chris@0 701 }
Chris@0 702
Chris@0 703 void
Chris@0 704 View::layerParametersChanged()
Chris@0 705 {
Chris@0 706 Layer *layer = dynamic_cast<Layer *>(sender());
Chris@0 707
Chris@0 708 #ifdef DEBUG_VIEW_WIDGET_PAINT
Chris@0 709 std::cerr << "View::layerParametersChanged()" << std::endl;
Chris@0 710 #endif
Chris@0 711
Chris@0 712 delete m_cache;
Chris@0 713 m_cache = 0;
Chris@0 714 update();
Chris@0 715
Chris@0 716 if (layer) {
Chris@0 717 emit propertyContainerPropertyChanged(layer);
Chris@0 718 }
Chris@0 719 }
Chris@0 720
Chris@0 721 void
Chris@0 722 View::layerNameChanged()
Chris@0 723 {
Chris@0 724 Layer *layer = dynamic_cast<Layer *>(sender());
Chris@0 725 if (layer) emit propertyContainerNameChanged(layer);
Chris@0 726 }
Chris@0 727
Chris@0 728 void
Chris@0 729 View::viewManagerCentreFrameChanged(void *p, unsigned long f, bool locked)
Chris@0 730 {
Chris@0 731 if (m_followPan && p != this && locked) {
Chris@0 732 if (m_manager && (sender() == m_manager)) {
Chris@0 733 #ifdef DEBUG_VIEW_WIDGET_PAINT
Chris@0 734 std::cerr << this << ": manager frame changed " << f << " from " << p << std::endl;
Chris@0 735 #endif
Chris@0 736 setCentreFrame(f);
Chris@0 737 if (p == this) repaint();
Chris@0 738 }
Chris@0 739 }
Chris@0 740 }
Chris@0 741
Chris@0 742 void
Chris@0 743 View::viewManagerPlaybackFrameChanged(unsigned long f)
Chris@0 744 {
Chris@0 745 if (m_manager) {
Chris@0 746 if (sender() != m_manager) return;
Chris@0 747 }
Chris@0 748
Chris@0 749 if (m_playPointerFrame == f) return;
Chris@15 750 bool visible = (getXForFrame(m_playPointerFrame) != getXForFrame(f));
Chris@0 751 size_t oldPlayPointerFrame = m_playPointerFrame;
Chris@0 752 m_playPointerFrame = f;
Chris@0 753 if (!visible) return;
Chris@0 754
Chris@0 755 switch (m_followPlay) {
Chris@0 756
Chris@0 757 case PlaybackScrollContinuous:
Chris@107 758 if (QApplication::mouseButtons() == Qt::NoButton) {
Chris@0 759 setCentreFrame(f, false);
Chris@0 760 }
Chris@0 761 break;
Chris@0 762
Chris@0 763 case PlaybackScrollPage:
Chris@0 764 {
Chris@15 765 int xold = getXForFrame(oldPlayPointerFrame);
Chris@107 766 update(xold - 1, 0, 3, height());
Chris@10 767
Chris@15 768 long w = getEndFrame() - getStartFrame();
Chris@0 769 w -= w/5;
Chris@0 770 long sf = (f / w) * w - w/8;
Chris@10 771
Chris@10 772 if (m_manager &&
Chris@10 773 m_manager->isPlaying() &&
Chris@10 774 m_manager->getPlaySelectionMode()) {
Chris@24 775 MultiSelection::SelectionList selections = m_manager->getSelections();
Chris@10 776 if (!selections.empty()) {
Chris@10 777 size_t selectionStart = selections.begin()->getStartFrame();
Chris@10 778 if (sf < long(selectionStart) - w / 10) {
Chris@10 779 sf = long(selectionStart) - w / 10;
Chris@10 780 }
Chris@10 781 }
Chris@10 782 }
Chris@10 783
Chris@0 784 #ifdef DEBUG_VIEW_WIDGET_PAINT
Chris@0 785 std::cerr << "PlaybackScrollPage: f = " << f << ", sf = " << sf << ", start frame "
Chris@0 786 << getStartFrame() << std::endl;
Chris@0 787 #endif
Chris@10 788
Chris@15 789 // We don't consider scrolling unless the pointer is outside
Chris@15 790 // the clearly visible range already
Chris@15 791
Chris@15 792 int xnew = getXForFrame(m_playPointerFrame);
Chris@15 793
Chris@18 794 #ifdef DEBUG_VIEW_WIDGET_PAINT
Chris@18 795 std::cerr << "xnew = " << xnew << ", width = " << width() << std::endl;
Chris@18 796 #endif
Chris@18 797
Chris@15 798 if (xnew < width()/8 || xnew > (width()*7)/8) {
Chris@107 799 if (QApplication::mouseButtons() == Qt::NoButton) {
Chris@15 800 long offset = getFrameForX(width()/2) - getStartFrame();
Chris@15 801 long newCentre = sf + offset;
Chris@15 802 bool changed = setCentreFrame(newCentre, false);
Chris@15 803 if (changed) {
Chris@15 804 xold = getXForFrame(oldPlayPointerFrame);
Chris@15 805 update(xold - 1, 0, 3, height());
Chris@15 806 }
Chris@10 807 }
Chris@10 808 }
Chris@10 809
Chris@0 810 update(xnew - 1, 0, 3, height());
Chris@10 811
Chris@0 812 break;
Chris@0 813 }
Chris@0 814
Chris@0 815 case PlaybackIgnore:
Chris@0 816 if (long(f) >= getStartFrame() && f < getEndFrame()) {
Chris@0 817 update();
Chris@0 818 }
Chris@0 819 break;
Chris@0 820 }
Chris@0 821 }
Chris@0 822
Chris@0 823 void
Chris@0 824 View::viewManagerZoomLevelChanged(void *p, unsigned long z, bool locked)
Chris@0 825 {
Chris@0 826 if (m_followZoom && p != this && locked) {
Chris@0 827 if (m_manager && (sender() == m_manager)) {
Chris@0 828 setZoomLevel(z);
Chris@0 829 if (p == this) repaint();
Chris@0 830 }
Chris@0 831 }
Chris@0 832 }
Chris@0 833
Chris@9 834 void
Chris@9 835 View::selectionChanged()
Chris@9 836 {
Chris@9 837 if (m_selectionCached) {
Chris@9 838 delete m_cache;
Chris@9 839 m_cache = 0;
Chris@9 840 m_selectionCached = false;
Chris@9 841 }
Chris@9 842 update();
Chris@9 843 }
Chris@9 844
Chris@0 845 size_t
Chris@0 846 View::getModelsStartFrame() const
Chris@0 847 {
Chris@0 848 bool first = true;
Chris@0 849 size_t startFrame = 0;
Chris@0 850
Chris@0 851 for (LayerList::const_iterator i = m_layers.begin(); i != m_layers.end(); ++i) {
Chris@0 852
Chris@0 853 if ((*i)->getModel() && (*i)->getModel()->isOK()) {
Chris@0 854
Chris@0 855 size_t thisStartFrame = (*i)->getModel()->getStartFrame();
Chris@0 856
Chris@0 857 if (first || thisStartFrame < startFrame) {
Chris@0 858 startFrame = thisStartFrame;
Chris@0 859 }
Chris@0 860 first = false;
Chris@0 861 }
Chris@0 862 }
Chris@0 863 return startFrame;
Chris@0 864 }
Chris@0 865
Chris@0 866 size_t
Chris@0 867 View::getModelsEndFrame() const
Chris@0 868 {
Chris@0 869 bool first = true;
Chris@0 870 size_t endFrame = 0;
Chris@0 871
Chris@0 872 for (LayerList::const_iterator i = m_layers.begin(); i != m_layers.end(); ++i) {
Chris@0 873
Chris@0 874 if ((*i)->getModel() && (*i)->getModel()->isOK()) {
Chris@0 875
Chris@0 876 size_t thisEndFrame = (*i)->getModel()->getEndFrame();
Chris@0 877
Chris@0 878 if (first || thisEndFrame > endFrame) {
Chris@0 879 endFrame = thisEndFrame;
Chris@0 880 }
Chris@0 881 first = false;
Chris@0 882 }
Chris@0 883 }
Chris@0 884
Chris@0 885 if (first) return getModelsStartFrame();
Chris@0 886 return endFrame;
Chris@0 887 }
Chris@0 888
Chris@0 889 int
Chris@0 890 View::getModelsSampleRate() const
Chris@0 891 {
Chris@0 892 //!!! Just go for the first, for now. If we were supporting
Chris@0 893 // multiple samplerates, we'd probably want to do frame/time
Chris@0 894 // conversion in the model
Chris@0 895
Chris@0 896 for (LayerList::const_iterator i = m_layers.begin(); i != m_layers.end(); ++i) {
Chris@0 897 if ((*i)->getModel() && (*i)->getModel()->isOK()) {
Chris@0 898 return (*i)->getModel()->getSampleRate();
Chris@0 899 }
Chris@0 900 }
Chris@0 901 return 0;
Chris@0 902 }
Chris@0 903
Chris@0 904 bool
Chris@0 905 View::areLayersScrollable() const
Chris@0 906 {
Chris@0 907 // True iff all views are scrollable
Chris@0 908 for (LayerList::const_iterator i = m_layers.begin(); i != m_layers.end(); ++i) {
Chris@36 909 if (!(*i)->isLayerScrollable(this)) return false;
Chris@0 910 }
Chris@0 911 return true;
Chris@0 912 }
Chris@0 913
Chris@0 914 View::LayerList
Chris@31 915 View::getScrollableBackLayers(bool testChanged, bool &changed) const
Chris@0 916 {
Chris@0 917 changed = false;
Chris@0 918
Chris@46 919 // We want a list of all the scrollable layers that are behind the
Chris@46 920 // backmost non-scrollable layer.
Chris@46 921
Chris@0 922 LayerList scrollables;
Chris@111 923 bool metUnscrollable = false;
Chris@111 924
Chris@0 925 for (LayerList::const_iterator i = m_layers.begin(); i != m_layers.end(); ++i) {
Chris@36 926 if ((*i)->isLayerDormant(this)) continue;
Chris@46 927 if ((*i)->isLayerOpaque()) {
Chris@46 928 // You can't see anything behind an opaque layer!
Chris@46 929 scrollables.clear();
Chris@111 930 if (metUnscrollable) break;
Chris@46 931 }
Chris@111 932 if (!metUnscrollable && (*i)->isLayerScrollable(this)) {
Chris@111 933 scrollables.push_back(*i);
Chris@111 934 } else {
Chris@111 935 metUnscrollable = true;
Chris@111 936 }
Chris@0 937 }
Chris@0 938
Chris@31 939 if (testChanged && scrollables != m_lastScrollableBackLayers) {
Chris@0 940 m_lastScrollableBackLayers = scrollables;
Chris@0 941 changed = true;
Chris@0 942 }
Chris@0 943 return scrollables;
Chris@0 944 }
Chris@0 945
Chris@0 946 View::LayerList
Chris@31 947 View::getNonScrollableFrontLayers(bool testChanged, bool &changed) const
Chris@0 948 {
Chris@0 949 changed = false;
Chris@0 950 LayerList nonScrollables;
Chris@0 951
Chris@0 952 // Everything in front of the first non-scrollable from the back
Chris@0 953 // should also be considered non-scrollable
Chris@0 954
Chris@46 955 bool started = false;
Chris@46 956
Chris@0 957 for (LayerList::const_iterator i = m_layers.begin(); i != m_layers.end(); ++i) {
Chris@36 958 if ((*i)->isLayerDormant(this)) continue;
Chris@46 959 if (!started && (*i)->isLayerScrollable(this)) {
Chris@0 960 continue;
Chris@0 961 }
Chris@46 962 started = true;
Chris@46 963 if ((*i)->isLayerOpaque()) {
Chris@46 964 // You can't see anything behind an opaque layer!
Chris@46 965 nonScrollables.clear();
Chris@46 966 }
Chris@0 967 nonScrollables.push_back(*i);
Chris@0 968 }
Chris@0 969
Chris@31 970 if (testChanged && nonScrollables != m_lastNonScrollableBackLayers) {
Chris@0 971 m_lastNonScrollableBackLayers = nonScrollables;
Chris@0 972 changed = true;
Chris@0 973 }
Chris@0 974
Chris@0 975 return nonScrollables;
Chris@0 976 }
Chris@0 977
Chris@0 978 size_t
Chris@0 979 View::getZoomConstraintBlockSize(size_t blockSize,
Chris@0 980 ZoomConstraint::RoundingDirection dir)
Chris@0 981 const
Chris@0 982 {
Chris@0 983 size_t candidate = blockSize;
Chris@0 984 bool haveCandidate = false;
Chris@0 985
Chris@25 986 PowerOfSqrtTwoZoomConstraint defaultZoomConstraint;
Chris@25 987
Chris@0 988 for (LayerList::const_iterator i = m_layers.begin(); i != m_layers.end(); ++i) {
Chris@0 989
Chris@25 990 const ZoomConstraint *zoomConstraint = (*i)->getZoomConstraint();
Chris@25 991 if (!zoomConstraint) zoomConstraint = &defaultZoomConstraint;
Chris@0 992
Chris@25 993 size_t thisBlockSize =
Chris@25 994 zoomConstraint->getNearestBlockSize(blockSize, dir);
Chris@0 995
Chris@25 996 // Go for the block size that's furthest from the one
Chris@25 997 // passed in. Most of the time, that's what we want.
Chris@25 998 if (!haveCandidate ||
Chris@25 999 (thisBlockSize > blockSize && thisBlockSize > candidate) ||
Chris@25 1000 (thisBlockSize < blockSize && thisBlockSize < candidate)) {
Chris@25 1001 candidate = thisBlockSize;
Chris@25 1002 haveCandidate = true;
Chris@0 1003 }
Chris@0 1004 }
Chris@0 1005
Chris@0 1006 return candidate;
Chris@0 1007 }
Chris@0 1008
Chris@0 1009 void
Chris@0 1010 View::zoom(bool in)
Chris@0 1011 {
Chris@0 1012 int newZoomLevel = m_zoomLevel;
Chris@0 1013
Chris@0 1014 if (in) {
Chris@0 1015 newZoomLevel = getZoomConstraintBlockSize(newZoomLevel - 1,
Chris@0 1016 ZoomConstraint::RoundDown);
Chris@0 1017 } else {
Chris@0 1018 newZoomLevel = getZoomConstraintBlockSize(newZoomLevel + 1,
Chris@0 1019 ZoomConstraint::RoundUp);
Chris@0 1020 }
Chris@0 1021
Chris@0 1022 if (newZoomLevel != m_zoomLevel) {
Chris@0 1023 setZoomLevel(newZoomLevel);
Chris@0 1024 }
Chris@0 1025 }
Chris@0 1026
Chris@0 1027 void
Chris@12 1028 View::scroll(bool right, bool lots)
Chris@12 1029 {
Chris@12 1030 long delta;
Chris@12 1031 if (lots) {
Chris@15 1032 delta = (getEndFrame() - getStartFrame()) / 2;
Chris@12 1033 } else {
Chris@15 1034 delta = (getEndFrame() - getStartFrame()) / 20;
Chris@12 1035 }
Chris@12 1036 if (right) delta = -delta;
Chris@12 1037
Chris@12 1038 if (int(m_centreFrame) < delta) {
Chris@12 1039 setCentreFrame(0);
Chris@12 1040 } else if (int(m_centreFrame) - delta >= int(getModelsEndFrame())) {
Chris@12 1041 setCentreFrame(getModelsEndFrame());
Chris@12 1042 } else {
Chris@12 1043 setCentreFrame(m_centreFrame - delta);
Chris@12 1044 }
Chris@12 1045 }
Chris@12 1046
Chris@12 1047 void
Chris@0 1048 View::checkProgress(void *object)
Chris@0 1049 {
Chris@0 1050 if (!m_showProgress) return;
Chris@0 1051
Chris@0 1052 int ph = height();
Chris@0 1053
Chris@0 1054 for (ProgressMap::const_iterator i = m_progressBars.begin();
Chris@0 1055 i != m_progressBars.end(); ++i) {
Chris@0 1056
Chris@0 1057 if (i->first == object) {
Chris@0 1058
Chris@0 1059 int completion = i->first->getCompletion();
Chris@0 1060
Chris@0 1061 if (completion >= 100) {
Chris@0 1062
Chris@0 1063 i->second->hide();
Chris@0 1064
Chris@0 1065 } else {
Chris@0 1066
Chris@0 1067 i->second->setText(i->first->getPropertyContainerName());
Chris@0 1068 i->second->setValue(completion);
Chris@0 1069 i->second->move(0, ph - i->second->height());
Chris@0 1070
Chris@0 1071 i->second->show();
Chris@0 1072 i->second->update();
Chris@0 1073
Chris@0 1074 ph -= i->second->height();
Chris@0 1075 }
Chris@0 1076 } else {
Chris@0 1077 if (i->second->isVisible()) {
Chris@0 1078 ph -= i->second->height();
Chris@0 1079 }
Chris@0 1080 }
Chris@0 1081 }
Chris@0 1082 }
Chris@0 1083
Chris@0 1084 void
Chris@0 1085 View::paintEvent(QPaintEvent *e)
Chris@0 1086 {
Chris@0 1087 // Profiler prof("View::paintEvent", true);
Chris@0 1088 // std::cerr << "View::paintEvent" << std::endl;
Chris@0 1089
Chris@0 1090 if (m_layers.empty()) {
Chris@0 1091 QFrame::paintEvent(e);
Chris@0 1092 return;
Chris@0 1093 }
Chris@0 1094
Chris@0 1095 // ensure our constraints are met
Chris@0 1096 m_zoomLevel = getZoomConstraintBlockSize(m_zoomLevel,
Chris@0 1097 ZoomConstraint::RoundUp);
Chris@0 1098
Chris@0 1099 QPainter paint;
Chris@0 1100 bool repaintCache = false;
Chris@0 1101 bool paintedCacheRect = false;
Chris@0 1102
Chris@0 1103 QRect cacheRect(rect());
Chris@0 1104
Chris@0 1105 if (e) {
Chris@0 1106 cacheRect &= e->rect();
Chris@0 1107 #ifdef DEBUG_VIEW_WIDGET_PAINT
Chris@0 1108 std::cerr << "paint rect " << cacheRect.width() << "x" << cacheRect.height()
Chris@0 1109 << ", my rect " << width() << "x" << height() << std::endl;
Chris@0 1110 #endif
Chris@0 1111 }
Chris@0 1112
Chris@0 1113 QRect nonCacheRect(cacheRect);
Chris@0 1114
Chris@0 1115 // If not all layers are scrollable, but some of the back layers
Chris@46 1116 // are, we should store only those in the cache.
Chris@0 1117
Chris@0 1118 bool layersChanged = false;
Chris@31 1119 LayerList scrollables = getScrollableBackLayers(true, layersChanged);
Chris@31 1120 LayerList nonScrollables = getNonScrollableFrontLayers(true, layersChanged);
Chris@9 1121 bool selectionCacheable = nonScrollables.empty();
Chris@9 1122 bool haveSelections = m_manager && !m_manager->getSelections().empty();
Chris@9 1123 bool selectionDrawn = false;
Chris@0 1124
Chris@46 1125 // If all the non-scrollable layers are non-opaque, then we draw
Chris@46 1126 // the selection rectangle behind them and cache it. If any are
Chris@46 1127 // opaque, however, we can't cache.
Chris@46 1128 //
Chris@10 1129 if (!selectionCacheable) {
Chris@10 1130 selectionCacheable = true;
Chris@10 1131 for (LayerList::const_iterator i = nonScrollables.begin();
Chris@10 1132 i != nonScrollables.end(); ++i) {
Chris@10 1133 if ((*i)->isLayerOpaque()) {
Chris@10 1134 selectionCacheable = false;
Chris@10 1135 break;
Chris@10 1136 }
Chris@10 1137 }
Chris@10 1138 }
Chris@10 1139
Chris@34 1140 if (selectionCacheable) {
Chris@34 1141 QPoint localPos;
Chris@34 1142 bool closeToLeft, closeToRight;
Chris@34 1143 if (shouldIlluminateLocalSelection(localPos, closeToLeft, closeToRight)) {
Chris@34 1144 selectionCacheable = false;
Chris@34 1145 }
Chris@34 1146 }
Chris@34 1147
Chris@0 1148 #ifdef DEBUG_VIEW_WIDGET_PAINT
Chris@0 1149 std::cerr << "View(" << this << ")::paintEvent: have " << scrollables.size()
Chris@0 1150 << " scrollable back layers and " << nonScrollables.size()
Chris@0 1151 << " non-scrollable front layers" << std::endl;
Chris@9 1152 std::cerr << "haveSelections " << haveSelections << ", selectionCacheable "
Chris@9 1153 << selectionCacheable << ", m_selectionCached " << m_selectionCached << std::endl;
Chris@0 1154 #endif
Chris@0 1155
Chris@9 1156 if (layersChanged || scrollables.empty() ||
Chris@9 1157 (haveSelections && (selectionCacheable != m_selectionCached))) {
Chris@0 1158 delete m_cache;
Chris@0 1159 m_cache = 0;
Chris@9 1160 m_selectionCached = false;
Chris@0 1161 }
Chris@0 1162
Chris@0 1163 if (!scrollables.empty()) {
Chris@0 1164 if (!m_cache ||
Chris@0 1165 m_cacheZoomLevel != m_zoomLevel ||
Chris@0 1166 width() != m_cache->width() ||
Chris@0 1167 height() != m_cache->height()) {
Chris@0 1168
Chris@0 1169 // cache is not valid
Chris@0 1170
Chris@0 1171 if (cacheRect.width() < width()/10) {
Chris@0 1172 #ifdef DEBUG_VIEW_WIDGET_PAINT
Chris@0 1173 std::cerr << "View(" << this << ")::paintEvent: small repaint, not bothering to recreate cache" << std::endl;
Chris@0 1174 #endif
Chris@0 1175 } else {
Chris@0 1176 delete m_cache;
Chris@0 1177 m_cache = new QPixmap(width(), height());
Chris@0 1178 #ifdef DEBUG_VIEW_WIDGET_PAINT
Chris@0 1179 std::cerr << "View(" << this << ")::paintEvent: recreated cache" << std::endl;
Chris@0 1180 #endif
Chris@0 1181 cacheRect = rect();
Chris@0 1182 repaintCache = true;
Chris@0 1183 }
Chris@0 1184
Chris@0 1185 } else if (m_cacheCentreFrame != m_centreFrame) {
Chris@0 1186
Chris@15 1187 long dx =
Chris@15 1188 getXForFrame(m_cacheCentreFrame) -
Chris@15 1189 getXForFrame(m_centreFrame);
Chris@0 1190
Chris@0 1191 if (dx > -width() && dx < width()) {
Chris@0 1192 #if defined(Q_WS_WIN32) || defined(Q_WS_MAC)
Chris@0 1193 // Copying a pixmap to itself doesn't work properly on Windows
Chris@0 1194 // or Mac (it only works when moving in one direction)
Chris@0 1195 static QPixmap *tmpPixmap = 0;
Chris@0 1196 if (!tmpPixmap ||
Chris@0 1197 tmpPixmap->width() != width() ||
Chris@0 1198 tmpPixmap->height() != height()) {
Chris@0 1199 delete tmpPixmap;
Chris@0 1200 tmpPixmap = new QPixmap(width(), height());
Chris@0 1201 }
Chris@0 1202 paint.begin(tmpPixmap);
Chris@0 1203 paint.drawPixmap(0, 0, *m_cache);
Chris@0 1204 paint.end();
Chris@0 1205 paint.begin(m_cache);
Chris@0 1206 paint.drawPixmap(dx, 0, *tmpPixmap);
Chris@0 1207 paint.end();
Chris@0 1208 #else
Chris@0 1209 // But it seems to be fine on X11
Chris@0 1210 paint.begin(m_cache);
Chris@0 1211 paint.drawPixmap(dx, 0, *m_cache);
Chris@0 1212 paint.end();
Chris@0 1213 #endif
Chris@0 1214
Chris@0 1215 if (dx < 0) {
Chris@0 1216 cacheRect = QRect(width() + dx, 0, -dx, height());
Chris@0 1217 } else {
Chris@0 1218 cacheRect = QRect(0, 0, dx, height());
Chris@0 1219 }
Chris@0 1220 #ifdef DEBUG_VIEW_WIDGET_PAINT
Chris@0 1221 std::cerr << "View(" << this << ")::paintEvent: scrolled cache by " << dx << std::endl;
Chris@0 1222 #endif
Chris@0 1223 } else {
Chris@0 1224 cacheRect = rect();
Chris@0 1225 #ifdef DEBUG_VIEW_WIDGET_PAINT
Chris@0 1226 std::cerr << "View(" << this << ")::paintEvent: scrolling too far" << std::endl;
Chris@0 1227 #endif
Chris@0 1228 }
Chris@0 1229 repaintCache = true;
Chris@0 1230
Chris@0 1231 } else {
Chris@0 1232 #ifdef DEBUG_VIEW_WIDGET_PAINT
Chris@0 1233 std::cerr << "View(" << this << ")::paintEvent: cache is good" << std::endl;
Chris@0 1234 #endif
Chris@0 1235 paint.begin(this);
Chris@0 1236 paint.drawPixmap(cacheRect, *m_cache, cacheRect);
Chris@0 1237 paint.end();
Chris@0 1238 QFrame::paintEvent(e);
Chris@0 1239 paintedCacheRect = true;
Chris@0 1240 }
Chris@0 1241
Chris@0 1242 m_cacheCentreFrame = m_centreFrame;
Chris@0 1243 m_cacheZoomLevel = m_zoomLevel;
Chris@0 1244 }
Chris@0 1245
Chris@0 1246 #ifdef DEBUG_VIEW_WIDGET_PAINT
Chris@0 1247 // std::cerr << "View(" << this << ")::paintEvent: cacheRect " << cacheRect << ", nonCacheRect " << (nonCacheRect | cacheRect) << ", repaintCache " << repaintCache << ", paintedCacheRect " << paintedCacheRect << std::endl;
Chris@0 1248 #endif
Chris@0 1249
Chris@0 1250 // Scrollable (cacheable) items first
Chris@0 1251
Chris@0 1252 if (!paintedCacheRect) {
Chris@0 1253
Chris@0 1254 if (repaintCache) paint.begin(m_cache);
Chris@0 1255 else paint.begin(this);
Chris@0 1256
Chris@0 1257 paint.setClipRect(cacheRect);
Chris@0 1258
Chris@0 1259 if (hasLightBackground()) {
Chris@0 1260 paint.setPen(Qt::white);
Chris@0 1261 paint.setBrush(Qt::white);
Chris@0 1262 } else {
Chris@0 1263 paint.setPen(Qt::black);
Chris@0 1264 paint.setBrush(Qt::black);
Chris@0 1265 }
Chris@0 1266 paint.drawRect(cacheRect);
Chris@0 1267
Chris@0 1268 paint.setPen(Qt::black);
Chris@0 1269 paint.setBrush(Qt::NoBrush);
Chris@0 1270
Chris@0 1271 for (LayerList::iterator i = scrollables.begin(); i != scrollables.end(); ++i) {
Chris@8 1272 paint.setRenderHint(QPainter::Antialiasing, false);
Chris@8 1273 paint.save();
Chris@36 1274 (*i)->paint(this, paint, cacheRect);
Chris@8 1275 paint.restore();
Chris@0 1276 }
Chris@9 1277
Chris@9 1278 if (haveSelections && selectionCacheable) {
Chris@9 1279 drawSelections(paint);
Chris@9 1280 m_selectionCached = repaintCache;
Chris@9 1281 selectionDrawn = true;
Chris@9 1282 }
Chris@0 1283
Chris@0 1284 paint.end();
Chris@0 1285
Chris@0 1286 if (repaintCache) {
Chris@0 1287 cacheRect |= (e ? e->rect() : rect());
Chris@0 1288 paint.begin(this);
Chris@0 1289 paint.drawPixmap(cacheRect, *m_cache, cacheRect);
Chris@0 1290 paint.end();
Chris@0 1291 }
Chris@0 1292 }
Chris@0 1293
Chris@0 1294 // Now non-cacheable items. We always need to redraw the
Chris@0 1295 // non-cacheable items across at least the area we drew of the
Chris@0 1296 // cacheable items.
Chris@0 1297
Chris@0 1298 nonCacheRect |= cacheRect;
Chris@0 1299
Chris@0 1300 paint.begin(this);
Chris@0 1301 paint.setClipRect(nonCacheRect);
Chris@0 1302
Chris@0 1303 if (scrollables.empty()) {
Chris@0 1304 if (hasLightBackground()) {
Chris@0 1305 paint.setPen(Qt::white);
Chris@0 1306 paint.setBrush(Qt::white);
Chris@0 1307 } else {
Chris@0 1308 paint.setPen(Qt::black);
Chris@0 1309 paint.setBrush(Qt::black);
Chris@0 1310 }
Chris@0 1311 paint.drawRect(nonCacheRect);
Chris@0 1312 }
Chris@0 1313
Chris@0 1314 paint.setPen(Qt::black);
Chris@0 1315 paint.setBrush(Qt::NoBrush);
Chris@0 1316
Chris@0 1317 for (LayerList::iterator i = nonScrollables.begin(); i != nonScrollables.end(); ++i) {
Chris@36 1318 (*i)->paint(this, paint, nonCacheRect);
Chris@0 1319 }
Chris@0 1320
Chris@9 1321 paint.end();
Chris@8 1322
Chris@9 1323 paint.begin(this);
Chris@9 1324 if (e) paint.setClipRect(e->rect());
Chris@9 1325 if (!m_selectionCached) {
Chris@9 1326 drawSelections(paint);
Chris@8 1327 }
Chris@0 1328 paint.end();
Chris@0 1329
Chris@0 1330 if (m_followPlay != PlaybackScrollContinuous) {
Chris@0 1331
Chris@0 1332 paint.begin(this);
Chris@0 1333
Chris@0 1334 if (long(m_playPointerFrame) > getStartFrame() &&
Chris@0 1335 m_playPointerFrame < getEndFrame()) {
Chris@0 1336
Chris@15 1337 int playx = getXForFrame(m_playPointerFrame);
Chris@0 1338
Chris@0 1339 paint.setPen(Qt::black);
Chris@0 1340 paint.drawLine(playx - 1, 0, playx - 1, height() - 1);
Chris@0 1341 paint.drawLine(playx + 1, 0, playx + 1, height() - 1);
Chris@0 1342 paint.drawPoint(playx, 0);
Chris@0 1343 paint.drawPoint(playx, height() - 1);
Chris@0 1344 paint.setPen(Qt::white);
Chris@0 1345 paint.drawLine(playx, 1, playx, height() - 2);
Chris@0 1346 }
Chris@0 1347
Chris@0 1348 paint.end();
Chris@0 1349 }
Chris@0 1350
Chris@0 1351 QFrame::paintEvent(e);
Chris@0 1352 }
Chris@0 1353
Chris@9 1354 void
Chris@9 1355 View::drawSelections(QPainter &paint)
Chris@9 1356 {
Chris@24 1357 MultiSelection::SelectionList selections;
Chris@9 1358
Chris@9 1359 if (m_manager) {
Chris@9 1360 selections = m_manager->getSelections();
Chris@9 1361 if (m_manager->haveInProgressSelection()) {
Chris@9 1362 bool exclusive;
Chris@9 1363 Selection inProgressSelection =
Chris@9 1364 m_manager->getInProgressSelection(exclusive);
Chris@9 1365 if (exclusive) selections.clear();
Chris@9 1366 selections.insert(inProgressSelection);
Chris@9 1367 }
Chris@9 1368 }
Chris@9 1369
Chris@9 1370 paint.save();
Chris@9 1371 paint.setBrush(QColor(150, 150, 255, 80));
Chris@9 1372
Chris@10 1373 int sampleRate = getModelsSampleRate();
Chris@10 1374
Chris@34 1375 QPoint localPos;
Chris@34 1376 long illuminateFrame = -1;
Chris@34 1377 bool closeToLeft, closeToRight;
Chris@34 1378
Chris@34 1379 if (shouldIlluminateLocalSelection(localPos, closeToLeft, closeToRight)) {
Chris@34 1380 illuminateFrame = getFrameForX(localPos.x());
Chris@34 1381 }
Chris@34 1382
Chris@10 1383 const QFontMetrics &metrics = paint.fontMetrics();
Chris@10 1384
Chris@24 1385 for (MultiSelection::SelectionList::iterator i = selections.begin();
Chris@9 1386 i != selections.end(); ++i) {
Chris@9 1387
Chris@15 1388 int p0 = getXForFrame(i->getStartFrame());
Chris@15 1389 int p1 = getXForFrame(i->getEndFrame());
Chris@9 1390
Chris@10 1391 if (p1 < 0 || p0 > width()) continue;
Chris@9 1392
Chris@9 1393 #ifdef DEBUG_VIEW_WIDGET_PAINT
Chris@9 1394 std::cerr << "View::drawSelections: " << p0 << ",-1 [" << (p1-p0) << "x" << (height()+1) << "]" << std::endl;
Chris@9 1395 #endif
Chris@9 1396
Chris@34 1397 bool illuminateThis =
Chris@34 1398 (illuminateFrame >= 0 && i->contains(illuminateFrame));
Chris@34 1399
Chris@34 1400 paint.setPen(QColor(150, 150, 255));
Chris@9 1401 paint.drawRect(p0, -1, p1 - p0, height() + 1);
Chris@10 1402
Chris@34 1403 if (illuminateThis) {
Chris@34 1404 paint.save();
Chris@34 1405 if (hasLightBackground()) {
Chris@34 1406 paint.setPen(QPen(Qt::black, 2));
Chris@34 1407 } else {
Chris@34 1408 paint.setPen(QPen(Qt::white, 2));
Chris@34 1409 }
Chris@34 1410 if (closeToLeft) {
Chris@34 1411 paint.drawLine(p0, 1, p1, 1);
Chris@34 1412 paint.drawLine(p0, 0, p0, height());
Chris@34 1413 paint.drawLine(p0, height() - 1, p1, height() - 1);
Chris@34 1414 } else if (closeToRight) {
Chris@34 1415 paint.drawLine(p0, 1, p1, 1);
Chris@34 1416 paint.drawLine(p1, 0, p1, height());
Chris@34 1417 paint.drawLine(p0, height() - 1, p1, height() - 1);
Chris@34 1418 } else {
Chris@34 1419 paint.setBrush(Qt::NoBrush);
Chris@34 1420 paint.drawRect(p0, 1, p1 - p0, height() - 2);
Chris@34 1421 }
Chris@34 1422 paint.restore();
Chris@34 1423 }
Chris@34 1424
Chris@110 1425 if (sampleRate && shouldLabelSelections() && m_manager &&
Chris@110 1426 m_manager->getOverlayMode() != ViewManager::NoOverlays) {
Chris@10 1427
Chris@10 1428 QString startText = QString("%1 / %2")
Chris@10 1429 .arg(QString::fromStdString
Chris@10 1430 (RealTime::frame2RealTime
Chris@10 1431 (i->getStartFrame(), sampleRate).toText(true)))
Chris@10 1432 .arg(i->getStartFrame());
Chris@10 1433
Chris@10 1434 QString endText = QString(" %1 / %2")
Chris@10 1435 .arg(QString::fromStdString
Chris@10 1436 (RealTime::frame2RealTime
Chris@10 1437 (i->getEndFrame(), sampleRate).toText(true)))
Chris@10 1438 .arg(i->getEndFrame());
Chris@10 1439
Chris@10 1440 QString durationText = QString("(%1 / %2) ")
Chris@10 1441 .arg(QString::fromStdString
Chris@10 1442 (RealTime::frame2RealTime
Chris@10 1443 (i->getEndFrame() - i->getStartFrame(), sampleRate)
Chris@10 1444 .toText(true)))
Chris@10 1445 .arg(i->getEndFrame() - i->getStartFrame());
Chris@10 1446
Chris@10 1447 int sw = metrics.width(startText),
Chris@10 1448 ew = metrics.width(endText),
Chris@10 1449 dw = metrics.width(durationText);
Chris@10 1450
Chris@10 1451 int sy = metrics.ascent() + metrics.height() + 4;
Chris@10 1452 int ey = sy;
Chris@10 1453 int dy = sy + metrics.height();
Chris@10 1454
Chris@10 1455 int sx = p0 + 2;
Chris@10 1456 int ex = sx;
Chris@10 1457 int dx = sx;
Chris@10 1458
Chris@10 1459 if (sw + ew > (p1 - p0)) {
Chris@10 1460 ey += metrics.height();
Chris@10 1461 dy += metrics.height();
Chris@10 1462 }
Chris@10 1463
Chris@10 1464 if (ew < (p1 - p0)) {
Chris@10 1465 ex = p1 - 2 - ew;
Chris@10 1466 }
Chris@10 1467
Chris@10 1468 if (dw < (p1 - p0)) {
Chris@10 1469 dx = p1 - 2 - dw;
Chris@10 1470 }
Chris@10 1471
Chris@10 1472 paint.drawText(sx, sy, startText);
Chris@10 1473 paint.drawText(ex, ey, endText);
Chris@10 1474 paint.drawText(dx, dy, durationText);
Chris@10 1475 }
Chris@9 1476 }
Chris@9 1477
Chris@9 1478 paint.restore();
Chris@9 1479 }
Chris@9 1480
Chris@3 1481 QString
Chris@3 1482 View::toXmlString(QString indent, QString extraAttributes) const
Chris@3 1483 {
Chris@3 1484 QString s;
Chris@3 1485
Chris@3 1486 s += indent;
Chris@3 1487
Chris@3 1488 s += QString("<view "
Chris@3 1489 "centre=\"%1\" "
Chris@3 1490 "zoom=\"%2\" "
Chris@3 1491 "followPan=\"%3\" "
Chris@3 1492 "followZoom=\"%4\" "
Chris@3 1493 "tracking=\"%5\" "
Chris@3 1494 "light=\"%6\" %7>\n")
Chris@3 1495 .arg(m_centreFrame)
Chris@3 1496 .arg(m_zoomLevel)
Chris@3 1497 .arg(m_followPan)
Chris@3 1498 .arg(m_followZoom)
Chris@3 1499 .arg(m_followPlay == PlaybackScrollContinuous ? "scroll" :
Chris@3 1500 m_followPlay == PlaybackScrollPage ? "page" : "ignore")
Chris@3 1501 .arg(m_lightBackground)
Chris@3 1502 .arg(extraAttributes);
Chris@3 1503
Chris@3 1504 for (size_t i = 0; i < m_layers.size(); ++i) {
Chris@3 1505 s += m_layers[i]->toXmlString(indent + " ");
Chris@3 1506 }
Chris@3 1507
Chris@4 1508 s += indent + "</view>\n";
Chris@3 1509
Chris@3 1510 return s;
Chris@3 1511 }
Chris@3 1512
Chris@29 1513 ViewPropertyContainer::ViewPropertyContainer(View *v) :
Chris@29 1514 m_v(v)
Chris@29 1515 {
Chris@29 1516 connect(m_v, SIGNAL(propertyChanged(PropertyName)),
Chris@29 1517 this, SIGNAL(propertyChanged(PropertyName)));
Chris@29 1518 }
Chris@3 1519
Chris@0 1520 #ifdef INCLUDE_MOCFILES
Chris@0 1521 #include "View.moc.cpp"
Chris@0 1522 #endif
Chris@0 1523