annotate view/Pane.cpp @ 1496:d09345e578a7

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