annotate view/Pane.cpp @ 1534:bfd8b22fd67c

Fix #1904 Scrolling colour 3d plot does not always work when in View normalisation mode. We shouldn't imagine we've just invalidated the cache if the truth is that we've only just created the renderer
author Chris Cannam
date Wed, 09 Oct 2019 13:45:17 +0100
parents 1819978526f9
children bfacecf7ea7e
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@264 7 This file copyright 2006-2007 Chris Cannam and QMUL.
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 "Pane.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/RealTime.h"
Chris@127 21 #include "base/Profiler.h"
Chris@128 22 #include "ViewManager.h"
Chris@376 23 #include "widgets/CommandHistory.h"
Chris@376 24 #include "widgets/TextAbbrev.h"
Chris@1193 25 #include "widgets/IconLoader.h"
Chris@338 26 #include "base/Preferences.h"
Chris@127 27 #include "layer/WaveformLayer.h"
Chris@928 28 #include "layer/TimeRulerLayer.h"
Chris@1078 29 #include "layer/PaintAssistant.h"
Chris@127 30
gyorgyf@646 31 // GF: added so we can propagate the mouse move event to the note layer for context handling.
gyorgyf@646 32 #include "layer/LayerFactory.h"
gyorgyf@646 33 #include "layer/FlexiNoteLayer.h"
gyorgyf@646 34
gyorgyf@646 35
Chris@326 36 //!!! ugh
Chris@326 37 #include "data/model/WaveFileModel.h"
Chris@1497 38 #include "data/model/AlignmentModel.h"
Chris@326 39
Chris@127 40 #include <QPaintEvent>
Chris@127 41 #include <QPainter>
Chris@257 42 #include <QBitmap>
Chris@312 43 #include <QDragEnterEvent>
Chris@312 44 #include <QDropEvent>
Chris@257 45 #include <QCursor>
Chris@316 46 #include <QTextStream>
Chris@616 47 #include <QMimeData>
Chris@802 48 #include <QApplication>
Chris@316 49
Chris@127 50 #include <iostream>
Chris@127 51 #include <cmath>
Chris@127 52
Chris@133 53 //!!! for HUD -- pull out into a separate class
Chris@133 54 #include <QFrame>
Chris@133 55 #include <QGridLayout>
Chris@133 56 #include <QPushButton>
Chris@133 57 #include "widgets/Thumbwheel.h"
Chris@172 58 #include "widgets/Panner.h"
Chris@188 59 #include "widgets/RangeInputDialog.h"
Chris@189 60 #include "widgets/NotifyingPushButton.h"
Chris@133 61
Chris@282 62 #include "widgets/KeyReference.h" //!!! should probably split KeyReference into a data class in base and another that shows the widget
Chris@282 63
Chris@363 64 //#define DEBUG_PANE
Chris@363 65
Chris@1408 66 QCursor *Pane::m_measureCursor1 = nullptr;
Chris@1408 67 QCursor *Pane::m_measureCursor2 = nullptr;
Chris@262 68
Chris@127 69 Pane::Pane(QWidget *w) :
Chris@127 70 View(w, true),
Chris@127 71 m_identifyFeatures(false),
Chris@127 72 m_clickedInRange(false),
Chris@127 73 m_shiftPressed(false),
Chris@127 74 m_ctrlPressed(false),
Chris@510 75 m_altPressed(false),
Chris@127 76 m_navigating(false),
Chris@127 77 m_resizing(false),
Chris@343 78 m_editing(false),
Chris@343 79 m_releasing(false),
Chris@133 80 m_centreLineVisible(true),
Chris@222 81 m_scaleWidth(0),
Chris@826 82 m_pendingWheelAngle(0),
Chris@1408 83 m_headsUpDisplay(nullptr),
Chris@1408 84 m_vpan(nullptr),
Chris@1408 85 m_hthumb(nullptr),
Chris@1408 86 m_vthumb(nullptr),
Chris@1408 87 m_reset(nullptr),
Chris@802 88 m_mouseInWidget(false),
Chris@802 89 m_playbackFrameMoveScheduled(false),
Chris@802 90 m_playbackFrameMoveTo(0)
Chris@127 91 {
Chris@127 92 setObjectName("Pane");
Chris@127 93 setMouseTracking(true);
Chris@312 94 setAcceptDrops(true);
Chris@133 95
Chris@133 96 updateHeadsUpDisplay();
Chris@456 97
Chris@730 98 connect(this, SIGNAL(regionOutlined(QRect)),
Chris@730 99 this, SLOT(zoomToRegion(QRect)));
Chris@730 100
Chris@728 101 cerr << "Pane::Pane(" << this << ") returning" << endl;
Chris@133 102 }
Chris@133 103
Chris@133 104 void
Chris@133 105 Pane::updateHeadsUpDisplay()
Chris@133 106 {
Chris@382 107 Profiler profiler("Pane::updateHeadsUpDisplay");
Chris@187 108
Chris@192 109 if (!isVisible()) return;
Chris@192 110
Chris@1408 111 Layer *layer = nullptr;
Chris@1362 112 if (getLayerCount() > 0) {
Chris@1362 113 layer = getLayer(getLayerCount() - 1);
Chris@1362 114 }
Chris@188 115
Chris@133 116 if (!m_headsUpDisplay) {
Chris@133 117
Chris@133 118 m_headsUpDisplay = new QFrame(this);
Chris@133 119
Chris@133 120 QGridLayout *layout = new QGridLayout;
Chris@133 121 layout->setMargin(0);
Chris@133 122 layout->setSpacing(0);
Chris@133 123 m_headsUpDisplay->setLayout(layout);
Chris@133 124
Chris@133 125 m_hthumb = new Thumbwheel(Qt::Horizontal);
Chris@187 126 m_hthumb->setObjectName(tr("Horizontal Zoom"));
Chris@260 127 m_hthumb->setCursor(Qt::ArrowCursor);
Chris@173 128 layout->addWidget(m_hthumb, 1, 0, 1, 2);
Chris@1193 129 m_hthumb->setFixedWidth(m_manager->scalePixelSize(70));
Chris@1193 130 m_hthumb->setFixedHeight(m_manager->scalePixelSize(16));
Chris@133 131 m_hthumb->setDefaultValue(0);
Chris@908 132 m_hthumb->setSpeed(0.6f);
Chris@133 133 connect(m_hthumb, SIGNAL(valueChanged(int)), this,
Chris@133 134 SLOT(horizontalThumbwheelMoved(int)));
Chris@189 135 connect(m_hthumb, SIGNAL(mouseEntered()), this, SLOT(mouseEnteredWidget()));
Chris@189 136 connect(m_hthumb, SIGNAL(mouseLeft()), this, SLOT(mouseLeftWidget()));
Chris@172 137
Chris@172 138 m_vpan = new Panner;
Chris@260 139 m_vpan->setCursor(Qt::ArrowCursor);
Chris@172 140 layout->addWidget(m_vpan, 0, 1);
Chris@1193 141 m_vpan->setFixedWidth(m_manager->scalePixelSize(12));
Chris@1193 142 m_vpan->setFixedHeight(m_manager->scalePixelSize(70));
Chris@174 143 m_vpan->setAlpha(80, 130);
Chris@174 144 connect(m_vpan, SIGNAL(rectExtentsChanged(float, float, float, float)),
Chris@174 145 this, SLOT(verticalPannerMoved(float, float, float, float)));
Chris@188 146 connect(m_vpan, SIGNAL(doubleClicked()),
Chris@188 147 this, SLOT(editVerticalPannerExtents()));
Chris@189 148 connect(m_vpan, SIGNAL(mouseEntered()), this, SLOT(mouseEnteredWidget()));
Chris@189 149 connect(m_vpan, SIGNAL(mouseLeft()), this, SLOT(mouseLeftWidget()));
Chris@172 150
Chris@133 151 m_vthumb = new Thumbwheel(Qt::Vertical);
Chris@187 152 m_vthumb->setObjectName(tr("Vertical Zoom"));
Chris@260 153 m_vthumb->setCursor(Qt::ArrowCursor);
Chris@172 154 layout->addWidget(m_vthumb, 0, 2);
Chris@1193 155 m_vthumb->setFixedWidth(m_manager->scalePixelSize(16));
Chris@1193 156 m_vthumb->setFixedHeight(m_manager->scalePixelSize(70));
Chris@133 157 connect(m_vthumb, SIGNAL(valueChanged(int)), this,
Chris@133 158 SLOT(verticalThumbwheelMoved(int)));
Chris@189 159 connect(m_vthumb, SIGNAL(mouseEntered()), this, SLOT(mouseEnteredWidget()));
Chris@189 160 connect(m_vthumb, SIGNAL(mouseLeft()), this, SLOT(mouseLeftWidget()));
Chris@133 161
Chris@188 162 if (layer) {
Chris@188 163 RangeMapper *rm = layer->getNewVerticalZoomRangeMapper();
Chris@188 164 if (rm) m_vthumb->setRangeMapper(rm);
Chris@188 165 }
Chris@188 166
Chris@189 167 m_reset = new NotifyingPushButton;
Chris@713 168 m_reset->setFlat(true);
Chris@260 169 m_reset->setCursor(Qt::ArrowCursor);
Chris@1193 170 m_reset->setFixedHeight(m_manager->scalePixelSize(16));
Chris@1193 171 m_reset->setFixedWidth(m_manager->scalePixelSize(16));
Chris@1193 172 m_reset->setIcon(IconLoader().load("zoom-reset"));
Chris@501 173 m_reset->setToolTip(tr("Reset zoom to default"));
Chris@189 174 layout->addWidget(m_reset, 1, 2);
Chris@492 175
Chris@492 176 layout->setColumnStretch(0, 20);
Chris@492 177
Chris@189 178 connect(m_reset, SIGNAL(clicked()), m_hthumb, SLOT(resetToDefault()));
Chris@189 179 connect(m_reset, SIGNAL(clicked()), m_vthumb, SLOT(resetToDefault()));
Chris@189 180 connect(m_reset, SIGNAL(clicked()), m_vpan, SLOT(resetToDefault()));
Chris@189 181 connect(m_reset, SIGNAL(mouseEntered()), this, SLOT(mouseEnteredWidget()));
Chris@189 182 connect(m_reset, SIGNAL(mouseLeft()), this, SLOT(mouseLeftWidget()));
Chris@133 183 }
Chris@133 184
Chris@1354 185 int count = countZoomLevels();
Chris@1354 186 int current = getZoomLevelIndex(getZoomLevel());
Chris@1354 187
Chris@1354 188 m_hthumb->setMinimumValue(1);
Chris@133 189 m_hthumb->setMaximumValue(count);
Chris@133 190 m_hthumb->setValue(count - current);
Chris@133 191
Chris@1354 192 if (m_hthumb->getDefaultValue() == 0) {
Chris@133 193 m_hthumb->setDefaultValue(count - current);
Chris@133 194 }
Chris@133 195
Chris@204 196 bool haveVThumb = false;
Chris@204 197
Chris@133 198 if (layer) {
Chris@133 199 int defaultStep = 0;
Chris@133 200 int max = layer->getVerticalZoomSteps(defaultStep);
Chris@133 201 if (max == 0) {
Chris@133 202 m_vthumb->hide();
Chris@133 203 } else {
Chris@204 204 haveVThumb = true;
Chris@133 205 m_vthumb->show();
Chris@187 206 m_vthumb->blockSignals(true);
Chris@133 207 m_vthumb->setMinimumValue(0);
Chris@133 208 m_vthumb->setMaximumValue(max);
Chris@133 209 m_vthumb->setDefaultValue(defaultStep);
Chris@133 210 m_vthumb->setValue(layer->getCurrentVerticalZoomStep());
Chris@187 211 m_vthumb->blockSignals(false);
Chris@135 212
Chris@682 213 // cerr << "Vertical thumbwheel: min 0, max " << max
Chris@205 214 // << ", default " << defaultStep << ", value "
Chris@682 215 // << m_vthumb->getValue() << endl;
Chris@135 216
Chris@133 217 }
Chris@133 218 }
Chris@133 219
Chris@174 220 updateVerticalPanner();
Chris@174 221
Chris@133 222 if (m_manager && m_manager->getZoomWheelsEnabled() &&
Chris@1193 223 width() > m_manager->scalePixelSize(120) &&
Chris@1193 224 height() > m_manager->scalePixelSize(100)) {
Chris@165 225 if (!m_headsUpDisplay->isVisible()) {
Chris@165 226 m_headsUpDisplay->show();
Chris@165 227 }
Chris@1193 228 int shift = m_manager->scalePixelSize(86);
Chris@204 229 if (haveVThumb) {
Chris@204 230 m_headsUpDisplay->setFixedHeight(m_vthumb->height() + m_hthumb->height());
Chris@1193 231 m_headsUpDisplay->move(width() - shift, height() - shift);
Chris@133 232 } else {
Chris@204 233 m_headsUpDisplay->setFixedHeight(m_hthumb->height());
Chris@1193 234 m_headsUpDisplay->move(width() - shift,
Chris@1193 235 height() - m_manager->scalePixelSize(16));
Chris@133 236 }
Chris@133 237 } else {
Chris@133 238 m_headsUpDisplay->hide();
Chris@133 239 }
Chris@127 240 }
Chris@127 241
Chris@174 242 void
Chris@174 243 Pane::updateVerticalPanner()
Chris@174 244 {
Chris@174 245 if (!m_vpan || !m_manager || !m_manager->getZoomWheelsEnabled()) return;
Chris@174 246
Chris@208 247 // In principle we should show or hide the panner on the basis of
Chris@208 248 // whether the top layer has adjustable display extents, and we do
Chris@208 249 // that below. However, we have no basis for layout of the panner
Chris@208 250 // if the vertical scroll wheel is not also present. So if we
Chris@208 251 // have no vertical scroll wheel, we should remove the panner as
Chris@208 252 // well. Ideally any layer that implements display extents should
Chris@208 253 // implement vertical zoom steps as well, but they don't all at
Chris@208 254 // the moment.
Chris@208 255
Chris@1408 256 Layer *layer = nullptr;
Chris@208 257 if (getLayerCount() > 0) layer = getLayer(getLayerCount() - 1);
Chris@208 258 int discard;
Chris@208 259 if (layer && layer->getVerticalZoomSteps(discard) == 0) {
Chris@208 260 m_vpan->hide();
Chris@208 261 return;
Chris@208 262 }
Chris@208 263
Chris@908 264 double vmin, vmax, dmin, dmax;
Chris@174 265 if (getTopLayerDisplayExtents(vmin, vmax, dmin, dmax) && vmax != vmin) {
Chris@908 266 double y0 = (dmin - vmin) / (vmax - vmin);
Chris@908 267 double y1 = (dmax - vmin) / (vmax - vmin);
Chris@174 268 m_vpan->blockSignals(true);
Chris@908 269 m_vpan->setRectExtents(0, float(1.0 - y1), 1, float(y1 - y0));
Chris@174 270 m_vpan->blockSignals(false);
Chris@174 271 m_vpan->show();
Chris@174 272 } else {
Chris@174 273 m_vpan->hide();
Chris@174 274 }
Chris@174 275 }
Chris@174 276
Chris@127 277 bool
Chris@127 278 Pane::shouldIlluminateLocalFeatures(const Layer *layer, QPoint &pos) const
Chris@127 279 {
Chris@127 280 QPoint discard;
Chris@127 281 bool b0, b1;
Chris@127 282
Chris@711 283 if (m_manager && m_manager->getToolModeFor(this) == ViewManager::MeasureMode) {
Chris@262 284 return false;
Chris@262 285 }
matthiasm@651 286
Chris@326 287 if (m_manager && !m_manager->shouldIlluminateLocalFeatures()) {
Chris@326 288 return false;
Chris@326 289 }
Chris@326 290
Chris@840 291 if (layer == getInteractionLayer() &&
Chris@753 292 !shouldIlluminateLocalSelection(discard, b0, b1)) {
Chris@753 293
Chris@753 294 pos = m_identifyPoint;
Chris@753 295 return m_identifyFeatures;
Chris@127 296 }
Chris@127 297
Chris@127 298 return false;
Chris@127 299 }
Chris@127 300
Chris@127 301 bool
Chris@127 302 Pane::shouldIlluminateLocalSelection(QPoint &pos,
Chris@1391 303 bool &closeToLeft,
Chris@1391 304 bool &closeToRight) const
Chris@127 305 {
Chris@127 306 if (m_identifyFeatures &&
Chris@731 307 m_manager &&
Chris@731 308 m_manager->getToolModeFor(this) == ViewManager::EditMode &&
Chris@731 309 !m_manager->getSelections().empty() &&
Chris@731 310 !selectionIsBeingEdited()) {
gyorgyf@645 311
Chris@753 312 Selection s(getSelectionAt(m_identifyPoint.x(),
Chris@753 313 closeToLeft, closeToRight));
Chris@753 314
Chris@753 315 if (!s.isEmpty()) {
Chris@840 316 if (getInteractionLayer() && getInteractionLayer()->isLayerEditable()) {
Chris@753 317
Chris@753 318 pos = m_identifyPoint;
Chris@753 319 return true;
Chris@753 320 }
gyorgyf@645 321 }
gyorgyf@645 322 }
Chris@127 323
Chris@127 324 return false;
Chris@127 325 }
Chris@127 326
Chris@127 327 bool
Chris@127 328 Pane::selectionIsBeingEdited() const
Chris@127 329 {
Chris@127 330 if (!m_editingSelection.isEmpty()) {
Chris@966 331 if (m_mousePos != m_clickPos &&
Chris@966 332 getFrameForX(m_mousePos.x()) != getFrameForX(m_clickPos.x())) {
Chris@966 333 return true;
Chris@966 334 }
Chris@127 335 }
Chris@127 336 return false;
Chris@127 337 }
Chris@127 338
Chris@127 339 void
Chris@127 340 Pane::setCentreLineVisible(bool visible)
Chris@127 341 {
Chris@127 342 m_centreLineVisible = visible;
Chris@127 343 update();
Chris@127 344 }
Chris@127 345
Chris@127 346 void
Chris@127 347 Pane::paintEvent(QPaintEvent *e)
Chris@127 348 {
Chris@127 349 // Profiler profiler("Pane::paintEvent", true);
Chris@127 350
Chris@127 351 QPainter paint;
Chris@127 352
Chris@127 353 QRect r(rect());
Chris@261 354 if (e) r = e->rect();
Chris@127 355
Chris@127 356 View::paintEvent(e);
Chris@127 357
Chris@127 358 paint.begin(this);
Chris@339 359 setPaintFont(paint);
Chris@338 360
Chris@261 361 if (e) paint.setClipRect(r);
Chris@127 362
Chris@854 363 ViewManager::ToolMode toolMode = ViewManager::NavigateMode;
Chris@854 364 if (m_manager) toolMode = m_manager->getToolModeFor(this);
Chris@259 365
Chris@1239 366 // Locate some relevant layers and models
Chris@1239 367
Chris@268 368 Layer *topLayer = getTopLayer();
Chris@277 369 bool haveSomeTimeXAxis = false;
Chris@268 370
Chris@1481 371 ModelId waveformModelId; // just for reporting purposes
Chris@1481 372 ModelId workModelId;
Chris@1481 373
Chris@1481 374 for (LayerList::iterator vi = m_layerStack.end();
Chris@1481 375 vi != m_layerStack.begin(); ) {
Chris@1481 376
Chris@127 377 --vi;
Chris@1481 378
Chris@277 379 if (!haveSomeTimeXAxis && (*vi)->hasTimeXAxis()) {
Chris@277 380 haveSomeTimeXAxis = true;
Chris@277 381 }
Chris@1481 382
Chris@1481 383 ModelId modelId = (*vi)->getModel();
Chris@1481 384 if (!modelId.isNone()) {
Chris@1475 385 if (dynamic_cast<WaveformLayer *>(*vi)) {
Chris@1481 386 waveformModelId = modelId;
Chris@1481 387 workModelId = modelId;
Chris@1475 388 } else {
Chris@1481 389 if (ModelById::isa<WaveFileModel>(modelId)) {
Chris@1481 390 workModelId = modelId;
Chris@1481 391 } else {
Chris@1490 392 ModelId sourceId = (*vi)->getSourceModel();
Chris@1490 393 if (ModelById::isa<WaveFileModel>(sourceId)) {
Chris@1490 394 workModelId = sourceId;
Chris@1481 395 }
Chris@1475 396 }
Chris@326 397 }
Chris@127 398 }
Chris@326 399
Chris@1481 400 if (!waveformModelId.isNone() &&
Chris@1481 401 !workModelId.isNone() &&
Chris@1481 402 haveSomeTimeXAxis) {
Chris@1481 403 break;
Chris@1481 404 }
Chris@258 405 }
Chris@127 406
Chris@1239 407 // Block off left and right extents so we can see where the main model ends
Chris@1239 408
Chris@1481 409 if (!workModelId.isNone() && hasTopLayerTimeXAxis()) {
Chris@1481 410 drawModelTimeExtents(r, paint, workModelId);
Chris@759 411 }
Chris@759 412
Chris@1239 413 // Crosshairs for mouse movement in measure mode
Chris@1239 414
Chris@1239 415 if (m_manager &&
Chris@1239 416 m_mouseInWidget &&
Chris@1239 417 toolMode == ViewManager::MeasureMode) {
Chris@1239 418
Chris@1239 419 for (LayerList::iterator vi = m_layerStack.end(); vi != m_layerStack.begin(); ) {
Chris@1239 420 --vi;
Chris@1239 421
Chris@1239 422 std::vector<QRect> crosshairExtents;
Chris@1239 423
Chris@1239 424 if ((*vi)->getCrosshairExtents(this, paint, m_identifyPoint,
Chris@1239 425 crosshairExtents)) {
Chris@1239 426 (*vi)->paintCrosshairs(this, paint, m_identifyPoint);
Chris@1239 427 break;
Chris@1239 428 } else if ((*vi)->isLayerOpaque()) {
Chris@1239 429 break;
Chris@1239 430 }
Chris@1239 431 }
Chris@1239 432 }
Chris@1239 433
Chris@1239 434 // Scale width will be set implicitly during drawVerticalScale call
Chris@1239 435 m_scaleWidth = 0;
Chris@1239 436
Chris@261 437 if (m_manager && m_manager->shouldShowVerticalScale() && topLayer) {
Chris@261 438 drawVerticalScale(r, topLayer, paint);
Chris@261 439 }
Chris@261 440
Chris@1239 441 // Feature description: the box in top-right showing values from
Chris@1239 442 // the nearest feature to the mouse
Chris@1239 443
Chris@326 444 if (m_identifyFeatures &&
Chris@326 445 m_manager && m_manager->shouldIlluminateLocalFeatures() &&
Chris@326 446 topLayer) {
Chris@261 447 drawFeatureDescription(topLayer, paint);
Chris@261 448 }
Chris@261 449
Chris@908 450 sv_samplerate_t sampleRate = getModelsSampleRate();
Chris@261 451 paint.setBrush(Qt::NoBrush);
Chris@261 452
Chris@261 453 if (m_centreLineVisible &&
Chris@261 454 m_manager &&
Chris@261 455 m_manager->shouldShowCentreLine()) {
Chris@277 456 drawCentreLine(sampleRate, paint, !haveSomeTimeXAxis);
Chris@261 457 }
Chris@261 458
Chris@261 459 paint.setPen(QColor(50, 50, 50));
Chris@261 460
Chris@1481 461 if (!waveformModelId.isNone() &&
Chris@854 462 sampleRate &&
Chris@261 463 m_manager &&
Chris@261 464 m_manager->shouldShowDuration()) {
Chris@1481 465 drawDurationAndRate(r, waveformModelId, sampleRate, paint);
Chris@261 466 }
Chris@261 467
Chris@326 468 bool haveWorkTitle = false;
Chris@326 469
Chris@1481 470 if (!workModelId.isNone() &&
Chris@326 471 m_manager &&
Chris@326 472 m_manager->shouldShowWorkTitle()) {
Chris@1481 473 drawWorkTitle(r, paint, workModelId);
Chris@326 474 haveWorkTitle = true;
Chris@326 475 }
Chris@326 476
Chris@1481 477 if (!workModelId.isNone() &&
Chris@320 478 m_manager &&
Chris@320 479 m_manager->getAlignMode()) {
Chris@1481 480 drawAlignmentStatus(r, paint, workModelId, haveWorkTitle);
Chris@320 481 }
Chris@320 482
Chris@261 483 if (m_manager &&
Chris@261 484 m_manager->shouldShowLayerNames()) {
Chris@261 485 drawLayerNames(r, paint);
Chris@261 486 }
Chris@261 487
Chris@1239 488 // The blue box that is shown when you ctrl-click in navigate mode
Chris@1239 489 // to define a zoom region
Chris@1239 490
Chris@262 491 if (m_shiftPressed && m_clickedInRange &&
Chris@283 492 (toolMode == ViewManager::NavigateMode || m_navigating)) {
Chris@261 493
Chris@261 494 //!!! be nice if this looked a bit more in keeping with the
Chris@261 495 //selection block
Chris@262 496
Chris@262 497 paint.setPen(Qt::blue);
Chris@262 498 //!!! shouldn't use clickPos -- needs to use a clicked frame
Chris@262 499 paint.drawRect(m_clickPos.x(), m_clickPos.y(),
Chris@262 500 m_mousePos.x() - m_clickPos.x(),
Chris@262 501 m_mousePos.y() - m_clickPos.y());
Chris@261 502
Chris@262 503 }
Chris@261 504
Chris@266 505 if (toolMode == ViewManager::MeasureMode && topLayer) {
Chris@272 506 bool showFocus = false;
Chris@272 507 if (!m_manager || !m_manager->isPlaying()) showFocus = true;
Chris@272 508 topLayer->paintMeasurementRects(this, paint, showFocus, m_identifyPoint);
Chris@261 509 }
Chris@261 510
Chris@261 511 if (selectionIsBeingEdited()) {
Chris@261 512 drawEditingSelection(paint);
Chris@261 513 }
Chris@261 514
Chris@261 515 paint.end();
Chris@261 516 }
Chris@261 517
Chris@806 518 int
Chris@276 519 Pane::getVerticalScaleWidth() const
Chris@276 520 {
Chris@276 521 if (m_scaleWidth > 0) return m_scaleWidth;
Chris@276 522 else return 0;
Chris@276 523 }
Chris@276 524
Chris@261 525 void
Chris@261 526 Pane::drawVerticalScale(QRect r, Layer *topLayer, QPainter &paint)
Chris@261 527 {
Chris@1408 528 Layer *scaleLayer = nullptr;
Chris@258 529
Chris@1226 530 // cerr << "Pane::drawVerticalScale[" << this << "]" << endl;
Chris@1226 531
Chris@908 532 double min, max;
Chris@261 533 bool log;
Chris@261 534 QString unit;
Chris@258 535
Chris@1467 536 // If the top layer has no scale and reports no display extents,
Chris@1467 537 // but does report a unit, then the scale should be drawn from any
Chris@1467 538 // (visible) underlying layer with a scale and that unit. If the
Chris@1467 539 // top layer has no scale and no value extents at all, then the
Chris@1467 540 // scale should be drawn from any (visible) underlying layer with
Chris@1467 541 // a scale regardless of unit.
Chris@258 542
Chris@607 543 int sw = topLayer->getVerticalScaleWidth
Chris@607 544 (this, m_manager->shouldShowVerticalColourScale(), paint);
Chris@1226 545
Chris@261 546 if (sw > 0) {
Chris@261 547 scaleLayer = topLayer;
Chris@261 548 m_scaleWidth = sw;
Chris@258 549
Chris@261 550 } else {
Chris@258 551
Chris@261 552 bool hasDisplayExtents = topLayer->getDisplayExtents(min, max);
Chris@261 553 bool hasValueExtents = topLayer->getValueExtents(min, max, log, unit);
Chris@261 554
Chris@261 555 if (!hasDisplayExtents) {
Chris@258 556
Chris@261 557 if (!hasValueExtents) {
Chris@258 558
Chris@835 559 for (LayerList::iterator vi = m_layerStack.end();
Chris@835 560 vi != m_layerStack.begin(); ) {
Chris@261 561
Chris@261 562 --vi;
Chris@261 563
Chris@261 564 if ((*vi) == topLayer) continue;
Chris@1467 565 if ((*vi)->isLayerDormant(this)) continue;
Chris@261 566
Chris@607 567 sw = (*vi)->getVerticalScaleWidth
Chris@607 568 (this, m_manager->shouldShowVerticalColourScale(), paint);
Chris@261 569
Chris@261 570 if (sw > 0) {
Chris@261 571 scaleLayer = *vi;
Chris@261 572 m_scaleWidth = sw;
Chris@261 573 break;
Chris@261 574 }
Chris@261 575 }
Chris@261 576 } else if (unit != "") { // && hasValueExtents && !hasDisplayExtents
Chris@258 577
Chris@261 578 QString requireUnit = unit;
Chris@261 579
Chris@835 580 for (LayerList::iterator vi = m_layerStack.end();
Chris@835 581 vi != m_layerStack.begin(); ) {
Chris@258 582
Chris@261 583 --vi;
Chris@258 584
Chris@261 585 if ((*vi) == topLayer) continue;
Chris@1467 586 if ((*vi)->isLayerDormant(this)) continue;
Chris@258 587
Chris@261 588 if ((*vi)->getDisplayExtents(min, max)) {
Chris@261 589
Chris@261 590 // search no further than this: if the
Chris@261 591 // scale from this layer isn't suitable,
Chris@261 592 // we'll have to draw no scale (else we'd
Chris@261 593 // risk ending up with the wrong scale)
Chris@261 594
Chris@261 595 if ((*vi)->getValueExtents(min, max, log, unit) &&
Chris@261 596 unit == requireUnit) {
Chris@261 597
Chris@607 598 sw = (*vi)->getVerticalScaleWidth
Chris@607 599 (this, m_manager->shouldShowVerticalColourScale(), paint);
Chris@261 600 if (sw > 0) {
Chris@261 601 scaleLayer = *vi;
Chris@261 602 m_scaleWidth = sw;
Chris@261 603 }
Chris@258 604 }
Chris@261 605 break;
Chris@258 606 }
Chris@258 607 }
Chris@258 608 }
Chris@127 609 }
Chris@258 610 }
Chris@127 611
Chris@258 612 if (!scaleLayer) m_scaleWidth = 0;
Chris@1226 613
Chris@1226 614 // cerr << "m_scaleWidth = " << m_scaleWidth << ", r.left = " << r.left() << endl;
Chris@1226 615
Chris@258 616 if (m_scaleWidth > 0 && r.left() < m_scaleWidth) {
Chris@127 617
gyorgyf@645 618 // Profiler profiler("Pane::paintEvent - painting vertical scale", true);
gyorgyf@645 619
gyorgyf@645 620 // SVDEBUG << "Pane::paintEvent: calling paint.save() in vertical scale block" << endl;
Chris@258 621 paint.save();
Chris@258 622
Chris@1316 623 paint.setPen(Qt::NoPen);
Chris@1316 624 paint.setBrush(getBackground());
Chris@1316 625 paint.drawRect(0, 0, m_scaleWidth, height());
Chris@1316 626
Chris@287 627 paint.setPen(getForeground());
Chris@1316 628 paint.drawLine(m_scaleWidth, 0, m_scaleWidth, height());
Chris@1316 629
Chris@258 630 paint.setBrush(Qt::NoBrush);
Chris@258 631 scaleLayer->paintVerticalScale
Chris@607 632 (this, m_manager->shouldShowVerticalColourScale(),
Chris@607 633 paint, QRect(0, 0, m_scaleWidth, height()));
Chris@258 634
Chris@258 635 paint.restore();
Chris@258 636 }
Chris@261 637 }
Chris@261 638
Chris@261 639 void
Chris@261 640 Pane::drawFeatureDescription(Layer *topLayer, QPainter &paint)
Chris@261 641 {
Chris@261 642 QPoint pos = m_identifyPoint;
Chris@261 643 QString desc = topLayer->getFeatureDescription(this, pos);
gyorgyf@645 644
Chris@261 645 if (desc != "") {
Chris@261 646
Chris@261 647 paint.save();
Chris@261 648
Chris@1475 649 // Qt 5.13 deprecates QFontMetrics::width(), but its suggested
Chris@1475 650 // replacement (horizontalAdvance) was only added in Qt 5.11
Chris@1475 651 // which is too new for us
Chris@1475 652 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
Chris@1475 653
Chris@261 654 int tabStop =
Chris@261 655 paint.fontMetrics().width(tr("Some lengthy prefix:"));
Chris@261 656
Chris@261 657 QRect boundingRect =
Chris@261 658 paint.fontMetrics().boundingRect
Chris@261 659 (rect(),
Chris@261 660 Qt::AlignRight | Qt::AlignTop | Qt::TextExpandTabs,
Chris@261 661 desc, tabStop);
Chris@261 662
Chris@261 663 if (hasLightBackground()) {
Chris@261 664 paint.setPen(Qt::NoPen);
Chris@261 665 paint.setBrush(QColor(250, 250, 250, 200));
Chris@261 666 } else {
Chris@261 667 paint.setPen(Qt::NoPen);
Chris@261 668 paint.setBrush(QColor(50, 50, 50, 200));
Chris@261 669 }
Chris@261 670
Chris@261 671 int extra = paint.fontMetrics().descent();
Chris@261 672 paint.drawRect(width() - boundingRect.width() - 10 - extra,
Chris@261 673 10 - extra,
Chris@261 674 boundingRect.width() + 2 * extra,
Chris@261 675 boundingRect.height() + extra);
Chris@261 676
Chris@261 677 if (hasLightBackground()) {
Chris@261 678 paint.setPen(QColor(150, 20, 0));
Chris@261 679 } else {
Chris@261 680 paint.setPen(QColor(255, 150, 100));
Chris@261 681 }
Chris@261 682
Chris@261 683 QTextOption option;
Chris@261 684 option.setWrapMode(QTextOption::NoWrap);
Chris@261 685 option.setAlignment(Qt::AlignRight | Qt::AlignTop);
Chris@261 686 option.setTabStop(tabStop);
Chris@261 687 paint.drawText(QRectF(width() - boundingRect.width() - 10, 10,
Chris@261 688 boundingRect.width(),
Chris@261 689 boundingRect.height()),
Chris@261 690 desc,
Chris@261 691 option);
Chris@261 692
Chris@261 693 paint.restore();
Chris@261 694 }
Chris@261 695 }
Chris@258 696
Chris@261 697 void
Chris@908 698 Pane::drawCentreLine(sv_samplerate_t sampleRate, QPainter &paint, bool omitLine)
Chris@261 699 {
Chris@880 700 if (omitLine && m_manager->getMainModelSampleRate() == 0) {
Chris@880 701 return;
Chris@880 702 }
Chris@880 703
Chris@261 704 int fontHeight = paint.fontMetrics().height();
Chris@261 705 int fontAscent = paint.fontMetrics().ascent();
Chris@261 706
Chris@261 707 QColor c = QColor(0, 0, 0);
Chris@261 708 if (!hasLightBackground()) {
Chris@261 709 c = QColor(240, 240, 240);
Chris@261 710 }
Chris@277 711
Chris@1401 712 paint.setPen(scalePen(c));
Chris@274 713 int x = width() / 2;
Chris@277 714
Chris@277 715 if (!omitLine) {
Chris@277 716 paint.drawLine(x, 0, x, height() - 1);
Chris@277 717 paint.drawLine(x-1, 1, x+1, 1);
Chris@277 718 paint.drawLine(x-2, 0, x+2, 0);
Chris@277 719 paint.drawLine(x-1, height() - 2, x+1, height() - 2);
Chris@277 720 paint.drawLine(x-2, height() - 1, x+2, height() - 1);
Chris@277 721 }
Chris@261 722
Chris@261 723 paint.setPen(QColor(50, 50, 50));
Chris@261 724
Chris@261 725 int y = height() - fontHeight + fontAscent - 6;
Chris@261 726
Chris@835 727 LayerList::iterator vi = m_layerStack.end();
Chris@261 728
Chris@835 729 if (vi != m_layerStack.begin()) {
gyorgyf@645 730
Chris@261 731 switch ((*--vi)->getPreferredFrameCountPosition()) {
Chris@258 732
Chris@261 733 case Layer::PositionTop:
Chris@261 734 y = fontAscent + 6;
Chris@261 735 break;
Chris@258 736
Chris@261 737 case Layer::PositionMiddle:
Chris@261 738 y = (height() - fontHeight) / 2
Chris@261 739 + fontAscent;
Chris@261 740 break;
Chris@127 741
Chris@261 742 case Layer::PositionBottom:
Chris@261 743 // y already set correctly
Chris@261 744 break;
Chris@127 745 }
Chris@127 746 }
Chris@127 747
Chris@261 748 if (m_manager && m_manager->shouldShowFrameCount()) {
Chris@261 749
Chris@261 750 if (sampleRate) {
Chris@127 751
Chris@261 752 QString text(QString::fromStdString
Chris@261 753 (RealTime::frame2RealTime
Chris@549 754 (m_centreFrame, sampleRate)
Chris@549 755 .toText(true)));
Chris@127 756
Chris@261 757 int tw = paint.fontMetrics().width(text);
Chris@261 758 int x = width()/2 - 4 - tw;
Chris@127 759
Chris@1078 760 PaintAssistant::drawVisibleText(this, paint, x, y, text, PaintAssistant::OutlinedText);
Chris@127 761 }
Chris@261 762
Chris@261 763 QString text = QString("%1").arg(m_centreFrame);
Chris@261 764
Chris@261 765 int x = width()/2 + 4;
Chris@261 766
Chris@1078 767 PaintAssistant::drawVisibleText(this, paint, x, y, text, PaintAssistant::OutlinedText);
Chris@261 768 }
Chris@261 769 }
Chris@127 770
Chris@261 771 void
Chris@1481 772 Pane::drawModelTimeExtents(QRect r, QPainter &paint, ModelId modelId)
Chris@759 773 {
Chris@1481 774 auto model = ModelById::get(modelId);
Chris@1481 775 if (!model) return;
Chris@1481 776
Chris@759 777 paint.save();
Chris@1375 778
Chris@759 779 QBrush brush;
Chris@759 780
Chris@759 781 if (hasLightBackground()) {
Chris@1269 782 brush = QBrush(QColor("#aaf8f8f8"));
Chris@759 783 paint.setPen(Qt::black);
Chris@759 784 } else {
Chris@1269 785 brush = QBrush(QColor("#aa101010"));
Chris@759 786 paint.setPen(Qt::white);
Chris@759 787 }
Chris@759 788
Chris@1481 789 sv_frame_t f0 = model->getStartFrame();
Chris@1375 790
Chris@1375 791 if (f0 > getStartFrame() && f0 < getEndFrame()) {
Chris@1375 792 int x0 = getXForFrame(f0);
Chris@1375 793 if (x0 > r.x()) {
Chris@1375 794 paint.fillRect(0, 0, x0, height(), brush);
Chris@1375 795 paint.drawLine(x0, 0, x0, height());
Chris@1375 796 }
Chris@759 797 }
Chris@759 798
Chris@1481 799 sv_frame_t f1 = model->getEndFrame();
Chris@1375 800
Chris@1375 801 if (f1 > getStartFrame() && f1 < getEndFrame()) {
Chris@1375 802 int x1 = getXForFrame(f1);
Chris@1375 803 if (x1 < r.x() + r.width()) {
Chris@1375 804 paint.fillRect(x1, 0, width() - x1, height(), brush);
Chris@1375 805 paint.drawLine(x1, 0, x1, height());
Chris@1375 806 }
Chris@759 807 }
Chris@759 808
Chris@759 809 paint.restore();
Chris@759 810 }
Chris@759 811
Chris@759 812 void
Chris@1481 813 Pane::drawAlignmentStatus(QRect r, QPainter &paint, ModelId modelId,
Chris@326 814 bool down)
Chris@320 815 {
Chris@1481 816 auto model = ModelById::get(modelId);
Chris@1481 817 if (!model) return;
Chris@1481 818
Chris@1481 819 ModelId reference = model->getAlignmentReference();
Chris@320 820 /*
Chris@320 821 if (!reference) {
Chris@682 822 cerr << "Pane[" << this << "]::drawAlignmentStatus: No reference" << endl;
Chris@1481 823 } else if (reference == model->getId()) {
Chris@682 824 cerr << "Pane[" << this << "]::drawAlignmentStatus: This is the reference model" << endl;
Chris@320 825 } else {
Chris@682 826 cerr << "Pane[" << this << "]::drawAlignmentStatus: This is not the reference" << endl;
Chris@320 827 }
Chris@320 828 */
Chris@320 829 QString text;
Chris@320 830 int completion = 100;
Chris@320 831
Chris@1481 832 if (reference == modelId) {
Chris@320 833 text = tr("Reference");
Chris@1475 834 } else if (reference.isNone()) {
Chris@320 835 text = tr("Unaligned");
Chris@320 836 } else {
Chris@1481 837 completion = model->getAlignmentCompletion();
Chris@1497 838 int relativePitch = 0;
Chris@1497 839 if (auto alignmentModel =
Chris@1497 840 ModelById::getAs<AlignmentModel>(model->getAlignment())) {
Chris@1497 841 relativePitch = alignmentModel->getRelativePitch();
Chris@1497 842 }
Chris@320 843 if (completion == 0) {
Chris@320 844 text = tr("Unaligned");
Chris@320 845 } else if (completion < 100) {
Chris@320 846 text = tr("Aligning: %1%").arg(completion);
Chris@1497 847 } else if (relativePitch < 0) {
Chris@1497 848 text = tr("Aligned at -%1 cents").arg(-relativePitch);
Chris@1497 849 } else if (relativePitch > 0) {
Chris@1497 850 text = tr("Aligned at +%1 cents").arg(relativePitch);
Chris@320 851 } else {
Chris@320 852 text = tr("Aligned");
Chris@320 853 }
Chris@320 854 }
Chris@320 855
Chris@320 856 paint.save();
Chris@320 857 QFont font(paint.font());
Chris@320 858 font.setBold(true);
Chris@320 859 paint.setFont(font);
Chris@326 860 if (completion < 100) paint.setBrush(Qt::red);
Chris@326 861
Chris@326 862 int y = 5;
Chris@326 863 if (down) y += paint.fontMetrics().height();
Chris@326 864 int w = paint.fontMetrics().width(text);
Chris@326 865 int h = paint.fontMetrics().height();
Chris@326 866 if (r.top() > h + y || r.left() > w + m_scaleWidth + 5) {
Chris@326 867 paint.restore();
Chris@326 868 return;
Chris@326 869 }
Chris@320 870
Chris@1078 871 PaintAssistant::drawVisibleText(this, paint, m_scaleWidth + 5,
Chris@1078 872 paint.fontMetrics().ascent() + y, text, PaintAssistant::OutlinedText);
Chris@320 873
Chris@320 874 paint.restore();
Chris@320 875 }
Chris@320 876
Chris@320 877 void
Chris@1481 878 Pane::modelAlignmentCompletionChanged(ModelId modelId)
Chris@320 879 {
Chris@1481 880 View::modelAlignmentCompletionChanged(modelId);
Chris@320 881 update(QRect(0, 0, 300, 100));
Chris@320 882 }
Chris@320 883
Chris@320 884 void
Chris@1481 885 Pane::drawWorkTitle(QRect r, QPainter &paint, ModelId modelId)
Chris@326 886 {
Chris@1481 887 auto model = ModelById::get(modelId);
Chris@1481 888 if (!model) return;
Chris@1481 889
Chris@1481 890 QString title = model->getTitle();
Chris@1481 891 QString maker = model->getMaker();
Chris@587 892 //SVDEBUG << "Pane::drawWorkTitle: title=\"" << title//<< "\", maker=\"" << maker << "\"" << endl;
Chris@326 893 if (title == "") return;
Chris@326 894
Chris@326 895 QString text = title;
Chris@326 896 if (maker != "") {
Chris@326 897 text = tr("%1 - %2").arg(title).arg(maker);
Chris@326 898 }
Chris@326 899
Chris@326 900 paint.save();
Chris@326 901 QFont font(paint.font());
Chris@326 902 font.setItalic(true);
Chris@326 903 paint.setFont(font);
Chris@326 904
Chris@326 905 int y = 5;
Chris@326 906 int w = paint.fontMetrics().width(text);
Chris@326 907 int h = paint.fontMetrics().height();
Chris@326 908 if (r.top() > h + y || r.left() > w + m_scaleWidth + 5) {
Chris@326 909 paint.restore();
Chris@326 910 return;
Chris@326 911 }
Chris@326 912
Chris@1078 913 PaintAssistant::drawVisibleText(this, paint, m_scaleWidth + 5,
Chris@1078 914 paint.fontMetrics().ascent() + y, text, PaintAssistant::OutlinedText);
Chris@326 915
Chris@326 916 paint.restore();
Chris@326 917 }
Chris@326 918
Chris@326 919 void
Chris@261 920 Pane::drawLayerNames(QRect r, QPainter &paint)
Chris@261 921 {
Chris@261 922 int fontHeight = paint.fontMetrics().height();
Chris@261 923 int fontAscent = paint.fontMetrics().ascent();
Chris@127 924
Chris@300 925 int lly = height() - 6;
Chris@1397 926
Chris@1397 927 int zoomWheelSkip = 0, horizontalScaleSkip = 0;
Chris@1397 928
Chris@300 929 if (m_manager->getZoomWheelsEnabled()) {
Chris@1397 930 zoomWheelSkip = m_manager->scalePixelSize(20);
Chris@300 931 }
Chris@300 932
Chris@1390 933 for (LayerList::iterator i = m_layerStack.end(); i != m_layerStack.begin();) {
Chris@1390 934 --i;
Chris@1397 935 horizontalScaleSkip = (*i)->getHorizontalScaleHeight(this, paint);
Chris@1397 936 if (horizontalScaleSkip > 0) {
Chris@1390 937 break;
Chris@1390 938 }
Chris@1390 939 if ((*i)->isLayerOpaque()) {
Chris@1390 940 break;
Chris@1390 941 }
Chris@1390 942 }
Chris@1397 943
Chris@1397 944 lly -= std::max(zoomWheelSkip, horizontalScaleSkip);
Chris@1390 945
Chris@835 946 if (r.y() + r.height() < lly - int(m_layerStack.size()) * fontHeight) {
Chris@261 947 return;
Chris@127 948 }
Chris@127 949
Chris@294 950 QStringList texts;
Chris@299 951 std::vector<QPixmap> pixmaps;
Chris@835 952 for (LayerList::iterator i = m_layerStack.begin(); i != m_layerStack.end(); ++i) {
Chris@294 953 texts.push_back((*i)->getLayerPresentationName());
Chris@682 954 // cerr << "Pane " << this << ": Layer presentation name for " << *i << ": "
Chris@682 955 // << texts[texts.size()-1] << endl;
Chris@299 956 pixmaps.push_back((*i)->getLayerPresentationPixmap
Chris@299 957 (QSize(fontAscent, fontAscent)));
Chris@294 958 }
Chris@127 959
Chris@294 960 int maxTextWidth = width() / 3;
Chris@294 961 texts = TextAbbrev::abbreviate(texts, paint.fontMetrics(), maxTextWidth);
Chris@294 962
Chris@261 963 int llx = width() - maxTextWidth - 5;
Chris@261 964 if (m_manager->getZoomWheelsEnabled()) {
Chris@1193 965 llx -= m_manager->scalePixelSize(36);
Chris@261 966 }
Chris@261 967
Chris@300 968 if (r.x() + r.width() >= llx - fontAscent - 3) {
gyorgyf@645 969
Chris@802 970 for (int i = 0; i < texts.size(); ++i) {
Chris@299 971
Chris@682 972 // cerr << "Pane "<< this << ": text " << i << ": " << texts[i] << endl;
Chris@261 973
Chris@261 974 if (i + 1 == texts.size()) {
Chris@287 975 paint.setPen(getForeground());
Chris@261 976 }
Chris@261 977
Chris@1078 978 PaintAssistant::drawVisibleText(this, paint, llx,
Chris@261 979 lly - fontHeight + fontAscent,
Chris@1078 980 texts[i], PaintAssistant::OutlinedText);
Chris@299 981
Chris@299 982 if (!pixmaps[i].isNull()) {
Chris@299 983 paint.drawPixmap(llx - fontAscent - 3,
Chris@299 984 lly - fontHeight + (fontHeight-fontAscent)/2,
Chris@299 985 pixmaps[i]);
Chris@299 986 }
Chris@261 987
Chris@261 988 lly -= fontHeight;
Chris@261 989 }
Chris@261 990 }
Chris@261 991 }
Chris@127 992
Chris@261 993 void
Chris@261 994 Pane::drawEditingSelection(QPainter &paint)
Chris@261 995 {
Chris@261 996 int offset = m_mousePos.x() - m_clickPos.x();
Chris@577 997
Chris@908 998 sv_frame_t origStart = m_editingSelection.getStartFrame();
Chris@577 999
Chris@577 1000 int p0 = getXForFrame(origStart) + offset;
Chris@261 1001 int p1 = getXForFrame(m_editingSelection.getEndFrame()) + offset;
Chris@577 1002
Chris@261 1003 if (m_editingSelectionEdge < 0) {
Chris@261 1004 p1 = getXForFrame(m_editingSelection.getEndFrame());
Chris@261 1005 } else if (m_editingSelectionEdge > 0) {
Chris@261 1006 p0 = getXForFrame(m_editingSelection.getStartFrame());
Chris@127 1007 }
Chris@127 1008
Chris@908 1009 sv_frame_t newStart = getFrameForX(p0);
Chris@908 1010 sv_frame_t newEnd = getFrameForX(p1);
Chris@577 1011
Chris@261 1012 paint.save();
Chris@287 1013 paint.setPen(QPen(getForeground(), 2));
Chris@577 1014
Chris@577 1015 int fontHeight = paint.fontMetrics().height();
Chris@577 1016 int fontAscent = paint.fontMetrics().ascent();
Chris@908 1017 sv_samplerate_t sampleRate = getModelsSampleRate();
Chris@577 1018 QString startText, endText, offsetText;
Chris@577 1019 startText = QString("%1").arg(newStart);
Chris@577 1020 endText = QString("%1").arg(newEnd);
Chris@577 1021 offsetText = QString("%1").arg(newStart - origStart);
Chris@577 1022 if (newStart >= origStart) {
Chris@577 1023 offsetText = tr("+%1").arg(offsetText);
Chris@577 1024 }
Chris@577 1025 if (sampleRate) {
Chris@577 1026 startText = QString("%1 / %2")
Chris@577 1027 .arg(QString::fromStdString
Chris@577 1028 (RealTime::frame2RealTime(newStart, sampleRate).toText()))
Chris@577 1029 .arg(startText);
Chris@577 1030 endText = QString("%1 / %2")
Chris@577 1031 .arg(QString::fromStdString
Chris@577 1032 (RealTime::frame2RealTime(newEnd, sampleRate).toText()))
Chris@577 1033 .arg(endText);
Chris@577 1034 offsetText = QString("%1 / %2")
Chris@577 1035 .arg(QString::fromStdString
Chris@577 1036 (RealTime::frame2RealTime(newStart - origStart, sampleRate).toText()))
Chris@577 1037 .arg(offsetText);
Chris@577 1038 if (newStart >= origStart) {
Chris@577 1039 offsetText = tr("+%1").arg(offsetText);
Chris@577 1040 }
Chris@577 1041 }
Chris@1078 1042 PaintAssistant::drawVisibleText(this, paint, p0 + 2, fontAscent + fontHeight + 4, startText, PaintAssistant::OutlinedText);
Chris@1078 1043 PaintAssistant::drawVisibleText(this, paint, p1 + 2, fontAscent + fontHeight + 4, endText, PaintAssistant::OutlinedText);
Chris@1078 1044 PaintAssistant::drawVisibleText(this, paint, p0 + 2, fontAscent + fontHeight*2 + 4, offsetText, PaintAssistant::OutlinedText);
Chris@1078 1045 PaintAssistant::drawVisibleText(this, paint, p1 + 2, fontAscent + fontHeight*2 + 4, offsetText, PaintAssistant::OutlinedText);
Chris@261 1046
Chris@261 1047 //!!! duplicating display policy with View::drawSelections
Chris@261 1048
Chris@261 1049 if (m_editingSelectionEdge < 0) {
Chris@261 1050 paint.drawLine(p0, 1, p1, 1);
Chris@261 1051 paint.drawLine(p0, 0, p0, height());
Chris@261 1052 paint.drawLine(p0, height() - 1, p1, height() - 1);
Chris@261 1053 } else if (m_editingSelectionEdge > 0) {
Chris@261 1054 paint.drawLine(p0, 1, p1, 1);
Chris@261 1055 paint.drawLine(p1, 0, p1, height());
Chris@261 1056 paint.drawLine(p0, height() - 1, p1, height() - 1);
Chris@261 1057 } else {
Chris@261 1058 paint.setBrush(Qt::NoBrush);
Chris@261 1059 paint.drawRect(p0, 1, p1 - p0, height() - 2);
Chris@261 1060 }
Chris@261 1061 paint.restore();
Chris@261 1062 }
Chris@127 1063
Chris@261 1064 void
Chris@1481 1065 Pane::drawDurationAndRate(QRect r, ModelId waveformModelId,
Chris@908 1066 sv_samplerate_t sampleRate, QPainter &paint)
Chris@261 1067 {
Chris@1481 1068 auto waveformModel = ModelById::get(waveformModelId);
Chris@1481 1069 if (!waveformModel) return;
Chris@1481 1070
Chris@261 1071 int fontHeight = paint.fontMetrics().height();
Chris@261 1072 int fontAscent = paint.fontMetrics().ascent();
Chris@127 1073
Chris@261 1074 if (r.y() + r.height() < height() - fontHeight - 6) return;
Chris@127 1075
Chris@1481 1076 sv_samplerate_t modelRate = waveformModel->getSampleRate();
Chris@1481 1077 sv_samplerate_t nativeRate = waveformModel->getNativeRate();
Chris@908 1078 sv_samplerate_t playbackRate = m_manager->getPlaybackSampleRate();
Chris@261 1079
Chris@261 1080 QString srNote = "";
Chris@127 1081
Chris@1181 1082 // Show (R) for waveform models that have been resampled during
Chris@1181 1083 // load, and (X) for waveform models that will be played at the
Chris@1181 1084 // wrong rate because their rate differs from the current playback
Chris@1181 1085 // rate (which is not necessarily that of the main model).
Chris@1181 1086
Chris@1181 1087 if (modelRate != nativeRate) {
Chris@1181 1088 if (playbackRate != 0 && modelRate != playbackRate) {
Chris@261 1089 srNote = " " + tr("(X)");
Chris@1181 1090 } else {
Chris@1181 1091 srNote = " " + tr("(R)");
Chris@261 1092 }
Chris@127 1093 }
Chris@127 1094
Chris@261 1095 QString desc = tr("%1 / %2Hz%3")
Chris@1481 1096 .arg(RealTime::frame2RealTime(waveformModel->getEndFrame(),
Chris@261 1097 sampleRate)
Chris@261 1098 .toText(false).c_str())
Chris@301 1099 .arg(nativeRate)
Chris@261 1100 .arg(srNote);
Chris@261 1101
Chris@384 1102 int x = m_scaleWidth + 5;
Chris@384 1103 int pbw = getProgressBarWidth();
Chris@384 1104 if (x < pbw + 5) x = pbw + 5;
Chris@384 1105
Chris@384 1106 if (r.x() < x + paint.fontMetrics().width(desc)) {
Chris@1078 1107 PaintAssistant::drawVisibleText(this, paint, x,
Chris@261 1108 height() - fontHeight + fontAscent - 6,
Chris@1078 1109 desc, PaintAssistant::OutlinedText);
Chris@261 1110 }
Chris@127 1111 }
Chris@127 1112
Chris@227 1113 bool
Chris@908 1114 Pane::render(QPainter &paint, int xorigin, sv_frame_t f0, sv_frame_t f1)
Chris@227 1115 {
Chris@229 1116 if (!View::render(paint, xorigin + m_scaleWidth, f0, f1)) {
Chris@227 1117 return false;
Chris@227 1118 }
Chris@227 1119
Chris@227 1120 if (m_scaleWidth > 0) {
Chris@227 1121
Chris@854 1122 Layer *layer = getTopLayer();
Chris@854 1123
Chris@854 1124 if (layer) {
Chris@227 1125
Chris@227 1126 paint.save();
Chris@227 1127
Chris@287 1128 paint.setPen(getForeground());
Chris@287 1129 paint.setBrush(getBackground());
Chris@229 1130 paint.drawRect(xorigin, -1, m_scaleWidth, height()+1);
Chris@227 1131
Chris@227 1132 paint.setBrush(Qt::NoBrush);
Chris@854 1133 layer->paintVerticalScale
Chris@607 1134 (this, m_manager->shouldShowVerticalColourScale(),
Chris@607 1135 paint, QRect(xorigin, 0, m_scaleWidth, height()));
Chris@227 1136
Chris@227 1137 paint.restore();
Chris@227 1138 }
Chris@227 1139 }
Chris@227 1140
Chris@227 1141 return true;
Chris@227 1142 }
Chris@227 1143
Chris@227 1144 QImage *
Chris@1202 1145 Pane::renderPartToNewImage(sv_frame_t f0, sv_frame_t f1)
Chris@227 1146 {
Chris@1326 1147 int x0 = int(round(getZoomLevel().framesToPixels(double(f0))));
Chris@1326 1148 int x1 = int(round(getZoomLevel().framesToPixels(double(f1))));
Chris@227 1149
Chris@227 1150 QImage *image = new QImage(x1 - x0 + m_scaleWidth,
Chris@227 1151 height(), QImage::Format_RGB32);
Chris@227 1152
Chris@227 1153 int formerScaleWidth = m_scaleWidth;
Chris@227 1154
Chris@227 1155 if (m_manager && m_manager->shouldShowVerticalScale()) {
Chris@854 1156 Layer *layer = getTopLayer();
Chris@854 1157 if (layer) {
Chris@227 1158 QPainter paint(image);
Chris@854 1159 m_scaleWidth = layer->getVerticalScaleWidth
Chris@607 1160 (this, m_manager->shouldShowVerticalColourScale(), paint);
Chris@227 1161 }
Chris@227 1162 } else {
Chris@227 1163 m_scaleWidth = 0;
Chris@227 1164 }
Chris@227 1165
Chris@227 1166 if (m_scaleWidth != formerScaleWidth) {
Chris@227 1167 delete image;
Chris@227 1168 image = new QImage(x1 - x0 + m_scaleWidth,
Chris@227 1169 height(), QImage::Format_RGB32);
Chris@227 1170 }
Chris@227 1171
Chris@227 1172 QPainter *paint = new QPainter(image);
Chris@229 1173 if (!render(*paint, 0, f0, f1)) {
Chris@227 1174 delete paint;
Chris@227 1175 delete image;
Chris@1408 1176 return nullptr;
Chris@227 1177 } else {
Chris@227 1178 delete paint;
Chris@227 1179 return image;
Chris@227 1180 }
Chris@227 1181 }
Chris@227 1182
Chris@229 1183 QSize
Chris@1202 1184 Pane::getRenderedPartImageSize(sv_frame_t f0, sv_frame_t f1)
Chris@229 1185 {
Chris@1202 1186 QSize s = View::getRenderedPartImageSize(f0, f1);
Chris@229 1187 QImage *image = new QImage(100, 100, QImage::Format_RGB32);
Chris@229 1188 QPainter paint(image);
Chris@229 1189
Chris@229 1190 int sw = 0;
Chris@229 1191 if (m_manager && m_manager->shouldShowVerticalScale()) {
Chris@854 1192 Layer *layer = getTopLayer();
Chris@854 1193 if (layer) {
Chris@854 1194 sw = layer->getVerticalScaleWidth
Chris@607 1195 (this, m_manager->shouldShowVerticalColourScale(), paint);
Chris@229 1196 }
Chris@229 1197 }
Chris@229 1198
Chris@229 1199 return QSize(sw + s.width(), s.height());
Chris@229 1200 }
Chris@229 1201
Chris@908 1202 sv_frame_t
Chris@222 1203 Pane::getFirstVisibleFrame() const
Chris@222 1204 {
Chris@908 1205 sv_frame_t f0 = getFrameForX(m_scaleWidth);
Chris@908 1206 sv_frame_t f = View::getFirstVisibleFrame();
Chris@908 1207 if (f0 < 0 || f0 < f) return f;
Chris@222 1208 return f0;
Chris@222 1209 }
Chris@222 1210
Chris@127 1211 Selection
Chris@127 1212 Pane::getSelectionAt(int x, bool &closeToLeftEdge, bool &closeToRightEdge) const
Chris@127 1213 {
Chris@127 1214 closeToLeftEdge = closeToRightEdge = false;
Chris@127 1215
Chris@127 1216 if (!m_manager) return Selection();
Chris@127 1217
Chris@1402 1218 sv_frame_t testFrame = getFrameForX(x - scalePixelSize(5));
Chris@127 1219 if (testFrame < 0) {
Chris@908 1220 testFrame = getFrameForX(x);
Chris@908 1221 if (testFrame < 0) return Selection();
Chris@127 1222 }
Chris@127 1223
Chris@127 1224 Selection selection = m_manager->getContainingSelection(testFrame, true);
Chris@127 1225 if (selection.isEmpty()) return selection;
Chris@127 1226
Chris@127 1227 int lx = getXForFrame(selection.getStartFrame());
Chris@127 1228 int rx = getXForFrame(selection.getEndFrame());
Chris@127 1229
Chris@1402 1230 int fuzz = scalePixelSize(2);
Chris@127 1231 if (x < lx - fuzz || x > rx + fuzz) return Selection();
Chris@127 1232
Chris@127 1233 int width = rx - lx;
Chris@1402 1234 fuzz = scalePixelSize(3);
Chris@127 1235 if (width < 12) fuzz = width / 4;
Chris@1402 1236 if (fuzz < scalePixelSize(1)) {
Chris@1402 1237 fuzz = scalePixelSize(1);
Chris@1270 1238 }
Chris@127 1239
Chris@127 1240 if (x < lx + fuzz) closeToLeftEdge = true;
Chris@127 1241 if (x > rx - fuzz) closeToRightEdge = true;
Chris@127 1242
Chris@127 1243 return selection;
Chris@127 1244 }
Chris@127 1245
Chris@174 1246 bool
Chris@174 1247 Pane::canTopLayerMoveVertical()
Chris@174 1248 {
Chris@908 1249 double vmin, vmax, dmin, dmax;
Chris@174 1250 if (!getTopLayerDisplayExtents(vmin, vmax, dmin, dmax)) return false;
Chris@174 1251 if (dmin <= vmin && dmax >= vmax) return false;
Chris@174 1252 return true;
Chris@174 1253 }
Chris@174 1254
Chris@174 1255 bool
Chris@908 1256 Pane::getTopLayerDisplayExtents(double &vmin, double &vmax,
Chris@908 1257 double &dmin, double &dmax,
Chris@188 1258 QString *unit)
Chris@174 1259 {
Chris@268 1260 Layer *layer = getTopLayer();
Chris@174 1261 if (!layer) return false;
Chris@174 1262 bool vlog;
Chris@174 1263 QString vunit;
Chris@188 1264 bool rv = (layer->getValueExtents(vmin, vmax, vlog, vunit) &&
Chris@188 1265 layer->getDisplayExtents(dmin, dmax));
Chris@188 1266 if (unit) *unit = vunit;
Chris@188 1267 return rv;
Chris@174 1268 }
Chris@174 1269
Chris@174 1270 bool
Chris@908 1271 Pane::setTopLayerDisplayExtents(double dmin, double dmax)
Chris@174 1272 {
Chris@268 1273 Layer *layer = getTopLayer();
Chris@174 1274 if (!layer) return false;
Chris@174 1275 return layer->setDisplayExtents(dmin, dmax);
Chris@174 1276 }
Chris@174 1277
Chris@127 1278 void
Chris@282 1279 Pane::registerShortcuts(KeyReference &kr)
Chris@282 1280 {
Chris@282 1281 kr.setCategory(tr("Zoom"));
Chris@282 1282 kr.registerAlternativeShortcut(tr("Zoom In"), tr("Wheel Up"));
Chris@282 1283 kr.registerAlternativeShortcut(tr("Zoom Out"), tr("Wheel Down"));
Chris@282 1284
Chris@282 1285 kr.setCategory(tr("General Pane Mouse Actions"));
Chris@282 1286
Chris@282 1287 kr.registerShortcut(tr("Zoom"), tr("Wheel"),
Chris@282 1288 tr("Zoom in or out in time axis"));
Chris@408 1289 kr.registerShortcut(tr("Scroll"), tr("Ctrl+Wheel"),
Chris@282 1290 tr("Scroll rapidly left or right in time axis"));
Chris@282 1291 kr.registerShortcut(tr("Zoom Vertically"), tr("Shift+Wheel"),
Chris@282 1292 tr("Zoom in or out in the vertical axis"));
Chris@282 1293 kr.registerShortcut(tr("Scroll Vertically"), tr("Alt+Wheel"),
Chris@282 1294 tr("Scroll up or down in the vertical axis"));
Chris@282 1295 kr.registerShortcut(tr("Navigate"), tr("Middle"),
Chris@282 1296 tr("Click middle button and drag to navigate with any tool"));
Chris@282 1297 kr.registerShortcut(tr("Relocate"), tr("Double-Click Middle"),
Chris@282 1298 tr("Double-click middle button to relocate with any tool"));
Chris@282 1299 kr.registerShortcut(tr("Menu"), tr("Right"),
Chris@282 1300 tr("Show pane context menu"));
Chris@282 1301 }
Chris@282 1302
Chris@753 1303 Layer *
Chris@753 1304 Pane::getTopFlexiNoteLayer()
Chris@753 1305 {
Chris@835 1306 for (int i = int(m_layerStack.size()) - 1; i >= 0; --i) {
Chris@835 1307 if (LayerFactory::getInstance()->getLayerType(m_layerStack[i]) ==
Chris@753 1308 LayerFactory::FlexiNotes) {
Chris@835 1309 return m_layerStack[i];
Chris@753 1310 }
Chris@753 1311 }
Chris@1408 1312 return nullptr;
Chris@753 1313 }
Chris@753 1314
Chris@282 1315 void
Chris@127 1316 Pane::mousePressEvent(QMouseEvent *e)
Chris@127 1317 {
Chris@127 1318 if (e->buttons() & Qt::RightButton) {
Chris@189 1319 emit contextHelpChanged("");
Chris@127 1320 emit rightButtonMenuRequested(mapToGlobal(e->pos()));
Chris@127 1321 return;
Chris@127 1322 }
Chris@127 1323
Chris@682 1324 // cerr << "mousePressEvent" << endl;
Chris@341 1325
Chris@127 1326 m_clickPos = e->pos();
Chris@262 1327 m_mousePos = m_clickPos;
Chris@127 1328 m_clickedInRange = true;
Chris@127 1329 m_editingSelection = Selection();
Chris@127 1330 m_editingSelectionEdge = 0;
Chris@127 1331 m_shiftPressed = (e->modifiers() & Qt::ShiftModifier);
Chris@127 1332 m_ctrlPressed = (e->modifiers() & Qt::ControlModifier);
Chris@510 1333 m_altPressed = (e->modifiers() & Qt::AltModifier);
Chris@150 1334 m_dragMode = UnresolvedDrag;
Chris@127 1335
Chris@127 1336 ViewManager::ToolMode mode = ViewManager::NavigateMode;
Chris@711 1337 if (m_manager) mode = m_manager->getToolModeFor(this);
Chris@127 1338
Chris@127 1339 m_navigating = false;
Chris@343 1340 m_resizing = false;
Chris@343 1341 m_editing = false;
Chris@343 1342 m_releasing = false;
Chris@127 1343
Chris@283 1344 if (mode == ViewManager::NavigateMode ||
Chris@283 1345 (e->buttons() & Qt::MidButton) ||
Chris@283 1346 (mode == ViewManager::MeasureMode &&
Chris@283 1347 (e->buttons() & Qt::LeftButton) && m_shiftPressed)) {
Chris@127 1348
Chris@713 1349 if (mode != ViewManager::NavigateMode) {
Chris@713 1350 setCursor(Qt::PointingHandCursor);
Chris@713 1351 }
Chris@713 1352
Chris@713 1353 m_navigating = true;
Chris@713 1354 m_dragCentreFrame = m_centreFrame;
Chris@136 1355 m_dragStartMinValue = 0;
Chris@174 1356
Chris@908 1357 double vmin, vmax, dmin, dmax;
Chris@174 1358 if (getTopLayerDisplayExtents(vmin, vmax, dmin, dmax)) {
Chris@174 1359 m_dragStartMinValue = dmin;
Chris@136 1360 }
Chris@136 1361
Chris@829 1362 if (m_followPlay == PlaybackScrollPage) {
Chris@829 1363 // Schedule a play-head move to the mouse frame
Chris@829 1364 // location. This will happen only if nothing else of
Chris@829 1365 // interest happens (double-click, drag) before the
Chris@829 1366 // timeout.
Chris@829 1367 schedulePlaybackFrameMove(getFrameForX(e->x()));
Chris@829 1368 }
Chris@802 1369
Chris@127 1370 } else if (mode == ViewManager::SelectMode) {
Chris@127 1371
Chris@217 1372 if (!hasTopLayerTimeXAxis()) return;
Chris@217 1373
Chris@713 1374 bool closeToLeft = false, closeToRight = false;
Chris@713 1375 Selection selection = getSelectionAt(e->x(), closeToLeft, closeToRight);
Chris@713 1376
Chris@713 1377 if ((closeToLeft || closeToRight) && !(closeToLeft && closeToRight)) {
Chris@713 1378
Chris@713 1379 m_manager->removeSelection(selection);
Chris@713 1380
Chris@713 1381 if (closeToLeft) {
Chris@713 1382 m_selectionStartFrame = selection.getEndFrame();
Chris@713 1383 } else {
Chris@713 1384 m_selectionStartFrame = selection.getStartFrame();
Chris@713 1385 }
Chris@713 1386
Chris@713 1387 m_manager->setInProgressSelection(selection, false);
Chris@713 1388 m_resizing = true;
Chris@713 1389
gyorgyf@645 1390 } else {
Chris@713 1391
Chris@908 1392 sv_frame_t mouseFrame = getFrameForX(e->x());
Chris@806 1393 int resolution = 1;
Chris@908 1394 sv_frame_t snapFrame = mouseFrame;
gyorgyf@645 1395
Chris@840 1396 Layer *layer = getInteractionLayer();
Chris@928 1397 if (layer && !m_shiftPressed &&
Chris@928 1398 !qobject_cast<TimeRulerLayer *>(layer)) { // don't snap to secs
Chris@713 1399 layer->snapToFeatureFrame(this, snapFrame,
Chris@713 1400 resolution, Layer::SnapLeft);
Chris@713 1401 }
gyorgyf@645 1402
Chris@713 1403 if (snapFrame < 0) snapFrame = 0;
Chris@713 1404 m_selectionStartFrame = snapFrame;
Chris@713 1405 if (m_manager) {
Chris@713 1406 m_manager->setInProgressSelection
Chris@333 1407 (Selection(alignToReference(snapFrame),
Chris@333 1408 alignToReference(snapFrame + resolution)),
Chris@333 1409 !m_ctrlPressed);
Chris@713 1410 }
Chris@713 1411
Chris@713 1412 m_resizing = false;
Chris@802 1413
Chris@829 1414 if (m_followPlay == PlaybackScrollPage) {
Chris@829 1415 // Schedule a play-head move to the mouse frame
Chris@829 1416 // location. This will happen only if nothing else of
Chris@829 1417 // interest happens (double-click, drag) before the
Chris@829 1418 // timeout.
Chris@829 1419 schedulePlaybackFrameMove(mouseFrame);
Chris@829 1420 }
gyorgyf@645 1421 }
gyorgyf@645 1422
Chris@713 1423 update();
Chris@127 1424
Chris@127 1425 } else if (mode == ViewManager::DrawMode) {
Chris@127 1426
Chris@840 1427 Layer *layer = getInteractionLayer();
Chris@713 1428 if (layer && layer->isLayerEditable()) {
Chris@713 1429 layer->drawStart(this, e);
Chris@713 1430 }
Chris@127 1431
Chris@335 1432 } else if (mode == ViewManager::EraseMode) {
Chris@335 1433
Chris@840 1434 Layer *layer = getInteractionLayer();
Chris@713 1435 if (layer && layer->isLayerEditable()) {
Chris@713 1436 layer->eraseStart(this, e);
Chris@713 1437 }
Chris@713 1438
Chris@713 1439 // GF: handle mouse press for NoteEditMode
gyorgyf@645 1440 } else if (mode == ViewManager::NoteEditMode) {
gyorgyf@645 1441
gyorgyf@645 1442 std::cerr << "mouse pressed in note edit mode" << std::endl;
Chris@753 1443 Layer *layer = getTopFlexiNoteLayer();
Chris@753 1444 if (layer) {
gyorgyf@635 1445 layer->splitStart(this, e);
gyorgyf@635 1446 }
Chris@335 1447
Chris@127 1448 } else if (mode == ViewManager::EditMode) {
Chris@127 1449
Chris@343 1450 // Do nothing here -- we'll do it in mouseMoveEvent when the
Chris@343 1451 // drag threshold has been passed
Chris@262 1452
Chris@262 1453 } else if (mode == ViewManager::MeasureMode) {
Chris@262 1454
Chris@268 1455 Layer *layer = getTopLayer();
Chris@267 1456 if (layer) layer->measureStart(this, e);
Chris@262 1457 update();
Chris@127 1458 }
Chris@127 1459
Chris@127 1460 emit paneInteractedWith();
Chris@127 1461 }
Chris@127 1462
Chris@127 1463 void
Chris@908 1464 Pane::schedulePlaybackFrameMove(sv_frame_t frame)
Chris@802 1465 {
Chris@802 1466 m_playbackFrameMoveTo = frame;
Chris@802 1467 m_playbackFrameMoveScheduled = true;
Chris@802 1468 QTimer::singleShot(QApplication::doubleClickInterval() + 10, this,
Chris@802 1469 SLOT(playbackScheduleTimerElapsed()));
Chris@802 1470 }
Chris@802 1471
Chris@802 1472 void
Chris@802 1473 Pane::playbackScheduleTimerElapsed()
Chris@802 1474 {
Chris@802 1475 if (m_playbackFrameMoveScheduled) {
Chris@802 1476 m_manager->setPlaybackFrame(m_playbackFrameMoveTo);
Chris@802 1477 m_playbackFrameMoveScheduled = false;
Chris@802 1478 }
Chris@802 1479 }
Chris@802 1480
Chris@802 1481 void
Chris@127 1482 Pane::mouseReleaseEvent(QMouseEvent *e)
Chris@127 1483 {
Chris@854 1484 if (e && (e->buttons() & Qt::RightButton)) {
Chris@127 1485 return;
Chris@127 1486 }
Chris@127 1487
Chris@1505 1488 #ifdef DEBUG_PANE
Chris@1505 1489 SVCERR << "Pane[" << getId() << "]::mouseReleaseEvent" << endl;
Chris@1505 1490 #endif
Chris@341 1491
Chris@127 1492 ViewManager::ToolMode mode = ViewManager::NavigateMode;
Chris@711 1493 if (m_manager) mode = m_manager->getToolModeFor(this);
Chris@127 1494
Chris@343 1495 m_releasing = true;
Chris@343 1496
Chris@127 1497 if (m_clickedInRange) {
Chris@713 1498 mouseMoveEvent(e);
Chris@127 1499 }
Chris@127 1500
Chris@908 1501 sv_frame_t mouseFrame = e ? getFrameForX(e->x()) : 0;
Chris@802 1502 if (mouseFrame < 0) mouseFrame = 0;
Chris@790 1503
Chris@127 1504 if (m_navigating || mode == ViewManager::NavigateMode) {
Chris@127 1505
Chris@713 1506 m_navigating = false;
Chris@713 1507
Chris@713 1508 if (mode != ViewManager::NavigateMode) {
Chris@713 1509 // restore cursor
Chris@713 1510 toolModeChanged();
Chris@713 1511 }
Chris@713 1512
Chris@713 1513 if (m_shiftPressed) {
Chris@713 1514
Chris@713 1515 int x0 = std::min(m_clickPos.x(), m_mousePos.x());
Chris@713 1516 int x1 = std::max(m_clickPos.x(), m_mousePos.x());
Chris@713 1517
Chris@713 1518 int y0 = std::min(m_clickPos.y(), m_mousePos.y());
Chris@713 1519 int y1 = std::max(m_clickPos.y(), m_mousePos.y());
Chris@127 1520
Chris@730 1521 emit regionOutlined(QRect(x0, y0, x1 - x0, y1 - y0));
Chris@713 1522 }
Chris@127 1523
Chris@127 1524 } else if (mode == ViewManager::SelectMode) {
Chris@127 1525
Chris@343 1526 if (!hasTopLayerTimeXAxis()) {
Chris@343 1527 m_releasing = false;
Chris@343 1528 return;
Chris@343 1529 }
Chris@217 1530
Chris@713 1531 if (m_manager && m_manager->haveInProgressSelection()) {
Chris@713 1532
justin@726 1533 //cerr << "JTEST: release with selection" << endl;
Chris@713 1534 bool exclusive;
Chris@713 1535 Selection selection = m_manager->getInProgressSelection(exclusive);
gyorgyf@645 1536
Chris@713 1537 if (selection.getEndFrame() < selection.getStartFrame() + 2) {
Chris@713 1538 selection = Selection();
Chris@713 1539 }
Chris@713 1540
Chris@713 1541 m_manager->clearInProgressSelection();
Chris@713 1542
Chris@713 1543 if (exclusive) {
Chris@713 1544 m_manager->setSelection(selection);
Chris@713 1545 } else {
Chris@713 1546 m_manager->addSelection(selection);
Chris@713 1547 }
justin@726 1548 }
Chris@713 1549
Chris@713 1550 update();
Chris@713 1551
Chris@713 1552 } else if (mode == ViewManager::DrawMode) {
Chris@713 1553
Chris@840 1554 Layer *layer = getInteractionLayer();
Chris@713 1555 if (layer && layer->isLayerEditable()) {
Chris@713 1556 layer->drawEnd(this, e);
Chris@713 1557 update();
gyorgyf@645 1558 }
Chris@127 1559
Chris@335 1560 } else if (mode == ViewManager::EraseMode) {
Chris@335 1561
Chris@840 1562 Layer *layer = getInteractionLayer();
gyorgyf@645 1563 if (layer && layer->isLayerEditable()) {
gyorgyf@645 1564 layer->eraseEnd(this, e);
gyorgyf@645 1565 update();
gyorgyf@645 1566 }
gyorgyf@645 1567
gyorgyf@645 1568 } else if (mode == ViewManager::NoteEditMode) {
gyorgyf@645 1569
gyorgyf@645 1570 //GF: handle mouse release for NoteEditMode (note: works but will need to re-think this a bit later)
Chris@753 1571 Layer *layer = getTopFlexiNoteLayer();
Chris@753 1572
Chris@753 1573 if (layer) {
gyorgyf@635 1574 layer->splitEnd(this, e);
Chris@753 1575 update();
Chris@753 1576
Chris@753 1577 if (m_editing) {
Chris@753 1578 if (!editSelectionEnd(e)) {
Chris@753 1579 layer->editEnd(this, e);
Chris@753 1580 update();
Chris@753 1581 }
Chris@753 1582 }
Chris@753 1583 }
Chris@753 1584
Chris@753 1585 } else if (mode == ViewManager::EditMode) {
Chris@753 1586
Chris@343 1587 if (m_editing) {
Chris@343 1588 if (!editSelectionEnd(e)) {
Chris@840 1589 Layer *layer = getInteractionLayer();
Chris@343 1590 if (layer && layer->isLayerEditable()) {
Chris@343 1591 layer->editEnd(this, e);
Chris@343 1592 update();
Chris@343 1593 }
Chris@343 1594 }
gyorgyf@635 1595 }
Chris@607 1596
Chris@262 1597 } else if (mode == ViewManager::MeasureMode) {
Chris@262 1598
Chris@268 1599 Layer *layer = getTopLayer();
Chris@267 1600 if (layer) layer->measureEnd(this, e);
Chris@267 1601 if (m_measureCursor1) setCursor(*m_measureCursor1);
Chris@267 1602 update();
Chris@127 1603 }
Chris@127 1604
Chris@127 1605 m_clickedInRange = false;
Chris@343 1606 m_releasing = false;
Chris@127 1607
Chris@127 1608 emit paneInteractedWith();
Chris@127 1609 }
Chris@127 1610
Chris@127 1611 void
Chris@127 1612 Pane::mouseMoveEvent(QMouseEvent *e)
Chris@127 1613 {
Chris@854 1614 if (!e || (e->buttons() & Qt::RightButton)) {
Chris@127 1615 return;
Chris@127 1616 }
Chris@127 1617
Chris@682 1618 // cerr << "mouseMoveEvent" << endl;
Chris@341 1619
Chris@616 1620 QPoint pos = e->pos();
Chris@616 1621 updateContextHelp(&pos);
Chris@189 1622
Chris@343 1623 if (m_navigating && m_clickedInRange && !m_releasing) {
Chris@343 1624
Chris@343 1625 // if no buttons pressed, and not called from
Chris@343 1626 // mouseReleaseEvent, we want to reset clicked-ness (to avoid
Chris@343 1627 // annoying continual drags when we moved the mouse outside
Chris@343 1628 // the window after pressing button first time).
Chris@343 1629
Chris@343 1630 if (!(e->buttons() & Qt::LeftButton) &&
Chris@343 1631 !(e->buttons() & Qt::MidButton)) {
Chris@343 1632 m_clickedInRange = false;
Chris@343 1633 return;
Chris@343 1634 }
Chris@343 1635 }
Chris@343 1636
Chris@127 1637 ViewManager::ToolMode mode = ViewManager::NavigateMode;
Chris@711 1638 if (m_manager) mode = m_manager->getToolModeFor(this);
Chris@127 1639
Chris@127 1640 QPoint prevPoint = m_identifyPoint;
Chris@127 1641 m_identifyPoint = e->pos();
Chris@127 1642
Chris@127 1643 if (!m_clickedInRange) {
gyorgyf@645 1644
gyorgyf@646 1645 // GF: handle mouse move for context sensitive cursor switching in NoteEditMode.
gyorgyf@646 1646 // GF: Propagate the event to FlexiNoteLayer. I somehow feel it's best handeled there rather than here, but perhaps not if this will be needed elsewhere too.
Chris@753 1647 if (mode == ViewManager::NoteEditMode) {
Chris@753 1648 FlexiNoteLayer *layer = qobject_cast<FlexiNoteLayer *>(getTopFlexiNoteLayer());
Chris@753 1649 if (layer) {
Chris@753 1650 layer->mouseMoveEvent(this, e); //!!! ew
matthiasm@785 1651 update();
matthiasm@785 1652 // return;
Chris@753 1653 }
gyorgyf@646 1654 }
gyorgyf@646 1655
gyorgyf@646 1656 if (mode == ViewManager::SelectMode && hasTopLayerTimeXAxis()) {
gyorgyf@646 1657 bool closeToLeft = false, closeToRight = false;
gyorgyf@646 1658 getSelectionAt(e->x(), closeToLeft, closeToRight);
gyorgyf@646 1659 if ((closeToLeft || closeToRight) && !(closeToLeft && closeToRight)) {
gyorgyf@646 1660 setCursor(Qt::SizeHorCursor);
gyorgyf@646 1661 } else {
gyorgyf@646 1662 setCursor(Qt::ArrowCursor);
gyorgyf@646 1663 }
gyorgyf@645 1664 }
Chris@127 1665
Chris@854 1666 if (m_manager && !m_manager->isPlaying()) {
Chris@127 1667
Chris@272 1668 bool updating = false;
Chris@272 1669
Chris@840 1670 if (getInteractionLayer() &&
Chris@326 1671 m_manager->shouldIlluminateLocalFeatures()) {
Chris@127 1672
Chris@174 1673 bool previouslyIdentifying = m_identifyFeatures;
Chris@174 1674 m_identifyFeatures = true;
Chris@174 1675
Chris@174 1676 if (m_identifyFeatures != previouslyIdentifying ||
Chris@174 1677 m_identifyPoint != prevPoint) {
Chris@174 1678 update();
Chris@272 1679 updating = true;
Chris@272 1680 }
Chris@272 1681 }
Chris@272 1682
Chris@854 1683 if (!updating && mode == ViewManager::MeasureMode) {
Chris@272 1684
Chris@272 1685 Layer *layer = getTopLayer();
Chris@272 1686 if (layer && layer->nearestMeasurementRectChanged
Chris@272 1687 (this, prevPoint, m_identifyPoint)) {
Chris@272 1688 update();
Chris@174 1689 }
Chris@174 1690 }
Chris@127 1691 }
Chris@127 1692
Chris@713 1693 return;
Chris@127 1694 }
Chris@127 1695
Chris@127 1696 if (m_navigating || mode == ViewManager::NavigateMode) {
Chris@127 1697
Chris@713 1698 if (m_shiftPressed) {
Chris@713 1699
Chris@713 1700 m_mousePos = e->pos();
Chris@713 1701 update();
Chris@713 1702
Chris@713 1703 } else {
Chris@127 1704
Chris@174 1705 dragTopLayer(e);
Chris@150 1706 }
Chris@127 1707
Chris@127 1708 } else if (mode == ViewManager::SelectMode) {
Chris@127 1709
Chris@713 1710 if (!hasTopLayerTimeXAxis()) return;
Chris@713 1711
Chris@713 1712 dragExtendSelection(e);
Chris@127 1713
Chris@127 1714 } else if (mode == ViewManager::DrawMode) {
Chris@127 1715
Chris@840 1716 Layer *layer = getInteractionLayer();
gyorgyf@649 1717 if (layer && layer->isLayerEditable()) {
gyorgyf@649 1718 layer->drawDrag(this, e);
gyorgyf@649 1719 }
Chris@127 1720
Chris@335 1721 } else if (mode == ViewManager::EraseMode) {
Chris@335 1722
Chris@840 1723 Layer *layer = getInteractionLayer();
gyorgyf@649 1724 if (layer && layer->isLayerEditable()) {
gyorgyf@649 1725 layer->eraseDrag(this, e);
gyorgyf@649 1726 }
gyorgyf@649 1727
Chris@713 1728 // GF: handling NoteEditMode dragging and boundary actions for mouseMoveEvent
gyorgyf@649 1729 } else if (mode == ViewManager::NoteEditMode) {
gyorgyf@649 1730
gyorgyf@649 1731 bool resist = true;
gyorgyf@649 1732
gyorgyf@649 1733 if ((e->modifiers() & Qt::ShiftModifier)) {
gyorgyf@649 1734 m_shiftPressed = true;
gyorgyf@649 1735 }
gyorgyf@649 1736
gyorgyf@649 1737 if (m_shiftPressed) resist = false;
gyorgyf@649 1738
gyorgyf@649 1739 m_dragMode = updateDragMode
gyorgyf@649 1740 (m_dragMode,
gyorgyf@649 1741 m_clickPos,
gyorgyf@649 1742 e->pos(),
gyorgyf@649 1743 true, // can move horiz
gyorgyf@649 1744 true, // can move vert
gyorgyf@649 1745 resist, // resist horiz
gyorgyf@649 1746 resist); // resist vert
gyorgyf@649 1747
gyorgyf@649 1748 if (!m_editing) {
gyorgyf@649 1749
gyorgyf@649 1750 if (m_dragMode != UnresolvedDrag) {
gyorgyf@649 1751
gyorgyf@649 1752 m_editing = true;
gyorgyf@649 1753
gyorgyf@649 1754 QMouseEvent clickEvent(QEvent::MouseButtonPress,
gyorgyf@649 1755 m_clickPos,
gyorgyf@649 1756 Qt::NoButton,
gyorgyf@649 1757 e->buttons(),
gyorgyf@649 1758 e->modifiers());
gyorgyf@649 1759
gyorgyf@649 1760 if (!editSelectionStart(&clickEvent)) {
Chris@753 1761 Layer *layer = getTopFlexiNoteLayer();
Chris@753 1762 if (layer) {
gyorgyf@649 1763 std::cerr << "calling edit start" << std::endl;
gyorgyf@649 1764 layer->editStart(this, &clickEvent);
gyorgyf@649 1765 }
gyorgyf@649 1766 }
gyorgyf@649 1767 }
gyorgyf@649 1768
gyorgyf@649 1769 } else {
gyorgyf@649 1770
gyorgyf@649 1771 if (!editSelectionDrag(e)) {
gyorgyf@649 1772
Chris@875 1773 Layer *layer = getTopFlexiNoteLayer();
Chris@875 1774
Chris@875 1775 if (layer) {
gyorgyf@649 1776
gyorgyf@649 1777 int x = e->x();
gyorgyf@649 1778 int y = e->y();
gyorgyf@649 1779 if (m_dragMode == VerticalDrag) x = m_clickPos.x();
gyorgyf@649 1780 else if (m_dragMode == HorizontalDrag) y = m_clickPos.y();
gyorgyf@649 1781
gyorgyf@649 1782 QMouseEvent moveEvent(QEvent::MouseMove,
gyorgyf@649 1783 QPoint(x, y),
gyorgyf@649 1784 Qt::NoButton,
gyorgyf@649 1785 e->buttons(),
gyorgyf@649 1786 e->modifiers());
gyorgyf@649 1787 std::cerr << "calling editDrag" << std::endl;
gyorgyf@649 1788 layer->editDrag(this, &moveEvent);
gyorgyf@649 1789 }
gyorgyf@649 1790 }
gyorgyf@649 1791 }
Chris@335 1792
Chris@127 1793 } else if (mode == ViewManager::EditMode) {
Chris@127 1794
Chris@551 1795 bool resist = true;
Chris@551 1796
Chris@551 1797 if ((e->modifiers() & Qt::ShiftModifier)) {
Chris@551 1798 m_shiftPressed = true;
Chris@551 1799 // ... but don't set it false if shift has been
Chris@551 1800 // released -- we want the state when we started
Chris@551 1801 // dragging to be used most of the time
Chris@343 1802 }
Chris@343 1803
Chris@551 1804 if (m_shiftPressed) resist = false;
Chris@551 1805
Chris@551 1806 m_dragMode = updateDragMode
Chris@551 1807 (m_dragMode,
Chris@551 1808 m_clickPos,
Chris@551 1809 e->pos(),
Chris@551 1810 true, // can move horiz
Chris@551 1811 true, // can move vert
Chris@551 1812 resist, // resist horiz
Chris@551 1813 resist); // resist vert
Chris@551 1814
Chris@343 1815 if (!m_editing) {
Chris@343 1816
Chris@551 1817 if (m_dragMode != UnresolvedDrag) {
Chris@343 1818
Chris@343 1819 m_editing = true;
Chris@343 1820
Chris@343 1821 QMouseEvent clickEvent(QEvent::MouseButtonPress,
Chris@343 1822 m_clickPos,
Chris@343 1823 Qt::NoButton,
Chris@343 1824 e->buttons(),
Chris@343 1825 e->modifiers());
Chris@343 1826
Chris@343 1827 if (!editSelectionStart(&clickEvent)) {
Chris@840 1828 Layer *layer = getInteractionLayer();
Chris@343 1829 if (layer && layer->isLayerEditable()) {
Chris@343 1830 layer->editStart(this, &clickEvent);
Chris@343 1831 }
Chris@343 1832 }
Chris@343 1833 }
Chris@551 1834
Chris@551 1835 } else {
Chris@551 1836
Chris@551 1837 if (!editSelectionDrag(e)) {
Chris@551 1838
Chris@840 1839 Layer *layer = getInteractionLayer();
Chris@551 1840
Chris@551 1841 if (layer && layer->isLayerEditable()) {
Chris@551 1842
Chris@551 1843 int x = e->x();
Chris@551 1844 int y = e->y();
Chris@551 1845 if (m_dragMode == VerticalDrag) x = m_clickPos.x();
Chris@551 1846 else if (m_dragMode == HorizontalDrag) y = m_clickPos.y();
Chris@551 1847
Chris@551 1848 QMouseEvent moveEvent(QEvent::MouseMove,
Chris@551 1849 QPoint(x, y),
Chris@551 1850 Qt::NoButton,
Chris@551 1851 e->buttons(),
Chris@551 1852 e->modifiers());
Chris@551 1853
Chris@551 1854 layer->editDrag(this, &moveEvent);
Chris@551 1855 }
Chris@551 1856 }
Chris@343 1857 }
Chris@259 1858
Chris@259 1859 } else if (mode == ViewManager::MeasureMode) {
Chris@259 1860
Chris@267 1861 if (m_measureCursor2) setCursor(*m_measureCursor2);
Chris@266 1862
Chris@268 1863 Layer *layer = getTopLayer();
Chris@290 1864 if (layer) {
Chris@290 1865 layer->measureDrag(this, e);
Chris@290 1866 if (layer->hasTimeXAxis()) edgeScrollMaybe(e->x());
Chris@290 1867 }
Chris@267 1868
Chris@267 1869 update();
Chris@127 1870 }
Chris@802 1871
Chris@802 1872 if (m_dragMode != UnresolvedDrag) {
Chris@802 1873 m_playbackFrameMoveScheduled = false;
Chris@802 1874 }
Chris@127 1875 }
Chris@127 1876
Chris@127 1877 void
Chris@730 1878 Pane::zoomToRegion(QRect r)
Chris@174 1879 {
Chris@730 1880 int x0 = r.x();
Chris@730 1881 int y0 = r.y();
Chris@730 1882 int x1 = r.x() + r.width();
Chris@730 1883 int y1 = r.y() + r.height();
Chris@730 1884
Chris@1389 1885 SVDEBUG << "Pane::zoomToRegion: region defined by pixel rect ("
Chris@1389 1886 << r.x() << "," << r.y() << "), " << r.width() << "x" << r.height()
Chris@1389 1887 << endl;
Chris@1389 1888
Chris@1389 1889 Layer *interactionLayer = getInteractionLayer();
Chris@1389 1890 if (interactionLayer && !(interactionLayer->hasTimeXAxis())) {
Chris@1389 1891 SVDEBUG << "Interaction layer does not have time X axis - delegating to it to decide what to do" << endl;
Chris@1397 1892 interactionLayer->zoomToRegion(this, r);
Chris@1389 1893 return;
Chris@1389 1894 }
Chris@1389 1895
Chris@908 1896 sv_frame_t newStartFrame = getFrameForX(x0);
Chris@1326 1897 sv_frame_t newEndFrame = getFrameForX(x1);
Chris@1326 1898 sv_frame_t dist = newEndFrame - newStartFrame;
gyorgyf@645 1899
Chris@908 1900 sv_frame_t visibleFrames = getEndFrame() - getStartFrame();
Chris@174 1901 if (newStartFrame <= -visibleFrames) {
Chris@174 1902 newStartFrame = -visibleFrames + 1;
Chris@174 1903 }
gyorgyf@645 1904
Chris@908 1905 if (newStartFrame >= getModelsEndFrame()) {
Chris@174 1906 newStartFrame = getModelsEndFrame() - 1;
Chris@174 1907 }
Chris@1326 1908
Chris@1326 1909 ZoomLevel newZoomLevel = ZoomLevel::fromRatio(width(), dist);
Chris@1326 1910 setZoomLevel(getZoomConstraintLevel(newZoomLevel));
Chris@174 1911 setStartFrame(newStartFrame);
Chris@174 1912
Chris@174 1913 QString unit;
Chris@908 1914 double min, max;
Chris@174 1915 bool log;
Chris@1408 1916 Layer *layer = nullptr;
Chris@835 1917 for (LayerList::const_iterator i = m_layerStack.begin();
Chris@835 1918 i != m_layerStack.end(); ++i) {
Chris@174 1919 if ((*i)->getValueExtents(min, max, log, unit) &&
Chris@174 1920 (*i)->getDisplayExtents(min, max)) {
Chris@174 1921 layer = *i;
Chris@174 1922 break;
Chris@174 1923 }
Chris@174 1924 }
Chris@174 1925
Chris@174 1926 if (layer) {
Chris@174 1927 if (log) {
Chris@908 1928 min = (min < 0.0) ? -log10(-min) : (min == 0.0) ? 0.0 : log10(min);
Chris@908 1929 max = (max < 0.0) ? -log10(-max) : (max == 0.0) ? 0.0 : log10(max);
Chris@174 1930 }
Chris@908 1931 double rmin = min + ((max - min) * (height() - y1)) / height();
Chris@908 1932 double rmax = min + ((max - min) * (height() - y0)) / height();
Chris@682 1933 cerr << "min: " << min << ", max: " << max << ", y0: " << y0 << ", y1: " << y1 << ", h: " << height() << ", rmin: " << rmin << ", rmax: " << rmax << endl;
Chris@174 1934 if (log) {
Chris@908 1935 rmin = pow(10, rmin);
Chris@908 1936 rmax = pow(10, rmax);
Chris@174 1937 }
Chris@682 1938 cerr << "finally: rmin: " << rmin << ", rmax: " << rmax << " " << unit << endl;
Chris@174 1939
Chris@174 1940 layer->setDisplayExtents(rmin, rmax);
Chris@174 1941 updateVerticalPanner();
Chris@174 1942 }
Chris@174 1943 }
Chris@174 1944
Chris@174 1945 void
Chris@174 1946 Pane::dragTopLayer(QMouseEvent *e)
Chris@174 1947 {
Chris@174 1948 // We need to avoid making it too easy to drag both
Chris@174 1949 // horizontally and vertically, in the case where the
Chris@174 1950 // mouse is moved "mostly" in horizontal or vertical axis
Chris@174 1951 // with only a small variation in the other axis. This is
Chris@174 1952 // particularly important during playback (when we want to
Chris@174 1953 // avoid small horizontal motions) or in slow refresh
Chris@174 1954 // layers like spectrogram (when we want to avoid small
Chris@174 1955 // vertical motions).
Chris@174 1956 //
Chris@174 1957 // To this end we have horizontal and vertical thresholds
Chris@174 1958 // and a series of states: unresolved, horizontally or
Chris@174 1959 // vertically constrained, free.
Chris@174 1960 //
Chris@174 1961 // When the mouse first moves, we're unresolved: we
Chris@174 1962 // restrict ourselves to whichever direction seems safest,
Chris@174 1963 // until the mouse has passed a small threshold distance
Chris@174 1964 // from the click point. Then we lock in to one of the
Chris@174 1965 // constrained modes, based on which axis that distance
Chris@174 1966 // was measured in first. Finally, if it turns out we've
Chris@174 1967 // also moved more than a certain larger distance in the
Chris@174 1968 // other direction as well, we may switch into free mode.
Chris@174 1969 //
Chris@174 1970 // If the top layer is incapable of being dragged
Chris@174 1971 // vertically, the logic is short circuited.
Chris@174 1972
Chris@343 1973 m_dragMode = updateDragMode
Chris@343 1974 (m_dragMode,
Chris@343 1975 m_clickPos,
Chris@343 1976 e->pos(),
Chris@343 1977 true, // can move horiz
Chris@343 1978 canTopLayerMoveVertical(), // can move vert
Chris@343 1979 canTopLayerMoveVertical() || (m_manager && m_manager->isPlaying()), // resist horiz
Chris@897 1980 true); // resist vert
Chris@174 1981
Chris@343 1982 if (m_dragMode == HorizontalDrag ||
Chris@343 1983 m_dragMode == FreeDrag) {
Chris@174 1984
Chris@1505 1985 sv_frame_t fromFrame = getFrameForX(m_clickPos.x());
Chris@1505 1986 sv_frame_t toFrame = getFrameForX(e->x());
Chris@1505 1987 sv_frame_t frameOff = toFrame - fromFrame;
Chris@1505 1988
Chris@908 1989 sv_frame_t newCentreFrame = m_dragCentreFrame;
Chris@174 1990 if (frameOff < 0) {
Chris@174 1991 newCentreFrame -= frameOff;
Chris@806 1992 } else if (newCentreFrame >= frameOff) {
Chris@174 1993 newCentreFrame -= frameOff;
Chris@174 1994 } else {
Chris@174 1995 newCentreFrame = 0;
Chris@174 1996 }
Chris@363 1997
Chris@1505 1998 #ifdef DEBUG_PANE
Chris@1505 1999 SVDEBUG << "Pane::dragTopLayer: dragged from x = "
Chris@1505 2000 << m_clickPos.x() << " to " << e->x()
Chris@1505 2001 << ", from frame = " << fromFrame
Chris@1505 2002 << " to " << toFrame
Chris@1505 2003 << ", for frame offset of " << frameOff << endl;
Chris@1505 2004 SVDEBUG << "Pane::dragTopLayer: newCentreFrame = " << newCentreFrame
Chris@1505 2005 << ", dragCentreFrame = " << m_dragCentreFrame
Chris@1505 2006 << ", models end frame = " << getModelsEndFrame() << endl;
Chris@363 2007 #endif
Chris@339 2008
Chris@174 2009 if (newCentreFrame >= getModelsEndFrame()) {
Chris@174 2010 newCentreFrame = getModelsEndFrame();
Chris@174 2011 if (newCentreFrame > 0) --newCentreFrame;
Chris@174 2012 }
Chris@174 2013
Chris@174 2014 if (getXForFrame(m_centreFrame) != getXForFrame(newCentreFrame)) {
Chris@510 2015 setCentreFrame(newCentreFrame, !m_altPressed);
Chris@174 2016 }
Chris@174 2017 }
Chris@174 2018
Chris@343 2019 if (m_dragMode == VerticalDrag ||
Chris@343 2020 m_dragMode == FreeDrag) {
Chris@174 2021
Chris@908 2022 double vmin = 0.f, vmax = 0.f;
Chris@908 2023 double dmin = 0.f, dmax = 0.f;
Chris@174 2024
Chris@174 2025 if (getTopLayerDisplayExtents(vmin, vmax, dmin, dmax)) {
Chris@174 2026
Chris@682 2027 // cerr << "ydiff = " << ydiff << endl;
Chris@174 2028
Chris@343 2029 int ydiff = e->y() - m_clickPos.y();
Chris@908 2030 double perpix = (dmax - dmin) / height();
Chris@908 2031 double valdiff = ydiff * perpix;
Chris@682 2032 // cerr << "valdiff = " << valdiff << endl;
Chris@174 2033
Chris@343 2034 if (m_dragMode == UnresolvedDrag && ydiff != 0) {
Chris@343 2035 m_dragMode = VerticalDrag;
Chris@343 2036 }
Chris@343 2037
Chris@908 2038 double newmin = m_dragStartMinValue + valdiff;
Chris@908 2039 double newmax = m_dragStartMinValue + (dmax - dmin) + valdiff;
Chris@174 2040 if (newmin < vmin) {
Chris@174 2041 newmax += vmin - newmin;
Chris@174 2042 newmin += vmin - newmin;
Chris@174 2043 }
Chris@174 2044 if (newmax > vmax) {
Chris@174 2045 newmin -= newmax - vmax;
Chris@174 2046 newmax -= newmax - vmax;
Chris@174 2047 }
Chris@682 2048 // cerr << "(" << dmin << ", " << dmax << ") -> ("
Chris@682 2049 // << newmin << ", " << newmax << ") (drag start " << m_dragStartMinValue << ")" << endl;
Chris@174 2050
Chris@174 2051 setTopLayerDisplayExtents(newmin, newmax);
Chris@174 2052 updateVerticalPanner();
Chris@174 2053 }
Chris@174 2054 }
Chris@174 2055 }
Chris@174 2056
Chris@343 2057 Pane::DragMode
Chris@343 2058 Pane::updateDragMode(DragMode dragMode,
Chris@343 2059 QPoint origin,
Chris@343 2060 QPoint point,
Chris@343 2061 bool canMoveHorizontal,
Chris@343 2062 bool canMoveVertical,
Chris@343 2063 bool resistHorizontal,
Chris@343 2064 bool resistVertical)
Chris@343 2065 {
Chris@343 2066 int xdiff = point.x() - origin.x();
Chris@343 2067 int ydiff = point.y() - origin.y();
Chris@343 2068
Chris@343 2069 int smallThreshold = 10, bigThreshold = 80;
Chris@343 2070
Chris@896 2071 if (m_manager) {
Chris@896 2072 smallThreshold = m_manager->scalePixelSize(smallThreshold);
Chris@896 2073 bigThreshold = m_manager->scalePixelSize(bigThreshold);
Chris@896 2074 }
Chris@896 2075
Chris@587 2076 // SVDEBUG << "Pane::updateDragMode: xdiff = " << xdiff << ", ydiff = "
Chris@585 2077 // << ydiff << ", canMoveVertical = " << canMoveVertical << ", drag mode = " << m_dragMode << endl;
Chris@343 2078
Chris@343 2079 if (dragMode == UnresolvedDrag) {
Chris@343 2080
Chris@343 2081 if (abs(ydiff) > smallThreshold &&
Chris@343 2082 abs(ydiff) > abs(xdiff) * 2 &&
Chris@343 2083 canMoveVertical) {
Chris@587 2084 // SVDEBUG << "Pane::updateDragMode: passed vertical threshold" << endl;
Chris@343 2085 dragMode = VerticalDrag;
Chris@343 2086 } else if (abs(xdiff) > smallThreshold &&
Chris@343 2087 abs(xdiff) > abs(ydiff) * 2 &&
Chris@343 2088 canMoveHorizontal) {
Chris@587 2089 // SVDEBUG << "Pane::updateDragMode: passed horizontal threshold" << endl;
Chris@343 2090 dragMode = HorizontalDrag;
Chris@343 2091 } else if (abs(xdiff) > smallThreshold &&
Chris@343 2092 abs(ydiff) > smallThreshold &&
Chris@343 2093 canMoveVertical &&
Chris@343 2094 canMoveHorizontal) {
Chris@587 2095 // SVDEBUG << "Pane::updateDragMode: passed both thresholds" << endl;
Chris@343 2096 dragMode = FreeDrag;
Chris@343 2097 }
Chris@343 2098 }
Chris@343 2099
Chris@343 2100 if (dragMode == VerticalDrag && canMoveHorizontal) {
Chris@343 2101 if (abs(xdiff) > bigThreshold) dragMode = FreeDrag;
Chris@343 2102 }
Chris@343 2103
Chris@343 2104 if (dragMode == HorizontalDrag && canMoveVertical) {
Chris@343 2105 if (abs(ydiff) > bigThreshold) dragMode = FreeDrag;
Chris@343 2106 }
Chris@343 2107
Chris@343 2108 if (dragMode == UnresolvedDrag) {
Chris@343 2109 if (!resistHorizontal && xdiff != 0) {
Chris@343 2110 dragMode = HorizontalDrag;
Chris@343 2111 }
Chris@343 2112 if (!resistVertical && ydiff != 0) {
Chris@343 2113 if (dragMode == HorizontalDrag) dragMode = FreeDrag;
Chris@343 2114 else dragMode = VerticalDrag;
Chris@343 2115 }
Chris@343 2116 }
Chris@343 2117
Chris@343 2118 return dragMode;
Chris@343 2119 }
Chris@343 2120
Chris@174 2121 void
Chris@174 2122 Pane::dragExtendSelection(QMouseEvent *e)
Chris@174 2123 {
Chris@908 2124 sv_frame_t mouseFrame = getFrameForX(e->x());
Chris@806 2125 int resolution = 1;
Chris@908 2126 sv_frame_t snapFrameLeft = mouseFrame;
Chris@908 2127 sv_frame_t snapFrameRight = mouseFrame;
gyorgyf@645 2128
Chris@840 2129 Layer *layer = getInteractionLayer();
Chris@928 2130 if (layer && !m_shiftPressed &&
Chris@928 2131 !qobject_cast<TimeRulerLayer *>(layer)) { // don't snap to secs
Chris@174 2132 layer->snapToFeatureFrame(this, snapFrameLeft,
Chris@174 2133 resolution, Layer::SnapLeft);
Chris@174 2134 layer->snapToFeatureFrame(this, snapFrameRight,
Chris@174 2135 resolution, Layer::SnapRight);
Chris@174 2136 }
Chris@1266 2137
Chris@1266 2138 // cerr << "snap: frame = " << mouseFrame << ", start frame = " << m_selectionStartFrame << ", left = " << snapFrameLeft << ", right = " << snapFrameRight << endl;
Chris@174 2139
Chris@174 2140 if (snapFrameLeft < 0) snapFrameLeft = 0;
Chris@174 2141 if (snapFrameRight < 0) snapFrameRight = 0;
gyorgyf@645 2142
Chris@908 2143 sv_frame_t min, max;
gyorgyf@645 2144
Chris@806 2145 if (m_selectionStartFrame > snapFrameLeft) {
Chris@174 2146 min = snapFrameLeft;
Chris@174 2147 max = m_selectionStartFrame;
Chris@806 2148 } else if (snapFrameRight > m_selectionStartFrame) {
Chris@174 2149 min = m_selectionStartFrame;
Chris@174 2150 max = snapFrameRight;
Chris@174 2151 } else {
Chris@174 2152 min = snapFrameLeft;
Chris@174 2153 max = snapFrameRight;
Chris@174 2154 }
Chris@174 2155
Chris@966 2156 sv_frame_t end = getModelsEndFrame();
Chris@966 2157 if (min > end) min = end;
Chris@966 2158 if (max > end) max = end;
Chris@966 2159
Chris@174 2160 if (m_manager) {
Chris@966 2161
Chris@966 2162 Selection sel(alignToReference(min), alignToReference(max));
Chris@966 2163
Chris@966 2164 bool exc;
Chris@966 2165 bool same = (m_manager->haveInProgressSelection() &&
Chris@966 2166 m_manager->getInProgressSelection(exc) == sel);
Chris@966 2167
Chris@966 2168 m_manager->setInProgressSelection(sel, !m_resizing && !m_ctrlPressed);
Chris@966 2169
Chris@966 2170 if (!same) {
Chris@966 2171 edgeScrollMaybe(e->x());
Chris@966 2172 }
Chris@174 2173 }
Chris@174 2174
Chris@259 2175 update();
Chris@802 2176
Chris@802 2177 if (min != max) {
Chris@802 2178 m_playbackFrameMoveScheduled = false;
Chris@802 2179 }
Chris@259 2180 }
Chris@259 2181
Chris@259 2182 void
Chris@259 2183 Pane::edgeScrollMaybe(int x)
Chris@259 2184 {
Chris@908 2185 sv_frame_t mouseFrame = getFrameForX(x);
Chris@259 2186
Chris@174 2187 bool doScroll = false;
Chris@174 2188 if (!m_manager) doScroll = true;
Chris@854 2189 else if (!m_manager->isPlaying()) doScroll = true;
Chris@854 2190
Chris@174 2191 if (m_followPlay != PlaybackScrollContinuous) doScroll = true;
Chris@174 2192
Chris@174 2193 if (doScroll) {
Chris@908 2194 sv_frame_t offset = mouseFrame - getStartFrame();
Chris@908 2195 sv_frame_t available = getEndFrame() - getStartFrame();
Chris@908 2196 sv_frame_t move = 0;
Chris@967 2197 sv_frame_t rightEdge = available - (available / 20);
Chris@967 2198 sv_frame_t leftEdge = (available / 10);
Chris@967 2199 if (offset >= rightEdge) {
Chris@967 2200 move = offset - rightEdge + 1;
Chris@967 2201 } else if (offset <= leftEdge) {
Chris@967 2202 move = offset - leftEdge - 1;
Chris@259 2203 }
Chris@259 2204 if (move != 0) {
Chris@174 2205 setCentreFrame(m_centreFrame + move);
Chris@259 2206 update();
Chris@174 2207 }
Chris@174 2208 }
Chris@174 2209 }
Chris@174 2210
Chris@174 2211 void
Chris@127 2212 Pane::mouseDoubleClickEvent(QMouseEvent *e)
Chris@127 2213 {
Chris@127 2214 if (e->buttons() & Qt::RightButton) {
Chris@127 2215 return;
Chris@127 2216 }
Chris@127 2217
Chris@802 2218 cerr << "mouseDoubleClickEvent" << endl;
Chris@127 2219
Chris@127 2220 m_clickPos = e->pos();
Chris@127 2221 m_clickedInRange = true;
Chris@127 2222 m_shiftPressed = (e->modifiers() & Qt::ShiftModifier);
Chris@127 2223 m_ctrlPressed = (e->modifiers() & Qt::ControlModifier);
Chris@510 2224 m_altPressed = (e->modifiers() & Qt::AltModifier);
Chris@127 2225
Chris@802 2226 // cancel any pending move that came from a single click
Chris@802 2227 m_playbackFrameMoveScheduled = false;
Chris@802 2228
Chris@127 2229 ViewManager::ToolMode mode = ViewManager::NavigateMode;
Chris@711 2230 if (m_manager) mode = m_manager->getToolModeFor(this);
Chris@127 2231
Chris@255 2232 bool relocate = (mode == ViewManager::NavigateMode ||
Chris@255 2233 (e->buttons() & Qt::MidButton));
Chris@255 2234
Chris@716 2235 if (mode == ViewManager::SelectMode) {
Chris@716 2236 m_clickedInRange = false;
Chris@854 2237 if (m_manager) m_manager->clearInProgressSelection();
Chris@716 2238 emit doubleClickSelectInvoked(getFrameForX(e->x()));
Chris@716 2239 return;
Chris@716 2240 }
Chris@716 2241
Chris@1468 2242 if (mode == ViewManager::EditMode ||
Chris@1468 2243 (mode == ViewManager::NavigateMode &&
Chris@1468 2244 m_manager->getOpportunisticEditingEnabled())) {
Chris@127 2245
Chris@840 2246 Layer *layer = getInteractionLayer();
matthiasm@660 2247 if (layer && layer->isLayerEditable()) {
matthiasm@660 2248 if (layer->editOpen(this, e)) relocate = false;
matthiasm@660 2249 }
Chris@280 2250
Chris@280 2251 } else if (mode == ViewManager::MeasureMode) {
Chris@280 2252
Chris@280 2253 Layer *layer = getTopLayer();
Chris@280 2254 if (layer) layer->measureDoubleClick(this, e);
Chris@280 2255 update();
Chris@127 2256 }
Chris@255 2257
Chris@255 2258 if (relocate) {
Chris@255 2259
Chris@908 2260 sv_frame_t f = getFrameForX(e->x());
Chris@255 2261
Chris@255 2262 setCentreFrame(f);
Chris@255 2263
Chris@255 2264 m_dragCentreFrame = f;
Chris@255 2265 m_dragStartMinValue = 0;
Chris@255 2266 m_dragMode = UnresolvedDrag;
Chris@255 2267
Chris@908 2268 double vmin, vmax, dmin, dmax;
Chris@255 2269 if (getTopLayerDisplayExtents(vmin, vmax, dmin, dmax)) {
Chris@255 2270 m_dragStartMinValue = dmin;
Chris@255 2271 }
Chris@255 2272 }
matthiasm@660 2273
matthiasm@660 2274 if (mode == ViewManager::NoteEditMode) {
matthiasm@660 2275 std::cerr << "double click in note edit mode" << std::endl;
Chris@840 2276 Layer *layer = getInteractionLayer();
matthiasm@660 2277 if (layer && layer->isLayerEditable()) {
matthiasm@660 2278 layer->addNote(this, e);
matthiasm@660 2279 }
matthiasm@660 2280 }
Chris@551 2281
Chris@551 2282 m_clickedInRange = false; // in case mouseReleaseEvent is not properly called
Chris@127 2283 }
Chris@127 2284
Chris@127 2285 void
Chris@290 2286 Pane::enterEvent(QEvent *)
Chris@290 2287 {
Chris@290 2288 m_mouseInWidget = true;
Chris@290 2289 }
Chris@290 2290
Chris@290 2291 void
Chris@127 2292 Pane::leaveEvent(QEvent *)
Chris@127 2293 {
Chris@290 2294 m_mouseInWidget = false;
Chris@127 2295 bool previouslyIdentifying = m_identifyFeatures;
Chris@127 2296 m_identifyFeatures = false;
Chris@127 2297 if (previouslyIdentifying) update();
Chris@189 2298 emit contextHelpChanged("");
Chris@127 2299 }
Chris@127 2300
Chris@127 2301 void
Chris@133 2302 Pane::resizeEvent(QResizeEvent *)
Chris@133 2303 {
Chris@133 2304 updateHeadsUpDisplay();
Chris@133 2305 }
Chris@133 2306
Chris@133 2307 void
Chris@127 2308 Pane::wheelEvent(QWheelEvent *e)
Chris@127 2309 {
cannam@1207 2310 // cerr << "wheelEvent, delta " << e->delta() << ", angleDelta " << e->angleDelta().x() << "," << e->angleDelta().y() << ", pixelDelta " << e->pixelDelta().x() << "," << e->pixelDelta().y() << ", modifiers " << e->modifiers() << endl;
Chris@826 2311
Chris@1172 2312 e->accept(); // we never want wheel events on the pane to be propagated
Chris@1172 2313
Chris@826 2314 int dx = e->angleDelta().x();
Chris@826 2315 int dy = e->angleDelta().y();
Chris@826 2316
Chris@826 2317 if (dx == 0 && dy == 0) {
Chris@826 2318 return;
Chris@127 2319 }
Chris@127 2320
Chris@826 2321 int d = dy;
Chris@826 2322 bool horizontal = false;
Chris@826 2323
Chris@826 2324 if (abs(dx) > abs(dy)) {
Chris@826 2325 d = dx;
Chris@826 2326 horizontal = true;
Chris@826 2327 } else if (e->modifiers() & Qt::ControlModifier) {
Chris@826 2328 // treat a vertical wheel as horizontal
Chris@826 2329 horizontal = true;
Chris@826 2330 }
Chris@826 2331
Chris@826 2332 if (e->phase() == Qt::ScrollBegin ||
cannam@1184 2333 std::abs(d) >= 120 ||
Chris@826 2334 (d > 0 && m_pendingWheelAngle < 0) ||
Chris@826 2335 (d < 0 && m_pendingWheelAngle > 0)) {
Chris@826 2336 m_pendingWheelAngle = d;
Chris@826 2337 } else {
Chris@826 2338 m_pendingWheelAngle += d;
Chris@826 2339 }
Chris@826 2340
Chris@826 2341 if (horizontal && e->pixelDelta().x() != 0) {
Chris@826 2342
Chris@826 2343 // Have fine pixel information: use it
Chris@826 2344
Chris@826 2345 wheelHorizontalFine(e->pixelDelta().x(), e->modifiers());
Chris@826 2346
Chris@826 2347 m_pendingWheelAngle = 0;
Chris@826 2348
Chris@826 2349 } else {
Chris@826 2350
Chris@826 2351 // Coarse wheel information (or vertical zoom, which is
Chris@826 2352 // necessarily coarse itself)
Chris@826 2353
Chris@1449 2354 // Sometimes on Linux we're seeing very extreme angles on the
Chris@1449 2355 // first wheel event. They could be spurious, or they could be
Chris@1449 2356 // a result of the user frantically wheeling away while the
Chris@1449 2357 // pane was unresponsive for some reason. We don't want to
Chris@1449 2358 // discard them, as that makes the application feel even less
Chris@1449 2359 // responsive, but if we take them literally we risk changing
Chris@1449 2360 // the view so radically that the user won't recognise what
Chris@1449 2361 // has happened. Clamp them instead.
Chris@1449 2362 if (m_pendingWheelAngle > 600) {
Chris@1449 2363 m_pendingWheelAngle = 600;
Chris@1449 2364 }
Chris@1449 2365 if (m_pendingWheelAngle < -600) {
Chris@1449 2366 m_pendingWheelAngle = -600;
Chris@870 2367 }
Chris@895 2368
Chris@826 2369 while (abs(m_pendingWheelAngle) >= 120) {
Chris@826 2370
Chris@826 2371 int sign = (m_pendingWheelAngle < 0 ? -1 : 1);
Chris@826 2372
Chris@826 2373 if (horizontal) {
Chris@826 2374 wheelHorizontal(sign, e->modifiers());
Chris@826 2375 } else {
Chris@826 2376 wheelVertical(sign, e->modifiers());
Chris@826 2377 }
Chris@826 2378
Chris@826 2379 m_pendingWheelAngle -= sign * 120;
Chris@826 2380 }
Chris@826 2381 }
Chris@826 2382 }
Chris@826 2383
Chris@826 2384 void
Chris@826 2385 Pane::wheelVertical(int sign, Qt::KeyboardModifiers mods)
Chris@826 2386 {
Chris@1208 2387 // cerr << "wheelVertical: sign = " << sign << endl;
Chris@826 2388
Chris@826 2389 if (mods & Qt::ShiftModifier) {
Chris@826 2390
Chris@826 2391 // Pan vertically
Chris@826 2392
Chris@826 2393 if (m_vpan) {
Chris@826 2394 m_vpan->scroll(sign > 0);
Chris@826 2395 }
Chris@826 2396
Chris@826 2397 } else if (mods & Qt::AltModifier) {
Chris@826 2398
Chris@826 2399 // Zoom vertically
Chris@826 2400
Chris@826 2401 if (m_vthumb) {
Chris@826 2402 m_vthumb->scroll(sign > 0);
Chris@826 2403 }
Chris@826 2404
Chris@826 2405 } else {
Chris@1326 2406 using namespace std::rel_ops;
Chris@826 2407
Chris@826 2408 // Zoom in or out
Chris@826 2409
Chris@1326 2410 ZoomLevel newZoomLevel = m_zoomLevel;
Chris@826 2411
Chris@826 2412 if (sign > 0) {
Chris@1326 2413 newZoomLevel = getZoomConstraintLevel(newZoomLevel.decremented(),
Chris@1326 2414 ZoomConstraint::RoundDown);
Chris@1326 2415 } else {
Chris@1326 2416 newZoomLevel = getZoomConstraintLevel(newZoomLevel.incremented(),
Chris@1326 2417 ZoomConstraint::RoundUp);
Chris@826 2418 }
Chris@826 2419
Chris@826 2420 if (newZoomLevel != m_zoomLevel) {
Chris@826 2421 setZoomLevel(newZoomLevel);
Chris@826 2422 }
Chris@826 2423 }
Chris@826 2424
Chris@826 2425 emit paneInteractedWith();
Chris@826 2426 }
Chris@826 2427
Chris@826 2428 void
Chris@826 2429 Pane::wheelHorizontal(int sign, Qt::KeyboardModifiers mods)
Chris@826 2430 {
gyorgyf@645 2431 // Scroll left or right, rapidly
gyorgyf@645 2432
Chris@1404 2433 wheelHorizontalFine(120 * sign, mods);
Chris@826 2434 }
Chris@826 2435
Chris@826 2436 void
Chris@826 2437 Pane::wheelHorizontalFine(int pixels, Qt::KeyboardModifiers)
Chris@826 2438 {
Chris@826 2439 // Scroll left or right by a fixed number of pixels
Chris@826 2440
gyorgyf@645 2441 if (getStartFrame() < 0 &&
Chris@1326 2442 getEndFrame() >= getModelsEndFrame()) {
Chris@1326 2443 return;
Chris@1326 2444 }
Chris@1326 2445
Chris@1326 2446 int delta = int(round(m_zoomLevel.pixelsToFrames(pixels)));
Chris@806 2447
Chris@806 2448 if (m_centreFrame < delta) {
gyorgyf@645 2449 setCentreFrame(0);
Chris@806 2450 } else if (m_centreFrame - delta >= getModelsEndFrame()) {
gyorgyf@645 2451 setCentreFrame(getModelsEndFrame());
gyorgyf@645 2452 } else {
gyorgyf@645 2453 setCentreFrame(m_centreFrame - delta);
gyorgyf@645 2454 }
Chris@127 2455
Chris@127 2456 emit paneInteractedWith();
Chris@127 2457 }
Chris@127 2458
Chris@132 2459 void
Chris@132 2460 Pane::horizontalThumbwheelMoved(int value)
Chris@132 2461 {
Chris@1357 2462 ZoomLevel level = getZoomLevelByIndex(m_hthumb->getMaximumValue() - value);
Chris@132 2463 setZoomLevel(level);
Chris@132 2464 }
Chris@132 2465
Chris@132 2466 void
Chris@132 2467 Pane::verticalThumbwheelMoved(int value)
Chris@132 2468 {
Chris@1408 2469 Layer *layer = nullptr;
Chris@133 2470 if (getLayerCount() > 0) layer = getLayer(getLayerCount() - 1);
Chris@133 2471 if (layer) {
Chris@133 2472 int defaultStep = 0;
Chris@133 2473 int max = layer->getVerticalZoomSteps(defaultStep);
Chris@133 2474 if (max == 0) {
Chris@133 2475 updateHeadsUpDisplay();
Chris@133 2476 return;
Chris@133 2477 }
Chris@133 2478 if (value > max) {
Chris@133 2479 value = max;
Chris@133 2480 }
Chris@133 2481 layer->setVerticalZoomStep(value);
Chris@174 2482 updateVerticalPanner();
Chris@133 2483 }
Chris@132 2484 }
Chris@132 2485
Chris@174 2486 void
Chris@806 2487 Pane::verticalPannerMoved(float , float y0, float , float h)
Chris@174 2488 {
Chris@908 2489 double vmin, vmax, dmin, dmax;
Chris@174 2490 if (!getTopLayerDisplayExtents(vmin, vmax, dmin, dmax)) return;
Chris@908 2491 double y1 = y0 + h;
Chris@908 2492 double newmax = vmin + ((1.0 - y0) * (vmax - vmin));
Chris@908 2493 double newmin = vmin + ((1.0 - y1) * (vmax - vmin));
Chris@682 2494 // cerr << "verticalPannerMoved: (" << x0 << "," << y0 << "," << w
Chris@682 2495 // << "," << h << ") -> (" << newmin << "," << newmax << ")" << endl;
Chris@174 2496 setTopLayerDisplayExtents(newmin, newmax);
Chris@174 2497 }
Chris@174 2498
Chris@188 2499 void
Chris@188 2500 Pane::editVerticalPannerExtents()
Chris@188 2501 {
Chris@188 2502 if (!m_vpan || !m_manager || !m_manager->getZoomWheelsEnabled()) return;
Chris@188 2503
Chris@908 2504 double vmin, vmax, dmin, dmax;
Chris@188 2505 QString unit;
Chris@188 2506 if (!getTopLayerDisplayExtents(vmin, vmax, dmin, dmax, &unit)
Chris@188 2507 || vmax == vmin) {
Chris@188 2508 return;
Chris@188 2509 }
Chris@188 2510
Chris@188 2511 RangeInputDialog dialog(tr("Enter new range"),
Chris@188 2512 tr("New vertical display range, from %1 to %2 %4:")
Chris@188 2513 .arg(vmin).arg(vmax).arg(unit),
Chris@908 2514 unit, float(vmin), float(vmax), this);
Chris@908 2515 dialog.setRange(float(dmin), float(dmax));
Chris@188 2516
Chris@188 2517 if (dialog.exec() == QDialog::Accepted) {
Chris@908 2518 float newmin, newmax;
Chris@908 2519 dialog.getRange(newmin, newmax);
Chris@908 2520 setTopLayerDisplayExtents(newmin, newmax);
Chris@188 2521 updateVerticalPanner();
Chris@188 2522 }
Chris@188 2523 }
Chris@188 2524
Chris@312 2525 void
Chris@437 2526 Pane::layerParametersChanged()
Chris@437 2527 {
Chris@437 2528 View::layerParametersChanged();
Chris@437 2529 updateHeadsUpDisplay();
Chris@437 2530 }
Chris@437 2531
Chris@437 2532 void
Chris@312 2533 Pane::dragEnterEvent(QDragEnterEvent *e)
Chris@312 2534 {
Chris@312 2535 QStringList formats(e->mimeData()->formats());
Chris@682 2536 cerr << "dragEnterEvent: format: "
Chris@683 2537 << formats.join(",")
Chris@312 2538 << ", possibleActions: " << e->possibleActions()
Chris@682 2539 << ", proposedAction: " << e->proposedAction() << endl;
Chris@312 2540
Chris@616 2541 if (e->mimeData()->hasFormat("text/uri-list") ||
Chris@616 2542 e->mimeData()->hasFormat("text/plain")) {
Chris@312 2543
Chris@312 2544 if (e->proposedAction() & Qt::CopyAction) {
Chris@312 2545 e->acceptProposedAction();
Chris@312 2546 } else {
Chris@312 2547 e->setDropAction(Qt::CopyAction);
Chris@312 2548 e->accept();
Chris@312 2549 }
Chris@312 2550 }
Chris@312 2551 }
Chris@312 2552
Chris@312 2553 void
Chris@312 2554 Pane::dropEvent(QDropEvent *e)
Chris@312 2555 {
Chris@683 2556 cerr << "dropEvent: text: \"" << e->mimeData()->text()
Chris@682 2557 << "\"" << endl;
Chris@312 2558
Chris@616 2559 if (e->mimeData()->hasFormat("text/uri-list") ||
Chris@616 2560 e->mimeData()->hasFormat("text/plain")) {
Chris@312 2561
Chris@312 2562 if (e->proposedAction() & Qt::CopyAction) {
Chris@312 2563 e->acceptProposedAction();
Chris@312 2564 } else {
Chris@312 2565 e->setDropAction(Qt::CopyAction);
Chris@312 2566 e->accept();
Chris@312 2567 }
Chris@312 2568
Chris@616 2569 if (e->mimeData()->hasFormat("text/uri-list")) {
Chris@616 2570
Chris@616 2571 SVDEBUG << "accepting... data is \"" << e->mimeData()->data("text/uri-list").data() << "\"" << endl;
Chris@312 2572 emit dropAccepted(QString::fromLocal8Bit
Chris@616 2573 (e->mimeData()->data("text/uri-list").data())
Chris@312 2574 .split(QRegExp("[\\r\\n]+"),
Chris@312 2575 QString::SkipEmptyParts));
Chris@312 2576 } else {
Chris@312 2577 emit dropAccepted(QString::fromLocal8Bit
Chris@616 2578 (e->mimeData()->data("text/plain").data()));
Chris@312 2579 }
Chris@312 2580 }
Chris@312 2581 }
Chris@312 2582
Chris@127 2583 bool
Chris@127 2584 Pane::editSelectionStart(QMouseEvent *e)
Chris@127 2585 {
Chris@127 2586 if (!m_identifyFeatures ||
Chris@711 2587 !m_manager ||
Chris@711 2588 m_manager->getToolModeFor(this) != ViewManager::EditMode) {
Chris@711 2589 return false;
Chris@127 2590 }
Chris@127 2591
Chris@127 2592 bool closeToLeft, closeToRight;
Chris@127 2593 Selection s(getSelectionAt(e->x(), closeToLeft, closeToRight));
Chris@127 2594 if (s.isEmpty()) return false;
Chris@127 2595 m_editingSelection = s;
Chris@127 2596 m_editingSelectionEdge = (closeToLeft ? -1 : closeToRight ? 1 : 0);
Chris@127 2597 m_mousePos = e->pos();
Chris@127 2598 return true;
Chris@127 2599 }
Chris@127 2600
Chris@127 2601 bool
Chris@127 2602 Pane::editSelectionDrag(QMouseEvent *e)
Chris@127 2603 {
Chris@127 2604 if (m_editingSelection.isEmpty()) return false;
Chris@127 2605 m_mousePos = e->pos();
Chris@127 2606 update();
Chris@127 2607 return true;
Chris@127 2608 }
Chris@127 2609
Chris@127 2610 bool
Chris@248 2611 Pane::editSelectionEnd(QMouseEvent *)
Chris@127 2612 {
Chris@127 2613 if (m_editingSelection.isEmpty()) return false;
Chris@127 2614
Chris@127 2615 int offset = m_mousePos.x() - m_clickPos.x();
Chris@840 2616 Layer *layer = getInteractionLayer();
Chris@127 2617
Chris@127 2618 if (offset == 0 || !layer) {
Chris@716 2619 m_editingSelection = Selection();
Chris@716 2620 return true;
Chris@127 2621 }
Chris@127 2622
Chris@127 2623 int p0 = getXForFrame(m_editingSelection.getStartFrame()) + offset;
Chris@127 2624 int p1 = getXForFrame(m_editingSelection.getEndFrame()) + offset;
Chris@127 2625
Chris@908 2626 sv_frame_t f0 = getFrameForX(p0);
Chris@908 2627 sv_frame_t f1 = getFrameForX(p1);
Chris@127 2628
Chris@127 2629 Selection newSelection(f0, f1);
Chris@127 2630
Chris@127 2631 if (m_editingSelectionEdge == 0) {
gyorgyf@645 2632
Chris@127 2633 CommandHistory::getInstance()->startCompoundOperation
Chris@127 2634 (tr("Drag Selection"), true);
Chris@127 2635
Chris@716 2636 layer->moveSelection(m_editingSelection, f0);
gyorgyf@645 2637
Chris@127 2638 } else {
gyorgyf@645 2639
Chris@127 2640 CommandHistory::getInstance()->startCompoundOperation
Chris@127 2641 (tr("Resize Selection"), true);
Chris@127 2642
Chris@716 2643 if (m_editingSelectionEdge < 0) {
Chris@716 2644 f1 = m_editingSelection.getEndFrame();
Chris@716 2645 } else {
Chris@716 2646 f0 = m_editingSelection.getStartFrame();
Chris@716 2647 }
Chris@716 2648
Chris@716 2649 newSelection = Selection(f0, f1);
Chris@716 2650 layer->resizeSelection(m_editingSelection, newSelection);
Chris@127 2651 }
Chris@127 2652
Chris@127 2653 m_manager->removeSelection(m_editingSelection);
Chris@127 2654 m_manager->addSelection(newSelection);
Chris@127 2655
Chris@127 2656 CommandHistory::getInstance()->endCompoundOperation();
Chris@127 2657
Chris@127 2658 m_editingSelection = Selection();
Chris@127 2659 return true;
Chris@127 2660 }
Chris@127 2661
Chris@127 2662 void
Chris@127 2663 Pane::toolModeChanged()
Chris@127 2664 {
Chris@711 2665 ViewManager::ToolMode mode = m_manager->getToolModeFor(this);
Chris@587 2666 // SVDEBUG << "Pane::toolModeChanged(" << mode << ")" << endl;
Chris@127 2667
Chris@267 2668 if (mode == ViewManager::MeasureMode && !m_measureCursor1) {
Chris@267 2669 m_measureCursor1 = new QCursor(QBitmap(":/icons/measure1cursor.xbm"),
Chris@267 2670 QBitmap(":/icons/measure1mask.xbm"),
Chris@267 2671 15, 14);
Chris@267 2672 m_measureCursor2 = new QCursor(QBitmap(":/icons/measure2cursor.xbm"),
Chris@267 2673 QBitmap(":/icons/measure2mask.xbm"),
Chris@267 2674 16, 17);
Chris@257 2675 }
Chris@257 2676
Chris@127 2677 switch (mode) {
Chris@127 2678
Chris@127 2679 case ViewManager::NavigateMode:
Chris@713 2680 setCursor(Qt::PointingHandCursor);
Chris@713 2681 break;
gyorgyf@645 2682
Chris@127 2683 case ViewManager::SelectMode:
Chris@713 2684 setCursor(Qt::ArrowCursor);
Chris@713 2685 break;
gyorgyf@645 2686
Chris@127 2687 case ViewManager::EditMode:
Chris@713 2688 setCursor(Qt::UpArrowCursor);
Chris@713 2689 break;
gyorgyf@645 2690
Chris@127 2691 case ViewManager::DrawMode:
Chris@713 2692 setCursor(Qt::CrossCursor);
Chris@713 2693 break;
gyorgyf@645 2694
Chris@335 2695 case ViewManager::EraseMode:
Chris@713 2696 setCursor(Qt::CrossCursor);
Chris@713 2697 break;
Chris@257 2698
Chris@257 2699 case ViewManager::MeasureMode:
Chris@267 2700 if (m_measureCursor1) setCursor(*m_measureCursor1);
Chris@713 2701 break;
Chris@713 2702
Chris@713 2703 // GF: NoteEditMode uses the same default cursor as EditMode, but it will change in a context sensitive manner.
gyorgyf@645 2704 case ViewManager::NoteEditMode:
Chris@713 2705 setCursor(Qt::UpArrowCursor);
Chris@713 2706 break;
gyorgyf@645 2707
gyorgyf@645 2708 /*
Chris@127 2709 case ViewManager::TextMode:
gyorgyf@645 2710 setCursor(Qt::IBeamCursor);
gyorgyf@645 2711 break;
Chris@127 2712 */
Chris@127 2713 }
Chris@127 2714 }
Chris@127 2715
Chris@133 2716 void
Chris@133 2717 Pane::zoomWheelsEnabledChanged()
Chris@133 2718 {
Chris@133 2719 updateHeadsUpDisplay();
Chris@133 2720 update();
Chris@133 2721 }
Chris@133 2722
Chris@133 2723 void
Chris@1326 2724 Pane::viewZoomLevelChanged(View *v, ZoomLevel z, bool locked)
Chris@133 2725 {
Chris@682 2726 // cerr << "Pane[" << this << "]::zoomLevelChanged (global now "
Chris@682 2727 // << (m_manager ? m_manager->getGlobalZoom() : 0) << ")" << endl;
Chris@192 2728
Chris@224 2729 View::viewZoomLevelChanged(v, z, locked);
Chris@224 2730
Chris@232 2731 if (m_hthumb && !m_hthumb->isVisible()) return;
Chris@224 2732
Chris@222 2733 if (v != this) {
Chris@222 2734 if (!locked || !m_followZoom) return;
Chris@222 2735 }
Chris@222 2736
Chris@133 2737 if (m_manager && m_manager->getZoomWheelsEnabled()) {
Chris@133 2738 updateHeadsUpDisplay();
Chris@133 2739 }
Chris@133 2740 }
Chris@133 2741
Chris@133 2742 void
Chris@133 2743 Pane::propertyContainerSelected(View *v, PropertyContainer *pc)
Chris@133 2744 {
Chris@1408 2745 Layer *layer = nullptr;
Chris@133 2746
Chris@133 2747 if (getLayerCount() > 0) {
Chris@133 2748 layer = getLayer(getLayerCount() - 1);
Chris@133 2749 disconnect(layer, SIGNAL(verticalZoomChanged()),
Chris@133 2750 this, SLOT(verticalZoomChanged()));
Chris@133 2751 }
Chris@133 2752
Chris@133 2753 View::propertyContainerSelected(v, pc);
Chris@133 2754 updateHeadsUpDisplay();
Chris@133 2755
Chris@187 2756 if (m_vthumb) {
Chris@1408 2757 RangeMapper *rm = nullptr;
Chris@187 2758 if (layer) rm = layer->getNewVerticalZoomRangeMapper();
Chris@187 2759 if (rm) m_vthumb->setRangeMapper(rm);
Chris@187 2760 }
Chris@187 2761
Chris@133 2762 if (getLayerCount() > 0) {
Chris@133 2763 layer = getLayer(getLayerCount() - 1);
Chris@133 2764 connect(layer, SIGNAL(verticalZoomChanged()),
Chris@133 2765 this, SLOT(verticalZoomChanged()));
Chris@133 2766 }
Chris@133 2767 }
Chris@133 2768
Chris@133 2769 void
Chris@133 2770 Pane::verticalZoomChanged()
Chris@133 2771 {
Chris@1408 2772 Layer *layer = nullptr;
Chris@133 2773
Chris@133 2774 if (getLayerCount() > 0) {
Chris@133 2775
Chris@133 2776 layer = getLayer(getLayerCount() - 1);
Chris@133 2777
Chris@133 2778 if (m_vthumb && m_vthumb->isVisible()) {
Chris@133 2779 m_vthumb->setValue(layer->getCurrentVerticalZoomStep());
Chris@133 2780 }
Chris@133 2781 }
Chris@133 2782 }
Chris@133 2783
Chris@189 2784 void
Chris@189 2785 Pane::updateContextHelp(const QPoint *pos)
Chris@189 2786 {
Chris@189 2787 QString help = "";
Chris@189 2788
Chris@189 2789 if (m_clickedInRange) {
Chris@189 2790 emit contextHelpChanged("");
Chris@189 2791 return;
Chris@189 2792 }
Chris@189 2793
Chris@189 2794 ViewManager::ToolMode mode = ViewManager::NavigateMode;
Chris@711 2795 if (m_manager) mode = m_manager->getToolModeFor(this);
Chris@189 2796
Chris@189 2797 bool editable = false;
Chris@840 2798 Layer *layer = getInteractionLayer();
Chris@189 2799 if (layer && layer->isLayerEditable()) {
Chris@189 2800 editable = true;
Chris@189 2801 }
Chris@189 2802
Chris@189 2803 if (mode == ViewManager::NavigateMode) {
Chris@189 2804
Chris@1388 2805 help = tr("Click and drag to navigate; use mouse-wheel or trackpad-scroll to zoom; hold Shift and drag to zoom to an area");
Chris@189 2806
Chris@189 2807 } else if (mode == ViewManager::SelectMode) {
Chris@189 2808
Chris@217 2809 if (!hasTopLayerTimeXAxis()) return;
Chris@217 2810
Chris@189 2811 bool haveSelection = (m_manager && !m_manager->getSelections().empty());
Chris@189 2812
Chris@189 2813 if (haveSelection) {
Chris@597 2814 #ifdef Q_OS_MAC
Chris@597 2815 if (editable) {
Chris@597 2816 help = tr("Click and drag to select a range; hold Shift to avoid snapping to items; hold Cmd for multi-select; middle-click and drag to navigate");
Chris@597 2817 } else {
Chris@597 2818 help = tr("Click and drag to select a range; hold Cmd for multi-select; middle-click and drag to navigate");
Chris@597 2819 }
Chris@597 2820 #else
Chris@189 2821 if (editable) {
Chris@189 2822 help = tr("Click and drag to select a range; hold Shift to avoid snapping to items; hold Ctrl for multi-select; middle-click and drag to navigate");
Chris@189 2823 } else {
Chris@189 2824 help = tr("Click and drag to select a range; hold Ctrl for multi-select; middle-click and drag to navigate");
Chris@189 2825 }
Chris@597 2826 #endif
Chris@189 2827
Chris@189 2828 if (pos) {
Chris@189 2829 bool closeToLeft = false, closeToRight = false;
Chris@189 2830 Selection selection = getSelectionAt(pos->x(), closeToLeft, closeToRight);
Chris@189 2831 if ((closeToLeft || closeToRight) && !(closeToLeft && closeToRight)) {
Chris@189 2832
Chris@189 2833 help = tr("Click and drag to move the selection boundary");
Chris@189 2834 }
Chris@189 2835 }
Chris@189 2836 } else {
Chris@189 2837 if (editable) {
Chris@189 2838 help = tr("Click and drag to select a range; hold Shift to avoid snapping to items; middle-click to navigate");
Chris@189 2839 } else {
Chris@189 2840 help = tr("Click and drag to select a range; middle-click and drag to navigate");
Chris@189 2841 }
Chris@189 2842 }
Chris@189 2843
Chris@189 2844 } else if (mode == ViewManager::DrawMode) {
Chris@189 2845
Chris@189 2846 //!!! could call through to a layer function to find out exact meaning
Chris@713 2847 if (editable) {
Chris@189 2848 help = tr("Click to add a new item in the active layer");
Chris@189 2849 }
Chris@335 2850
Chris@335 2851 } else if (mode == ViewManager::EraseMode) {
Chris@335 2852
Chris@335 2853 //!!! could call through to a layer function to find out exact meaning
Chris@713 2854 if (editable) {
Chris@335 2855 help = tr("Click to erase an item from the active layer");
Chris@335 2856 }
Chris@189 2857
Chris@189 2858 } else if (mode == ViewManager::EditMode) {
Chris@189 2859
Chris@189 2860 //!!! could call through to layer
Chris@713 2861 if (editable) {
Chris@551 2862 help = tr("Click and drag an item in the active layer to move it; hold Shift to override initial resistance");
Chris@189 2863 if (pos) {
Chris@189 2864 bool closeToLeft = false, closeToRight = false;
Chris@189 2865 Selection selection = getSelectionAt(pos->x(), closeToLeft, closeToRight);
Chris@189 2866 if (!selection.isEmpty()) {
Chris@189 2867 help = tr("Click and drag to move all items in the selected range");
Chris@189 2868 }
Chris@189 2869 }
Chris@189 2870 }
Chris@189 2871 }
Chris@189 2872
Chris@189 2873 emit contextHelpChanged(help);
Chris@189 2874 }
Chris@189 2875
Chris@189 2876 void
Chris@189 2877 Pane::mouseEnteredWidget()
Chris@189 2878 {
Chris@189 2879 QWidget *w = dynamic_cast<QWidget *>(sender());
Chris@189 2880 if (!w) return;
Chris@189 2881
Chris@189 2882 if (w == m_vpan) {
Chris@189 2883 emit contextHelpChanged(tr("Click and drag to adjust the visible range of the vertical scale"));
Chris@189 2884 } else if (w == m_vthumb) {
Chris@189 2885 emit contextHelpChanged(tr("Click and drag to adjust the vertical zoom level"));
Chris@189 2886 } else if (w == m_hthumb) {
Chris@189 2887 emit contextHelpChanged(tr("Click and drag to adjust the horizontal zoom level"));
Chris@189 2888 } else if (w == m_reset) {
Chris@189 2889 emit contextHelpChanged(tr("Reset horizontal and vertical zoom levels to their defaults"));
Chris@189 2890 }
Chris@189 2891 }
Chris@189 2892
Chris@189 2893 void
Chris@189 2894 Pane::mouseLeftWidget()
Chris@189 2895 {
Chris@189 2896 emit contextHelpChanged("");
Chris@189 2897 }
Chris@189 2898
Chris@316 2899 void
Chris@316 2900 Pane::toXml(QTextStream &stream,
Chris@316 2901 QString indent, QString extraAttributes) const
Chris@127 2902 {
Chris@316 2903 View::toXml
Chris@316 2904 (stream, indent,
gyorgyf@645 2905 QString("type=\"pane\" centreLineVisible=\"%1\" height=\"%2\" %3")
gyorgyf@645 2906 .arg(m_centreLineVisible).arg(height()).arg(extraAttributes));
Chris@127 2907 }
Chris@127 2908
Chris@127 2909