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