TimeValueLayer.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 Chris Cannam.
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 "TimeValueLayer.h"
17 
18 #include "data/model/Model.h"
19 #include "base/RealTime.h"
20 #include "base/Profiler.h"
21 #include "base/LogRange.h"
22 #include "base/RangeMapper.h"
23 #include "base/Pitch.h"
24 #include "view/View.h"
25 
26 #include "data/model/SparseTimeValueModel.h"
27 #include "data/model/Labeller.h"
28 
29 #include "widgets/ItemEditDialog.h"
31 #include "widgets/TextAbbrev.h"
32 
33 #include "ColourDatabase.h"
34 #include "ColourMapper.h"
35 #include "PianoScale.h"
36 #include "LinearNumericalScale.h"
37 #include "LogNumericalScale.h"
38 #include "LinearColourScale.h"
39 #include "LogColourScale.h"
40 #include "PaintAssistant.h"
41 
42 #include <QPainter>
43 #include <QPainterPath>
44 #include <QMouseEvent>
45 #include <QRegExp>
46 #include <QTextStream>
47 #include <QMessageBox>
48 #include <QInputDialog>
49 
50 #include <iostream>
51 #include <cmath>
52 
53 //#define DEBUG_TIME_VALUE_LAYER 1
54 
57  m_editing(false),
58  m_originalPoint(0, 0.0, tr("New Point")),
59  m_editingPoint(0, 0.0, tr("New Point")),
60  m_editingCommand(nullptr),
61  m_colourMap(0),
62  m_colourInverted(false),
63  m_plotStyle(PlotConnectedPoints),
64  m_verticalScale(AutoAlignScale),
65  m_drawSegmentDivisions(true),
66  m_derivative(false),
67  m_scaleMinimum(0),
68  m_scaleMaximum(0)
69 {
70 
71 }
72 
73 int
75 {
76  auto model = ModelById::get(m_model);
77  if (model) return model->getCompletion();
78  else return 0;
79 }
80 
81 void
82 TimeValueLayer::setModel(ModelId modelId)
83 {
84  auto newModel = ModelById::getAs<SparseTimeValueModel>(modelId);
85 
86  if (!modelId.isNone() && !newModel) {
87  throw std::logic_error("Not a SparseTimeValueModel");
88  }
89 
90  if (m_model == modelId) return;
91  m_model = modelId;
92 
93  if (newModel) {
94 
96 
97  m_scaleMinimum = 0;
98  m_scaleMaximum = 0;
99 
100  if (newModel->getRDFTypeURI().endsWith("Segment")) {
102  }
103  if (newModel->getRDFTypeURI().endsWith("Change")) {
105  }
106  }
107 
108  emit modelReplaced();
109 }
110 
111 Layer::PropertyList
113 {
114  PropertyList list = SingleColourLayer::getProperties();
115  list.push_back("Plot Type");
116  list.push_back("Vertical Scale");
117  list.push_back("Scale Units");
118  list.push_back("Draw Segment Division Lines");
119  list.push_back("Show Derivative");
120  return list;
121 }
122 
123 QString
124 TimeValueLayer::getPropertyLabel(const PropertyName &name) const
125 {
126  if (name == "Plot Type") return tr("Plot Type");
127  if (name == "Vertical Scale") return tr("Vertical Scale");
128  if (name == "Scale Units") return tr("Scale Units");
129  if (name == "Draw Segment Division Lines") return tr("Draw Segment Division Lines");
130  if (name == "Show Derivative") return tr("Show Derivative");
132 }
133 
134 QString
135 TimeValueLayer::getPropertyIconName(const PropertyName &name) const
136 {
137  if (name == "Draw Segment Division Lines") return "lines";
138  if (name == "Show Derivative") return "derivative";
139  return "";
140 }
141 
142 Layer::PropertyType
143 TimeValueLayer::getPropertyType(const PropertyName &name) const
144 {
145  if (name == "Plot Type") return ValueProperty;
146  if (name == "Vertical Scale") return ValueProperty;
147  if (name == "Scale Units") return UnitsProperty;
148  if (name == "Colour" && m_plotStyle == PlotSegmentation) return ColourMapProperty;
149  if (name == "Draw Segment Division Lines") return ToggleProperty;
150  if (name == "Show Derivative") return ToggleProperty;
152 }
153 
154 QString
155 TimeValueLayer::getPropertyGroupName(const PropertyName &name) const
156 {
157  if (name == "Vertical Scale" || name == "Scale Units") {
158  return tr("Scale");
159  }
160  if (name == "Plot Type" || name == "Draw Segment Division Lines" ||
161  name == "Show Derivative") {
162  return tr("Plot Type");
163  }
165 }
166 
167 bool
169 {
170  auto model = ModelById::getAs<SparseTimeValueModel>(m_model);
171  if (!model) return false;
172  return m_plotStyle == PlotSegmentation && model->hasTextLabels();
173 }
174 
175 QString
177 {
178  auto model = ModelById::getAs<SparseTimeValueModel>(m_model);
179  if (model) return model->getScaleUnits();
180  else return "";
181 }
182 
183 int
185  int *min, int *max, int *deflt) const
186 {
187  int val = 0;
188 
189  if (name == "Colour" && m_plotStyle == PlotSegmentation) {
190 
191  if (min) *min = 0;
192  if (max) *max = ColourMapper::getColourMapCount() - 1;
193  if (deflt) *deflt = 0;
194 
195  val = m_colourMap;
196 
197  } else if (name == "Plot Type") {
198 
199  if (min) *min = 0;
200  if (max) *max = 6;
201  if (deflt) *deflt = int(PlotConnectedPoints);
202 
203  val = int(m_plotStyle);
204 
205  } else if (name == "Vertical Scale") {
206 
207  if (min) *min = 0;
208  if (max) *max = 3;
209  if (deflt) *deflt = int(AutoAlignScale);
210 
211  val = int(m_verticalScale);
212 
213  } else if (name == "Scale Units") {
214 
215  if (deflt) *deflt = 0;
216  auto model = ModelById::getAs<SparseTimeValueModel>(m_model);
217  if (model) {
218  val = UnitDatabase::getInstance()->getUnitId
219  (getScaleUnits());
220  }
221 
222  } else if (name == "Draw Segment Division Lines") {
223 
224  if (min) *min = 0;
225  if (max) *max = 1;
226  if (deflt) *deflt = 1;
227  val = (m_drawSegmentDivisions ? 1.0 : 0.0);
228 
229  } else if (name == "Show Derivative") {
230 
231  if (min) *min = 0;
232  if (max) *max = 1;
233  if (deflt) *deflt = 0;
234  val = (m_derivative ? 1.0 : 0.0);
235 
236  } else {
237 
238  val = SingleColourLayer::getPropertyRangeAndValue(name, min, max, deflt);
239  }
240 
241  return val;
242 }
243 
244 QString
245 TimeValueLayer::getPropertyValueLabel(const PropertyName &name,
246  int value) const
247 {
248  if (name == "Colour" && m_plotStyle == PlotSegmentation) {
249  return ColourMapper::getColourMapLabel(value);
250  } else if (name == "Plot Type") {
251  switch (value) {
252  default:
253  case 0: return tr("Points");
254  case 1: return tr("Stems");
255  case 2: return tr("Connected Points");
256  case 3: return tr("Lines");
257  case 4: return tr("Curve");
258  case 5: return tr("Segmentation");
259  case 6: return tr("Discrete Curves");
260  }
261  } else if (name == "Vertical Scale") {
262  switch (value) {
263  default:
264  case 0: return tr("Auto-Align");
265  case 1: return tr("Linear");
266  case 2: return tr("Log");
267  case 3: return tr("+/-1");
268  }
269  }
270  return SingleColourLayer::getPropertyValueLabel(name, value);
271 }
272 
273 void
274 TimeValueLayer::setProperty(const PropertyName &name, int value)
275 {
276  if (name == "Colour" && m_plotStyle == PlotSegmentation) {
277  setFillColourMap(value);
278  } else if (name == "Plot Type") {
279  setPlotStyle(PlotStyle(value));
280  } else if (name == "Vertical Scale") {
282  } else if (name == "Scale Units") {
283  auto model = ModelById::getAs<SparseTimeValueModel>(m_model);
284  if (model) {
285  model->setScaleUnits
286  (UnitDatabase::getInstance()->getUnitById(value));
287  emit modelChanged(m_model);
288  }
289  } else if (name == "Draw Segment Division Lines") {
290  setDrawSegmentDivisions(value > 0.5);
291  } else if (name == "Show Derivative") {
292  setShowDerivative(value > 0.5);
293  } else {
294  SingleColourLayer::setProperty(name, value);
295  }
296 }
297 
298 void
300 {
301  if (m_colourMap == map) return;
302  m_colourMap = map;
303  emit layerParametersChanged();
304 }
305 
306 void
308 {
309  if (m_plotStyle == style) return;
310  bool colourTypeChanged = (style == PlotSegmentation ||
312  m_plotStyle = style;
313  if (colourTypeChanged) {
315  }
316  emit layerParametersChanged();
317 }
318 
319 void
321 {
322  if (m_verticalScale == scale) return;
323  m_verticalScale = scale;
324  emit layerParametersChanged();
325 }
326 
327 void
329 {
330  if (m_drawSegmentDivisions == draw) return;
331  m_drawSegmentDivisions = draw;
332  emit layerParametersChanged();
333 }
334 
335 void
337 {
338  if (m_derivative == show) return;
339  m_derivative = show;
340  emit layerParametersChanged();
341 }
342 
343 bool
345 {
346  // We don't illuminate sections in the line or curve modes, so
347  // they're always scrollable
348 
349  if (m_plotStyle == PlotLines ||
350  m_plotStyle == PlotCurve ||
351  m_plotStyle == PlotDiscreteCurves) return true;
352 
353  QPoint discard;
354  return !v->shouldIlluminateLocalFeatures(this, discard);
355 }
356 
357 bool
358 TimeValueLayer::getValueExtents(double &min, double &max,
359  bool &logarithmic, QString &unit) const
360 {
361  auto model = ModelById::getAs<SparseTimeValueModel>(m_model);
362  if (!model) return false;
363 
364  min = model->getValueMinimum();
365  max = model->getValueMaximum();
366 
367  logarithmic = (m_verticalScale == LogScale);
368 
369  unit = getScaleUnits();
370 
371  if (m_derivative) {
372  max = std::max(fabs(min), fabs(max));
373  min = -max;
374  }
375 
376 #ifdef DEBUG_TIME_VALUE_LAYER
377  cerr << "TimeValueLayer::getValueExtents: min = " << min << ", max = " << max << endl;
378 #endif
379 
380  if (!shouldAutoAlign() && !logarithmic && !m_derivative) {
381 
382  if (max == min) {
383  max = max + 0.5;
384  min = min - 0.5;
385  } else {
386  double margin = (max - min) / 10.0;
387  max = max + margin;
388  min = min - margin;
389  }
390 
391 #ifdef DEBUG_TIME_VALUE_LAYER
392  cerr << "TimeValueLayer::getValueExtents: min = " << min << ", max = " << max << " (after adjustment)" << endl;
393 #endif
394  }
395 
396  return true;
397 }
398 
399 bool
400 TimeValueLayer::getDisplayExtents(double &min, double &max) const
401 {
402  auto model = ModelById::getAs<SparseTimeValueModel>(m_model);
403  if (!model || shouldAutoAlign()) return false;
404 
406  bool log;
407  QString unit;
408  getValueExtents(min, max, log, unit);
409  } else {
410  min = m_scaleMinimum;
411  max = m_scaleMaximum;
412  }
413 
414  if (m_derivative) {
415  max = std::max(fabs(min), fabs(max));
416  min = -max;
417  }
418 
419 #ifdef DEBUG_TIME_VALUE_LAYER
420  cerr << "TimeValueLayer::getDisplayExtents: min = " << min << ", max = " << max << endl;
421 #endif
422 
423  return true;
424 }
425 
426 bool
427 TimeValueLayer::setDisplayExtents(double min, double max)
428 {
429  auto model = ModelById::getAs<SparseTimeValueModel>(m_model);
430  if (!model) return false;
431 
432  if (min == max) {
433  if (min == 0.f) {
434  max = 1.f;
435  } else {
436  max = min * 1.0001;
437  }
438  }
439 
440  m_scaleMinimum = min;
441  m_scaleMaximum = max;
442 
443 #ifdef DEBUG_TIME_VALUE_LAYER
444  cerr << "TimeValueLayer::setDisplayExtents: min = " << min << ", max = " << max << endl;
445 #endif
446 
447  emit layerParametersChanged();
448  return true;
449 }
450 
451 int
453 {
454  if (shouldAutoAlign()) return 0;
455  auto model = ModelById::getAs<SparseTimeValueModel>(m_model);
456  if (!model) return 0;
457 
458  defaultStep = 0;
459  return 100;
460 }
461 
462 int
464 {
465  if (shouldAutoAlign()) return 0;
466  auto model = ModelById::getAs<SparseTimeValueModel>(m_model);
467  if (!model) return 0;
468 
469  RangeMapper *mapper = getNewVerticalZoomRangeMapper();
470  if (!mapper) return 0;
471 
472  double dmin, dmax;
473  getDisplayExtents(dmin, dmax);
474 
475  int nr = mapper->getPositionForValue(dmax - dmin);
476 
477 #ifdef DEBUG_TIME_VALUE_LAYER
478  cerr << "TimeValueLayer::getCurrentVerticalZoomStep: dmin = " << dmin << ", dmax = " << dmax << ", nr = " << nr << endl;
479 #endif
480 
481  delete mapper;
482 
483  return 100 - nr;
484 }
485 
486 void
488 {
489  if (shouldAutoAlign()) return;
490  auto model = ModelById::getAs<SparseTimeValueModel>(m_model);
491  if (!model) return;
492 
493  RangeMapper *mapper = getNewVerticalZoomRangeMapper();
494  if (!mapper) return;
495 
496  double min, max;
497  bool logarithmic;
498  QString unit;
499  getValueExtents(min, max, logarithmic, unit);
500 
501  double dmin, dmax;
502  getDisplayExtents(dmin, dmax);
503 
504  double newdist = mapper->getValueForPosition(100 - step);
505 
506  double newmin, newmax;
507 
508  if (logarithmic) {
509 
510  // see SpectrogramLayer::setVerticalZoomStep
511 
512  newmax = (newdist + sqrt(newdist*newdist + 4*dmin*dmax)) / 2;
513  newmin = newmax - newdist;
514 
515 #ifdef DEBUG_TIME_VALUE_LAYER
516  cerr << "newmin = " << newmin << ", newmax = " << newmax << endl;
517 #endif
518 
519  } else {
520  double dmid = (dmax + dmin) / 2;
521  newmin = dmid - newdist / 2;
522  newmax = dmid + newdist / 2;
523  }
524 
525  if (newmin < min) {
526  newmax += (min - newmin);
527  newmin = min;
528  }
529  if (newmax > max) {
530  newmax = max;
531  }
532 
533 #ifdef DEBUG_TIME_VALUE_LAYER
534  cerr << "TimeValueLayer::setVerticalZoomStep: " << step << ": " << newmin << " -> " << newmax << " (range " << newdist << ")" << endl;
535 #endif
536 
537  setDisplayExtents(newmin, newmax);
538 }
539 
540 RangeMapper *
542 {
543  auto model = ModelById::getAs<SparseTimeValueModel>(m_model);
544  if (!model) return nullptr;
545 
546  RangeMapper *mapper;
547 
548  double min, max;
549  bool logarithmic;
550  QString unit;
551  getValueExtents(min, max, logarithmic, unit);
552 
553  if (min == max) return nullptr;
554 
555  if (logarithmic) {
556  mapper = new LogRangeMapper(0, 100, min, max, unit);
557  } else {
558  mapper = new LinearRangeMapper(0, 100, min, max, unit);
559  }
560 
561  return mapper;
562 }
563 
564 EventVector
566 {
567  auto model = ModelById::getAs<SparseTimeValueModel>(m_model);
568  if (!model) return {};
569 
570  // Return all points at a frame f, where f is the closest frame to
571  // pixel coordinate x whose pixel coordinate is both within a
572  // small (but somewhat arbitrary) fuzz distance from x and within
573  // the current view. If there is no such frame, return an empty
574  // vector.
575 
576  sv_frame_t frame = v->getFrameForX(x);
577 
578  EventVector exact = model->getEventsStartingAt(frame);
579  if (!exact.empty()) return exact;
580 
581  // overspill == 1, so one event either side of the given span
582  EventVector neighbouring = model->getEventsWithin
583  (frame, model->getResolution(), 1);
584 
585  double fuzz = v->scaleSize(2);
586  sv_frame_t suitable = 0;
587  bool have = false;
588 
589  for (Event e: neighbouring) {
590  sv_frame_t f = e.getFrame();
591  if (f < v->getStartFrame() || f > v->getEndFrame()) {
592  continue;
593  }
594  int px = v->getXForFrame(f);
595  if ((px > x && px - x > fuzz) || (px < x && x - px > fuzz + 3)) {
596  continue;
597  }
598  if (!have) {
599  suitable = f;
600  have = true;
601  } else if (llabs(frame - f) < llabs(suitable - f)) {
602  suitable = f;
603  }
604  }
605 
606  if (have) {
607  return model->getEventsStartingAt(suitable);
608  } else {
609  return {};
610  }
611 }
612 
613 QString
614 TimeValueLayer::getLabelPreceding(sv_frame_t frame) const
615 {
616  auto model = ModelById::getAs<SparseTimeValueModel>(m_model);
617  if (!model || !model->hasTextLabels()) return "";
618 
619  Event e;
620  if (model->getNearestEventMatching
621  (frame,
622  [](Event e) { return e.hasLabel() && e.getLabel() != ""; },
623  EventSeries::Backward,
624  e)) {
625  return e.getLabel();
626  }
627 
628  return "";
629 }
630 
631 QString
633 {
634  int x = pos.x();
635 
636  auto model = ModelById::getAs<SparseTimeValueModel>(m_model);
637  if (!model || !model->getSampleRate()) return "";
638 
639  EventVector points = getLocalPoints(v, x);
640 
641  if (points.empty()) {
642  if (!model->isReady()) {
643  return tr("In progress");
644  } else {
645  return tr("No local points");
646  }
647  }
648 
649  sv_frame_t useFrame = points.begin()->getFrame();
650 
651  RealTime rt = RealTime::frame2RealTime(useFrame, model->getSampleRate());
652 
653  QString valueText;
654  float value = points.begin()->getValue();
655  QString unit = getScaleUnits();
656 
657  if (unit == "Hz") {
658  valueText = tr("%1 Hz (%2, %3)")
659  .arg(value)
660  .arg(Pitch::getPitchLabelForFrequency(value))
661  .arg(Pitch::getPitchForFrequency(value));
662  } else if (unit != "") {
663  valueText = tr("%1 %2").arg(value).arg(unit);
664  } else {
665  valueText = tr("%1").arg(value);
666  }
667 
668  QString text;
669 
670  if (points.begin()->getLabel() == "") {
671  text = QString(tr("Time:\t%1\nValue:\t%2\nNo label"))
672  .arg(rt.toText(true).c_str())
673  .arg(valueText);
674  } else {
675  text = QString(tr("Time:\t%1\nValue:\t%2\nLabel:\t%4"))
676  .arg(rt.toText(true).c_str())
677  .arg(valueText)
678  .arg(points.begin()->getLabel());
679  }
680 
681  pos = QPoint(v->getXForFrame(useFrame),
682  getYForValue(v, points.begin()->getValue()));
683  return text;
684 }
685 
686 bool
688  sv_frame_t &frame,
689  int &resolution,
690  SnapType snap, int ycoord) const
691 {
692  auto model = ModelById::getAs<SparseTimeValueModel>(m_model);
693  if (!model) {
694  return Layer::snapToFeatureFrame(v, frame, resolution, snap, ycoord);
695  }
696 
697  // SnapLeft / SnapRight: return frame of nearest feature in that
698  // direction no matter how far away
699  //
700  // SnapNeighbouring: return frame of feature that would be used in
701  // an editing operation, i.e. closest feature in either direction
702  // but only if it is "close enough"
703 
704  resolution = model->getResolution();
705 
706  if (snap == SnapNeighbouring) {
707  EventVector points = getLocalPoints(v, v->getXForFrame(frame));
708  if (points.empty()) return false;
709  frame = points.begin()->getFrame();
710  return true;
711  }
712 
713  Event e;
714  if (model->getNearestEventMatching
715  (frame,
716  [](Event) { return true; },
717  snap == SnapLeft ? EventSeries::Backward : EventSeries::Forward,
718  e)) {
719  frame = e.getFrame();
720  return true;
721  }
722 
723  return false;
724 }
725 
726 bool
728  sv_frame_t &frame,
729  int &resolution,
730  SnapType snap) const
731 {
732  auto model = ModelById::getAs<SparseTimeValueModel>(m_model);
733  if (!model) {
734  return Layer::snapToSimilarFeature(v, frame, resolution, snap);
735  }
736 
737  // snap is only permitted to be SnapLeft or SnapRight here.
738 
739  resolution = model->getResolution();
740 
741  Event ref;
742  Event e;
743  float matchvalue;
744  bool found;
745 
746  found = model->getNearestEventMatching
747  (frame, [](Event) { return true; }, EventSeries::Backward, ref);
748 
749  if (!found) {
750  return false;
751  }
752 
753  matchvalue = ref.getValue();
754 
755  found = model->getNearestEventMatching
756  (frame,
757  [matchvalue](Event e) {
758  double epsilon = 0.0001;
759  return fabs(e.getValue() - matchvalue) < epsilon;
760  },
761  snap == SnapLeft ? EventSeries::Backward : EventSeries::Forward,
762  e);
763 
764  if (!found) {
765  return false;
766  }
767 
768  frame = e.getFrame();
769  return true;
770 }
771 
772 void
773 TimeValueLayer::getScaleExtents(LayerGeometryProvider *v, double &min, double &max, bool &log) const
774 {
775  min = 0.0;
776  max = 0.0;
777  log = false;
778 
779  auto model = ModelById::getAs<SparseTimeValueModel>(m_model);
780  if (!model) return;
781 
782  if (shouldAutoAlign()) {
783 
784  if (!v->getVisibleExtentsForUnit(getScaleUnits(), min, max, log)) {
785  min = model->getValueMinimum();
786  max = model->getValueMaximum();
787  } else {
788 #ifdef DEBUG_TIME_VALUE_LAYER
789  SVCERR << "getScaleExtents: view returned min = " << min
790  << ", max = " << max << ", log = " << log << endl;
791 #endif
792  if (log) {
793  LogRange::mapRange(min, max);
794 #ifdef DEBUG_TIME_VALUE_LAYER
795  SVCERR << "getScaleExtents: mapped to min = " << min
796  << ", max = " << max << endl;
797 #endif
798  }
799  }
800 
801  } else if (m_verticalScale == PlusMinusOneScale) {
802 
803  min = -1.0;
804  max = 1.0;
805 
806  } else {
807 
808  getDisplayExtents(min, max);
809 
810  if (m_verticalScale == LogScale) {
811  LogRange::mapRange(min, max);
812  log = true;
813  }
814  }
815 
816 #ifdef DEBUG_TIME_VALUE_LAYER
817  cerr << "TimeValueLayer::getScaleExtents: min = " << min << ", max = " << max << endl;
818 #endif
819 }
820 
821 int
823 {
824  double min = 0.0, max = 0.0;
825  bool logarithmic = false;
826  int h = v->getPaintHeight();
827 
828  getScaleExtents(v, min, max, logarithmic);
829 
830 #ifdef DEBUG_TIME_VALUE_LAYER
831  SVCERR << "getYForValue(" << val << "): min " << min << ", max "
832  << max << ", log " << logarithmic << endl;
833 #endif
834 
835  if (logarithmic) {
836  val = LogRange::map(val);
837 #ifdef DEBUG_TIME_VALUE_LAYER
838  SVCERR << "-> " << val << endl;
839 #endif
840  }
841 
842  return int(h - ((val - min) * h) / (max - min));
843 }
844 
845 double
847 {
848  double min = 0.0, max = 0.0;
849  bool logarithmic = false;
850  int h = v->getPaintHeight();
851 
852  getScaleExtents(v, min, max, logarithmic);
853 
854  double val = min + (double(h - y) * double(max - min)) / h;
855 
856  if (logarithmic) {
857  val = LogRange::map(val);
858  }
859 
860  return val;
861 }
862 
863 bool
865 {
866  QString unit = getScaleUnits();
867  return (m_verticalScale == AutoAlignScale && unit != "");
868 }
869 
870 QColor
872 {
873  double min, max;
874  bool log;
875  getScaleExtents(v, min, max, log);
876 
877  if (min > max) std::swap(min, max);
878  if (max == min) max = min + 1;
879 
880  if (log) {
881  val = LogRange::map(val);
882  }
883 
884 #ifdef DEBUG_TIME_VALUE_LAYER
885  cerr << "TimeValueLayer::getColourForValue: min " << min << ", max "
886  << max << ", log " << log << ", value " << val << endl;
887 #endif
888 
889  QColor solid = ColourMapper(m_colourMap, m_colourInverted, min, max).map(val);
890  return QColor(solid.red(), solid.green(), solid.blue(), 120);
891 }
892 
893 int
894 TimeValueLayer::getDefaultColourHint(bool darkbg, bool &impose)
895 {
896  impose = false;
898  (QString(darkbg ? "Bright Green" : "Green"));
899 }
900 
901 void
902 TimeValueLayer::paint(LayerGeometryProvider *v, QPainter &paint, QRect rect) const
903 {
904  auto model = ModelById::getAs<SparseTimeValueModel>(m_model);
905  if (!model || !model->isOK()) return;
906 
907  sv_samplerate_t sampleRate = model->getSampleRate();
908  if (!sampleRate) return;
909 
910 #ifdef DEBUG_TIME_VALUE_LAYER
911  SVCERR << "TimeValueLayer[" << this << ", model " << getModel() << "]::paint in " << v->getId() << endl;
912 #endif
913 
914  paint.setRenderHint(QPainter::Antialiasing, false);
915 
916 // Profiler profiler("TimeValueLayer::paint", true);
917 
918  int x0 = rect.left();
919  int x1 = x0 + rect.width();
920  sv_frame_t frame0 = v->getFrameForX(x0);
921  sv_frame_t frame1 = v->getFrameForX(x1);
922  if (m_derivative) --frame0;
923 
924  EventVector points(model->getEventsWithin(frame0, frame1 - frame0, 1));
925 
926 #ifdef DEBUG_TIME_VALUE_LAYER
927  SVCERR << "TimeValueLayer[" << this << "]::paint in " << v->getId()
928  << ": pixel extents " << x0 << " to " << x1 << ", frame extents "
929  << frame0 << " to " << frame1 << " yielding " << points.size()
930  << " points (of " << model->getAllEvents().size()
931  << " from frames " << model->getStartFrame() << " to "
932  << model->getEndFrame() << ")" << endl;
933 #endif
934 
935  if (points.empty()) return;
936 
937  paint.setPen(getBaseQColor());
938 
939  QColor brushColour(getBaseQColor());
940  brushColour.setAlpha(80);
941  paint.setBrush(brushColour);
942 
943 #ifdef DEBUG_TIME_VALUE_LAYER
944  cerr << "TimeValueLayer::paint: resolution is "
945  << model->getResolution() << " frames" << endl;
946 #endif
947 
948  double min = model->getValueMinimum();
949  double max = model->getValueMaximum();
950  if (max == min) max = min + 1.0;
951 
952  int origin = int(nearbyint(v->getPaintHeight() -
953  (-min * v->getPaintHeight()) / (max - min)));
954 
955  QPoint localPos;
956  sv_frame_t illuminateFrame = -1;
957 
958  if (v->shouldIlluminateLocalFeatures(this, localPos)) {
959  EventVector localPoints = getLocalPoints(v, localPos.x());
960 #ifdef DEBUG_TIME_VALUE_LAYER
961  cerr << "TimeValueLayer: " << localPoints.size() << " local points" << endl;
962 #endif
963  if (!localPoints.empty()) {
964  illuminateFrame = localPoints.begin()->getFrame();
965  }
966  }
967 
968  int w =
969  v->getXForFrame(frame0 + model->getResolution()) -
970  v->getXForFrame(frame0);
971 
972  if (m_plotStyle == PlotStems) {
973  if (w < 2) w = 2;
974  } else {
975  if (w < 1) w = 1;
976  }
977 
978  paint.save();
979 
980  QPainterPath path;
981  int pointCount = 0;
982 
983  int textY = 0;
984  if (m_plotStyle == PlotSegmentation) {
985  textY = v->getTextLabelYCoord(this, paint);
986  } else {
987  int originY = getYForValue(v, 0.f);
988  if (originY > 0 && originY < v->getPaintHeight()) {
989  paint.save();
990  paint.setPen(getPartialShades(v)[1]);
991  paint.drawLine(x0, originY, x1, originY);
992  paint.restore();
993  }
994  }
995 
996  sv_frame_t prevFrame = 0;
997 
998  for (EventVector::const_iterator i = points.begin();
999  i != points.end(); ++i) {
1000 
1001  if (m_derivative && i == points.begin()) continue;
1002 
1003  Event p(*i);
1004 
1005  double value = p.getValue();
1006  if (m_derivative) {
1007  EventVector::const_iterator j = i;
1008  --j;
1009  value -= j->getValue();
1010  }
1011 
1012  int x = v->getXForFrame(p.getFrame());
1013  int y = getYForValue(v, value);
1014 
1015  bool gap = false;
1016  if (m_plotStyle == PlotDiscreteCurves) {
1017  if (value == 0.0) {
1018  // Treat zeros as gaps
1019  continue;
1020  }
1021  gap = (p.getFrame() > prevFrame &&
1022  (p.getFrame() - prevFrame >= model->getResolution() * 2));
1023  }
1024 
1025  if (m_plotStyle != PlotSegmentation) {
1026  textY = y - paint.fontMetrics().height()
1027  + paint.fontMetrics().ascent() - 1;
1028  if (textY < paint.fontMetrics().ascent() + 1) {
1029  textY = paint.fontMetrics().ascent() + 1;
1030  }
1031  }
1032 
1033  bool haveNext = false;
1034  double nvalue = 0.f;
1035  sv_frame_t nf = v->getModelsEndFrame();
1036  int nx = v->getXForFrame(nf);
1037  int ny = y;
1038 
1039  EventVector::const_iterator j = i;
1040  ++j;
1041 
1042  if (j != points.end()) {
1043  Event q(*j);
1044  nvalue = q.getValue();
1045  if (m_derivative) nvalue -= p.getValue();
1046  nf = q.getFrame();
1047  nx = v->getXForFrame(nf);
1048  ny = getYForValue(v, nvalue);
1049  haveNext = true;
1050  }
1051 
1052 // cout << "frame = " << p.getFrame() << ", x = " << x << ", haveNext = " << haveNext
1053 // << ", nx = " << nx << endl;
1054 
1055  QPen pen(getBaseQColor());
1056  QBrush brush(brushColour);
1057 
1059  pen = QPen(getBaseQColor(), 3);
1060  brush = QBrush(Qt::NoBrush);
1061  } else if (m_plotStyle == PlotSegmentation) {
1062  pen = QPen(getForegroundQColor(v));
1063  brush = QBrush(getColourForValue(v, value));
1064  } else if (m_plotStyle == PlotLines ||
1065  m_plotStyle == PlotCurve) {
1066  brush = QBrush(Qt::NoBrush);
1067  }
1068 
1069  paint.setPen(v->scalePen(pen));
1070  paint.setBrush(brush);
1071 
1072  if (m_plotStyle == PlotStems) {
1073  if (y < origin - 1) {
1074  paint.drawLine(x + w/2, y + 1, x + w/2, origin);
1075  } else if (y > origin + 1) {
1076  paint.drawLine(x + w/2, origin, x + w/2, y - 1);
1077  }
1078  }
1079 
1080  bool illuminate = false;
1081 
1082  if (illuminateFrame == p.getFrame()) {
1083 
1084  // not equipped to illuminate the right section in line
1085  // or curve mode
1086 
1087  if (m_plotStyle != PlotCurve &&
1089  m_plotStyle != PlotLines) {
1090  illuminate = true;
1091  }
1092  }
1093 
1094  if (m_plotStyle != PlotLines &&
1095  m_plotStyle != PlotCurve &&
1098  if (illuminate) {
1099  paint.save();
1100  paint.setPen(v->scalePen(getForegroundQColor(v)));
1101  paint.setBrush(getForegroundQColor(v));
1102  }
1103  if (m_plotStyle != PlotStems ||
1104  w > 1) {
1105  paint.drawRect(x, y - 1, w, 2);
1106  }
1107  if (illuminate) {
1108  paint.restore();
1109  }
1110  }
1111 
1113  m_plotStyle == PlotLines ||
1115  m_plotStyle == PlotCurve) {
1116 
1117  if (haveNext) {
1118 
1120 
1121  paint.save();
1122  paint.setPen(v->scalePen(brushColour));
1123  paint.drawLine(x + w, y, nx, ny);
1124  paint.restore();
1125 
1126  } else if (m_plotStyle == PlotLines) {
1127 
1128  if (pointCount == 0) {
1129  path.moveTo(x + w/2, y);
1130  }
1131 
1132 // paint.drawLine(x + w/2, y, nx + w/2, ny);
1133  path.lineTo(nx + w/2, ny);
1134 
1135  } else {
1136 
1137  double x0 = x + double(w)/2;
1138  double x1 = nx + double(w)/2;
1139 
1140  double y0 = y;
1141  double y1 = ny;
1142 
1144  bool nextGap =
1145  (nvalue == 0.0) ||
1146  (nf - p.getFrame() >= model->getResolution() * 2);
1147  if (nextGap) {
1148  x1 = x0;
1149  y1 = y0;
1150  }
1151  }
1152 
1153  if (pointCount == 0 || gap) {
1154  path.moveTo((x0 + x1) / 2, (y0 + y1) / 2);
1155  }
1156 
1157  if (nx - x > 5) {
1158  path.cubicTo(x0, y0,
1159  x0, y0,
1160  (x0 + x1) / 2, (y0 + y1) / 2);
1161 
1162  // // or
1163  // path.quadTo(x0, y0, (x0 + x1) / 2, (y0 + y1) / 2);
1164 
1165  } else {
1166  path.lineTo(x0, y0);
1167  path.lineTo((x0 + x1) / 2, (y0 + y1) / 2);
1168  }
1169  }
1170  }
1171  }
1172 
1173  if (m_plotStyle == PlotSegmentation) {
1174 
1175 #ifdef DEBUG_TIME_VALUE_LAYER
1176  cerr << "drawing rect" << endl;
1177 #endif
1178 
1179  if (nx <= x) continue;
1180 
1181  paint.setPen(v->scalePen(QPen(getForegroundQColor(v), 2)));
1182 
1183  if (!illuminate) {
1184  if (!m_drawSegmentDivisions ||
1185  nx < x + 5 ||
1186  x >= v->getPaintWidth() - 1) {
1187  paint.setPen(Qt::NoPen);
1188  }
1189  }
1190 
1191  paint.drawRect(x, -1, nx - x, v->getPaintHeight() + 1);
1192  }
1193 
1194  if (v->shouldShowFeatureLabels()) {
1195 
1196  QString label = p.getLabel();
1197  bool italic = false;
1198 
1199  if (label == "" &&
1200  (m_plotStyle == PlotPoints ||
1203  char lc[20];
1204  snprintf(lc, 20, "%.3g", p.getValue());
1205  label = lc;
1206  italic = true;
1207  }
1208 
1209  // Qt 5.13 deprecates QFontMetrics::width(), but its suggested
1210  // replacement (horizontalAdvance) was only added in Qt 5.11
1211  // which is too new for us
1212 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
1213 
1214  if (label != "") {
1215  // Quick test for 20px before we do the slower test using metrics
1216  bool haveRoom = (nx > x + 20);
1217  haveRoom = (haveRoom &&
1218  (nx > x + 6 + paint.fontMetrics().width(label)));
1219  if (haveRoom ||
1220  (!haveNext &&
1221  (pointCount == 0 || !italic))) {
1223  (v, paint, x + 5, textY, label,
1224  italic ?
1227  }
1228  }
1229  }
1230 
1231  prevFrame = p.getFrame();
1232  ++pointCount;
1233  }
1234 
1236  paint.setRenderHint(QPainter::Antialiasing, true);
1237  paint.drawPath(path);
1238  } else if ((m_plotStyle == PlotCurve || m_plotStyle == PlotLines)
1239  && !path.isEmpty()) {
1240  paint.setRenderHint(QPainter::Antialiasing, pointCount <= v->getPaintWidth());
1241  paint.drawPath(path);
1242  }
1243 
1244  paint.restore();
1245 
1246  // looks like save/restore doesn't deal with this:
1247  paint.setRenderHint(QPainter::Antialiasing, false);
1248 }
1249 
1250 int
1252 {
1253  auto model = ModelById::getAs<SparseTimeValueModel>(m_model);
1254  if (!model) {
1255  return 0;
1256  } else if (shouldAutoAlign() && !valueExtentsMatchMine(v)) {
1257  return 0;
1258  } else if (m_plotStyle == PlotSegmentation) {
1259  if (m_verticalScale == LogScale) {
1260  return LogColourScale().getWidth(v, paint);
1261  } else {
1262  return LinearColourScale().getWidth(v, paint);
1263  }
1264  } else {
1265  if (m_verticalScale == LogScale) {
1266  return LogNumericalScale().getWidth(v, paint) + 10; // for piano
1267  } else {
1268  return LinearNumericalScale().getWidth(v, paint);
1269  }
1270  }
1271 }
1272 
1273 void
1275 {
1276  auto model = ModelById::getAs<SparseTimeValueModel>(m_model);
1277  if (!model || model->isEmpty()) return;
1278 
1279  QString unit;
1280  double min, max;
1281  bool logarithmic;
1282 
1283  int w = getVerticalScaleWidth(v, false, paint);
1284  int h = v->getPaintHeight();
1285 
1286  if (m_plotStyle == PlotSegmentation) {
1287 
1288  getValueExtents(min, max, logarithmic, unit);
1289 
1290  if (logarithmic) {
1291  LogRange::mapRange(min, max);
1292  LogColourScale().paintVertical(v, this, paint, 0, min, max);
1293  } else {
1294  LinearColourScale().paintVertical(v, this, paint, 0, min, max);
1295  }
1296 
1297  } else {
1298 
1299  getScaleExtents(v, min, max, logarithmic);
1300 
1301  if (logarithmic) {
1302  LogNumericalScale().paintVertical(v, this, paint, 0, min, max);
1303  } else {
1304  LinearNumericalScale().paintVertical(v, this, paint, 0, min, max);
1305  }
1306 
1307  if (logarithmic && (getScaleUnits() == "Hz")) {
1309  (v, paint, QRect(w - 10, 0, 10, h),
1310  LogRange::unmap(min),
1311  LogRange::unmap(max));
1312  paint.drawLine(w, 0, w, h);
1313  }
1314  }
1315 
1316  if (getScaleUnits() != "") {
1317  int mw = w - 5;
1318  paint.drawText(5,
1319  5 + paint.fontMetrics().ascent(),
1321  paint.fontMetrics(),
1322  mw));
1323  }
1324 }
1325 
1326 void
1328 {
1329 #ifdef DEBUG_TIME_VALUE_LAYER
1330  cerr << "TimeValueLayer::drawStart(" << e->x() << "," << e->y() << ")" << endl;
1331 #endif
1332 
1333  auto model = ModelById::getAs<SparseTimeValueModel>(m_model);
1334  if (!model) return;
1335 
1336  sv_frame_t frame = v->getFrameForX(e->x());
1337  int resolution = model->getResolution();
1338  if (frame < 0) frame = 0;
1339  frame = (frame / resolution) * resolution;
1340 
1341  double value = getValueForY(v, e->y());
1342 
1343  bool havePoint = false;
1344 
1345  EventVector points = getLocalPoints(v, e->x());
1346  if (!points.empty()) {
1347  for (EventVector::iterator i = points.begin();
1348  i != points.end(); ++i) {
1349  if (((i->getFrame() / resolution) * resolution) != frame) {
1350 #ifdef DEBUG_TIME_VALUE_LAYER
1351  cerr << "ignoring out-of-range frame at " << i->getFrame() << endl;
1352 #endif
1353  continue;
1354  }
1355  m_editingPoint = *i;
1356  havePoint = true;
1357  }
1358  }
1359 
1360  if (!havePoint) {
1361  m_editingPoint = Event(frame, float(value), tr("New Point"));
1362  }
1363 
1365 
1367  m_editingCommand = new ChangeEventsCommand(m_model.untyped, tr("Draw Point"));
1368  if (!havePoint) {
1370  }
1371 
1372  m_editing = true;
1373 }
1374 
1375 void
1377 {
1378 #ifdef DEBUG_TIME_VALUE_LAYER
1379  cerr << "TimeValueLayer::drawDrag(" << e->x() << "," << e->y() << ")" << endl;
1380 #endif
1381 
1382  auto model = ModelById::getAs<SparseTimeValueModel>(m_model);
1383  if (!model || !m_editing) return;
1384 
1385  sv_frame_t frame = v->getFrameForX(e->x());
1386  int resolution = model->getResolution();
1387  if (frame < 0) frame = 0;
1388  frame = (frame / resolution) * resolution;
1389 
1390  double value = getValueForY(v, e->y());
1391 
1392  EventVector points = getLocalPoints(v, e->x());
1393 
1394 #ifdef DEBUG_TIME_VALUE_LAYER
1395  cerr << points.size() << " points" << endl;
1396 #endif
1397 
1398  bool havePoint = false;
1399 
1400  if (!points.empty()) {
1401  for (EventVector::iterator i = points.begin();
1402  i != points.end(); ++i) {
1403  if (i->getFrame() == m_editingPoint.getFrame() &&
1404  i->getValue() == m_editingPoint.getValue()) {
1405 #ifdef DEBUG_TIME_VALUE_LAYER
1406  cerr << "ignoring current editing point at " << i->getFrame() << ", " << i->getValue() << endl;
1407 #endif
1408  continue;
1409  }
1410  if (((i->getFrame() / resolution) * resolution) != frame) {
1411 #ifdef DEBUG_TIME_VALUE_LAYER
1412  cerr << "ignoring out-of-range frame at " << i->getFrame() << endl;
1413 #endif
1414  continue;
1415  }
1416 #ifdef DEBUG_TIME_VALUE_LAYER
1417  cerr << "adjusting to new point at " << i->getFrame() << ", " << i->getValue() << endl;
1418 #endif
1419  m_editingPoint = *i;
1422  havePoint = true;
1423  }
1424  }
1425 
1426  if (!havePoint) {
1427  if (frame == m_editingPoint.getFrame()) {
1429  }
1430  }
1431 
1433  .withFrame(frame)
1434  .withValue(float(value));
1436 }
1437 
1438 void
1440 {
1441 #ifdef DEBUG_TIME_VALUE_LAYER
1442  cerr << "TimeValueLayer::drawEnd" << endl;
1443 #endif
1444  auto model = ModelById::getAs<SparseTimeValueModel>(m_model);
1445  if (!model || !m_editing) return;
1447  m_editingCommand = nullptr;
1448  m_editing = false;
1449 }
1450 
1451 void
1453 {
1454  auto model = ModelById::getAs<SparseTimeValueModel>(m_model);
1455  if (!model) return;
1456 
1457  EventVector points = getLocalPoints(v, e->x());
1458  if (points.empty()) return;
1459 
1460  m_editingPoint = *points.begin();
1461 
1462  if (m_editingCommand) {
1464  m_editingCommand = nullptr;
1465  }
1466 
1467  m_editing = true;
1468 }
1469 
1470 void
1472 {
1473 }
1474 
1475 void
1477 {
1478  auto model = ModelById::getAs<SparseTimeValueModel>(m_model);
1479  if (!model || !m_editing) return;
1480 
1481  m_editing = false;
1482 
1483  EventVector points = getLocalPoints(v, e->x());
1484  if (points.empty()) return;
1485  if (points.begin()->getFrame() != m_editingPoint.getFrame() ||
1486  points.begin()->getValue() != m_editingPoint.getValue()) return;
1487 
1488  m_editingCommand = new ChangeEventsCommand(m_model.untyped, tr("Erase Point"));
1491  m_editingCommand = nullptr;
1492  m_editing = false;
1493 }
1494 
1495 void
1497 {
1498 #ifdef DEBUG_TIME_VALUE_LAYER
1499  cerr << "TimeValueLayer::editStart(" << e->x() << "," << e->y() << ")" << endl;
1500 #endif
1501 
1502  auto model = ModelById::getAs<SparseTimeValueModel>(m_model);
1503  if (!model) return;
1504 
1505  EventVector points = getLocalPoints(v, e->x());
1506  if (points.empty()) return;
1507 
1508  m_editingPoint = *points.begin();
1510 
1511  if (m_editingCommand) {
1513  m_editingCommand = nullptr;
1514  }
1515 
1516  m_editing = true;
1517 }
1518 
1519 void
1521 {
1522 #ifdef DEBUG_TIME_VALUE_LAYER
1523  cerr << "TimeValueLayer::editDrag(" << e->x() << "," << e->y() << ")" << endl;
1524 #endif
1525 
1526  auto model = ModelById::getAs<SparseTimeValueModel>(m_model);
1527  if (!model || !m_editing) return;
1528 
1529  sv_frame_t frame = v->getFrameForX(e->x());
1530  if (frame < 0) frame = 0;
1531  frame = frame / model->getResolution() * model->getResolution();
1532 
1533  double value = getValueForY(v, e->y());
1534 
1535  if (!m_editingCommand) {
1536  m_editingCommand = new ChangeEventsCommand(m_model.untyped, tr("Drag Point"));
1537  }
1538 
1541  .withFrame(frame)
1542  .withValue(float(value));
1544 }
1545 
1546 void
1548 {
1549 #ifdef DEBUG_TIME_VALUE_LAYER
1550  cerr << "TimeValueLayer::editEnd" << endl;
1551 #endif
1552  auto model = ModelById::getAs<SparseTimeValueModel>(m_model);
1553  if (!model || !m_editing) return;
1554 
1555  if (m_editingCommand) {
1556 
1557  QString newName = m_editingCommand->getName();
1558 
1559  if (m_editingPoint.getFrame() != m_originalPoint.getFrame()) {
1560  if (m_editingPoint.getValue() != m_originalPoint.getValue()) {
1561  newName = tr("Edit Point");
1562  } else {
1563  newName = tr("Relocate Point");
1564  }
1565  } else {
1566  newName = tr("Change Point Value");
1567  }
1568 
1569  m_editingCommand->setName(newName);
1571  }
1572 
1573  m_editingCommand = nullptr;
1574  m_editing = false;
1575 }
1576 
1577 bool
1579 {
1580  auto model = ModelById::getAs<SparseTimeValueModel>(m_model);
1581  if (!model) return false;
1582 
1583  EventVector points = getLocalPoints(v, e->x());
1584  if (points.empty()) return false;
1585 
1586  Event point = *points.begin();
1587 
1588  ItemEditDialog *dialog = new ItemEditDialog
1589  (model->getSampleRate(),
1593  getScaleUnits());
1594 
1595  dialog->setFrameTime(point.getFrame());
1596  dialog->setValue(point.getValue());
1597  dialog->setText(point.getLabel());
1598 
1599  if (dialog->exec() == QDialog::Accepted) {
1600 
1601  Event newPoint = point
1602  .withFrame(dialog->getFrameTime())
1603  .withValue(dialog->getValue())
1604  .withLabel(dialog->getText());
1605 
1606  ChangeEventsCommand *command =
1607  new ChangeEventsCommand(m_model.untyped, tr("Edit Point"));
1608  command->remove(point);
1609  command->add(newPoint);
1610  finish(command);
1611  }
1612 
1613  delete dialog;
1614  return true;
1615 }
1616 
1617 void
1618 TimeValueLayer::moveSelection(Selection s, sv_frame_t newStartFrame)
1619 {
1620  auto model = ModelById::getAs<SparseTimeValueModel>(m_model);
1621  if (!model) return;
1622 
1623  ChangeEventsCommand *command =
1624  new ChangeEventsCommand(m_model.untyped, tr("Drag Selection"));
1625 
1626  EventVector points =
1627  model->getEventsWithin(s.getStartFrame(), s.getDuration());
1628 
1629  for (Event p: points) {
1630 
1631  Event newPoint = p.withFrame
1632  (p.getFrame() + newStartFrame - s.getStartFrame());
1633  command->remove(p);
1634  command->add(newPoint);
1635  }
1636 
1637  finish(command);
1638 }
1639 
1640 void
1641 TimeValueLayer::resizeSelection(Selection s, Selection newSize)
1642 {
1643  auto model = ModelById::getAs<SparseTimeValueModel>(m_model);
1644  if (!model || !s.getDuration()) return;
1645 
1646  ChangeEventsCommand *command =
1647  new ChangeEventsCommand(m_model.untyped, tr("Resize Selection"));
1648 
1649  EventVector points =
1650  model->getEventsWithin(s.getStartFrame(), s.getDuration());
1651 
1652  double ratio = double(newSize.getDuration()) / double(s.getDuration());
1653  double oldStart = double(s.getStartFrame());
1654  double newStart = double(newSize.getStartFrame());
1655 
1656  for (Event p: points) {
1657 
1658  double newFrame = (double(p.getFrame()) - oldStart) * ratio + newStart;
1659 
1660  Event newPoint = p
1661  .withFrame(lrint(newFrame));
1662  command->remove(p);
1663  command->add(newPoint);
1664  }
1665 
1666  finish(command);
1667 }
1668 
1669 void
1671 {
1672  auto model = ModelById::getAs<SparseTimeValueModel>(m_model);
1673  if (!model) return;
1674 
1675  ChangeEventsCommand *command =
1676  new ChangeEventsCommand(m_model.untyped, tr("Delete Selected Points"));
1677 
1678  EventVector points =
1679  model->getEventsWithin(s.getStartFrame(), s.getDuration());
1680 
1681  for (Event p: points) {
1682  command->remove(p);
1683  }
1684 
1685  finish(command);
1686 }
1687 
1688 void
1689 TimeValueLayer::copy(LayerGeometryProvider *v, Selection s, Clipboard &to)
1690 {
1691  auto model = ModelById::getAs<SparseTimeValueModel>(m_model);
1692  if (!model) return;
1693 
1694  EventVector points =
1695  model->getEventsWithin(s.getStartFrame(), s.getDuration());
1696 
1697  for (Event p: points) {
1698  to.addPoint(p.withReferenceFrame(alignToReference(v, p.getFrame())));
1699  }
1700 }
1701 
1702 bool
1704  sv_frame_t /* frameOffset */, bool interactive)
1705 {
1706  auto model = ModelById::getAs<SparseTimeValueModel>(m_model);
1707  if (!model) return false;
1708 
1709  EventVector points = from.getPoints();
1710 
1711  bool realign = false;
1712 
1713  if (clipboardHasDifferentAlignment(v, from)) {
1714 
1715  QMessageBox::StandardButton button =
1716  QMessageBox::question(v->getView(), tr("Re-align pasted items?"),
1717  tr("The items you are pasting came from a layer with different source material from this one. Do you want to re-align them in time, to match the source material for this layer?"),
1718  QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel,
1719  QMessageBox::Yes);
1720 
1721  if (button == QMessageBox::Cancel) {
1722  return false;
1723  }
1724 
1725  if (button == QMessageBox::Yes) {
1726  realign = true;
1727  }
1728  }
1729 
1730  ChangeEventsCommand *command =
1731  new ChangeEventsCommand(m_model.untyped, tr("Paste"));
1732 
1733  enum ValueAvailability {
1734  UnknownAvailability,
1735  NoValues,
1736  SomeValues,
1737  AllValues
1738  };
1739 
1740  Labeller::ValueType generation = Labeller::ValueNone;
1741 
1742  bool haveUsableLabels = false;
1743  Labeller labeller;
1744  labeller.setSampleRate(model->getSampleRate());
1745 
1746  if (interactive) {
1747 
1748  ValueAvailability availability = UnknownAvailability;
1749 
1750  for (EventVector::const_iterator i = points.begin();
1751  i != points.end(); ++i) {
1752 
1753  if (availability == UnknownAvailability) {
1754  if (i->hasValue()) availability = AllValues;
1755  else availability = NoValues;
1756  continue;
1757  }
1758 
1759  if (i->hasValue()) {
1760  if (availability == NoValues) {
1761  availability = SomeValues;
1762  }
1763  } else {
1764  if (availability == AllValues) {
1765  availability = SomeValues;
1766  }
1767  }
1768 
1769  if (!haveUsableLabels) {
1770  if (i->hasLabel()) {
1771  if (i->getLabel().contains(QRegExp("[0-9]"))) {
1772  haveUsableLabels = true;
1773  }
1774  }
1775  }
1776 
1777  if (availability == SomeValues && haveUsableLabels) break;
1778  }
1779 
1780  if (availability == NoValues || availability == SomeValues) {
1781 
1782  QString text;
1783  if (availability == NoValues) {
1784  text = tr("The items you are pasting do not have values.\nWhat values do you want to use for these items?");
1785  } else {
1786  text = tr("Some of the items you are pasting do not have values.\nWhat values do you want to use for these items?");
1787  }
1788 
1789  Labeller::TypeNameMap names = labeller.getTypeNames();
1790 
1791  QStringList options;
1792  std::vector<Labeller::ValueType> genopts;
1793 
1794  for (Labeller::TypeNameMap::const_iterator i = names.begin();
1795  i != names.end(); ++i) {
1796  if (i->first == Labeller::ValueNone) options << tr("Zero for all items");
1797  else options << i->second;
1798  genopts.push_back(i->first);
1799  }
1800 
1801  static int prevSelection = 0;
1802 
1803  bool ok = false;
1804  QString selected = ListInputDialog::getItem
1805  (nullptr, tr("Choose value calculation"),
1806  text, options, prevSelection, &ok);
1807 
1808  if (!ok) {
1809  delete command;
1810  return false;
1811  }
1812  int selection = 0;
1813  generation = Labeller::ValueNone;
1814 
1815  for (QStringList::const_iterator i = options.begin();
1816  i != options.end(); ++i) {
1817  if (selected == *i) {
1818  generation = genopts[selection];
1819  break;
1820  }
1821  ++selection;
1822  }
1823 
1824  labeller.setType(generation);
1825 
1826  if (generation == Labeller::ValueFromCyclicalCounter ||
1827  generation == Labeller::ValueFromTwoLevelCounter) {
1828  int cycleSize = QInputDialog::getInt
1829  (nullptr, tr("Select cycle size"),
1830  tr("Cycle size:"), 4, 2, 16, 1);
1831  labeller.setCounterCycleSize(cycleSize);
1832  }
1833 
1834  prevSelection = selection;
1835  }
1836  }
1837 
1838  Event prevPoint;
1839 
1840  for (EventVector::const_iterator i = points.begin();
1841  i != points.end(); ++i) {
1842 
1843  sv_frame_t frame = 0;
1844 
1845  if (!realign) {
1846 
1847  frame = i->getFrame();
1848 
1849  } else {
1850 
1851  if (i->hasReferenceFrame()) {
1852  frame = i->getReferenceFrame();
1853  frame = alignFromReference(v, frame);
1854  } else {
1855  frame = i->getFrame();
1856  }
1857  }
1858 
1859  Event newPoint = i->withFrame(frame);
1860 
1861  if (!i->hasLabel() && i->hasValue()) {
1862  newPoint = newPoint.withLabel(QString("%1").arg(i->getValue()));
1863  }
1864 
1865  bool usePrev = false;
1866  Event formerPrevPoint = prevPoint;
1867 
1868  if (!i->hasValue()) {
1869 #ifdef DEBUG_TIME_VALUE_LAYER
1870  cerr << "Setting value on point at " << newPoint.getFrame() << " from labeller";
1871  if (i == points.begin()) {
1872  cerr << ", no prev point" << endl;
1873  } else {
1874  cerr << ", prev point is at " << prevPoint.getFrame() << endl;
1875  }
1876 #endif
1877 
1878  Labeller::Revaluing valuing =
1879  labeller.revalue
1880  (newPoint, (i == points.begin()) ? nullptr : &prevPoint);
1881 
1882 #ifdef DEBUG_TIME_VALUE_LAYER
1883  cerr << "New point value = " << newPoint.getValue() << endl;
1884 #endif
1885  if (valuing.first == Labeller::AppliesToPreviousEvent) {
1886  usePrev = true;
1887  prevPoint = valuing.second;
1888  } else {
1889  newPoint = valuing.second;
1890  }
1891  }
1892 
1893  if (usePrev) {
1894  command->remove(formerPrevPoint);
1895  command->add(prevPoint);
1896  }
1897 
1898  prevPoint = newPoint;
1899  command->add(newPoint);
1900  }
1901 
1902  finish(command);
1903  return true;
1904 }
1905 
1906 void
1907 TimeValueLayer::toXml(QTextStream &stream,
1908  QString indent, QString extraAttributes) const
1909 {
1910  QString s;
1911 
1912  s += QString("plotStyle=\"%1\" "
1913  "verticalScale=\"%2\" "
1914  "scaleMinimum=\"%3\" "
1915  "scaleMaximum=\"%4\" "
1916  "drawDivisions=\"%5\" "
1917  "derivative=\"%6\" ")
1918  .arg(m_plotStyle)
1919  .arg(m_verticalScale)
1920  .arg(m_scaleMinimum)
1921  .arg(m_scaleMaximum)
1922  .arg(m_drawSegmentDivisions ? "true" : "false")
1923  .arg(m_derivative ? "true" : "false");
1924 
1925  // New-style colour map attribute, by string id rather than by
1926  // number
1927 
1928  s += QString("fillColourMap=\"%1\" ")
1930 
1931  // Old-style colour map attribute
1932 
1933  s += QString("colourMap=\"%1\" ")
1935 
1936  SingleColourLayer::toXml(stream, indent, extraAttributes + " " + s);
1937 }
1938 
1939 void
1940 TimeValueLayer::setProperties(const QXmlAttributes &attributes)
1941 {
1943 
1944  bool ok, alsoOk;
1945 
1946  QString colourMapId = attributes.value("fillColourMap");
1947  int colourMap = ColourMapper::getColourMapById(colourMapId);
1948  if (colourMap >= 0) {
1949  setFillColourMap(colourMap);
1950  } else {
1951  colourMap = attributes.value("colourMap").toInt(&ok);
1952  if (ok && colourMap < ColourMapper::getColourMapCount()) {
1953  setFillColourMap(colourMap);
1954  }
1955  }
1956 
1957  PlotStyle style = (PlotStyle)
1958  attributes.value("plotStyle").toInt(&ok);
1959  if (ok) setPlotStyle(style);
1960 
1961  VerticalScale scale = (VerticalScale)
1962  attributes.value("verticalScale").toInt(&ok);
1963  if (ok) setVerticalScale(scale);
1964 
1965  bool draw = (attributes.value("drawDivisions").trimmed() == "true");
1967 
1968  bool derivative = (attributes.value("derivative").trimmed() == "true");
1969  setShowDerivative(derivative);
1970 
1971  float min = attributes.value("scaleMinimum").toFloat(&ok);
1972  float max = attributes.value("scaleMaximum").toFloat(&alsoOk);
1973 #ifdef DEBUG_TIME_VALUE_LAYER
1974  cerr << "from properties: min = " << min << ", max = " << max << endl;
1975 #endif
1976  if (ok && alsoOk && min != max) setDisplayExtents(min, max);
1977 }
1978 
int getPropertyRangeAndValue(const PropertyName &, int *min, int *max, int *deflt) const override
float getValue() const
bool snapToSimilarFeature(LayerGeometryProvider *v, sv_frame_t &frame, int &resolution, SnapType snap) const override
Adjust the given frame to snap to the next feature that has "effectively" the same value as the featu...
ModelId getModel() const override
Return the ID of the model represented in this layer.
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
void editStart(LayerGeometryProvider *v, QMouseEvent *) override
void editEnd(LayerGeometryProvider *v, QMouseEvent *) override
QString getPropertyIconName(const PropertyName &) const override
void drawStart(LayerGeometryProvider *v, QMouseEvent *) override
PropertyList getProperties() const override
void getScaleExtents(LayerGeometryProvider *, double &min, double &max, bool &log) const
void editDrag(LayerGeometryProvider *v, QMouseEvent *) override
virtual bool shouldIlluminateLocalFeatures(const Layer *, QPoint &) const =0
void drawDrag(LayerGeometryProvider *v, QMouseEvent *) override
void finish(ChangeEventsCommand *command)
void modelReplaced()
RangeMapper * getNewVerticalZoomRangeMapper() const override
Create and return a range mapper for vertical zoom step values.
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
double getValueForY(LayerGeometryProvider *, int y) const override
void setPlotStyle(PlotStyle style)
virtual QColor getForegroundQColor(LayerGeometryProvider *v) const
void setFrameTime(sv_frame_t frame)
void toXml(QTextStream &stream, QString indent="", QString extraAttributes="") const override
void paintVertical(LayerGeometryProvider *v, const VerticalScaleLayer *layer, QPainter &paint, int x0, double minlog, double maxlog)
void paint(LayerGeometryProvider *v, QPainter &paint, QRect rect) const override
Paint the given rectangle of this layer onto the given view using the given painter, superimposing it on top of any existing material in that view.
sv_frame_t getFrameTime() const
virtual sv_frame_t getFrameForX(int x) const =0
Return the closest frame to the given pixel x-coordinate.
bool getDisplayExtents(double &min, double &max) const override
Return the minimum and maximum values within the visible area for the y axis of this layer...
QString getScaleUnits() const override
static int getColourMapCount()
Return the number of known colour maps.
void resizeSelection(Selection s, Selection newSize) override
std::vector< QColor > getPartialShades(LayerGeometryProvider *v) const
int getWidth(LayerGeometryProvider *v, QPainter &paint)
virtual double scaleSize(double size) const =0
VerticalScale m_verticalScale
virtual QColor getBaseQColor() const
void eraseEnd(LayerGeometryProvider *v, QMouseEvent *) override
void layerParameterRangesChanged()
void toXml(QTextStream &stream, QString indent="", QString extraAttributes="") const override
PropertyList getProperties() const override
int getWidth(LayerGeometryProvider *v, QPainter &paint)
virtual bool snapToSimilarFeature(LayerGeometryProvider *, sv_frame_t &, int &resolution, SnapType) const
Adjust the given frame to snap to the next feature that has "effectively" the same value as the featu...
Definition: Layer.h:251
static int getBackwardCompatibilityColourMap(int n)
Older versions of colour-handling code save and reload colour maps by numerical index and can&#39;t prope...
void modelChanged(ModelId)
void setText(QString text)
QString getPropertyValueLabel(const PropertyName &, int value) const override
Interface for classes that provide geometry information (such as size, start frame, and a large number of other properties) about the disposition of a layer.
static QString getColourMapLabel(int n)
Return a human-readable label for the colour map with the given index.
int getVerticalScaleWidth(LayerGeometryProvider *v, bool, QPainter &) const override
void setModel(ModelId model)
int getColourIndex(QString name) const
Return the index of the colour with the given name, if found in the database.
virtual sv_frame_t alignFromReference(LayerGeometryProvider *v, sv_frame_t frame) const
Definition: Layer.cpp:198
QString getPropertyGroupName(const PropertyName &) const override
static int getColourMapById(QString id)
Return the index for the colour map with the given machine-readable id string, or -1 if the id is not...
bool paste(LayerGeometryProvider *v, const Clipboard &from, sv_frame_t frameOffset, bool interactive) override
Paste from the given clipboard onto the layer at the given frame offset.
void layerParametersChanged()
void setProperty(const PropertyName &, int value) override
bool snapToFeatureFrame(LayerGeometryProvider *v, sv_frame_t &frame, int &resolution, SnapType snap, int ycoord) const override
Adjust the given frame to snap to the nearest feature, if possible.
void copy(LayerGeometryProvider *v, Selection s, Clipboard &to) override
QColor getColourForValue(LayerGeometryProvider *v, double value) const override
bool clipboardHasDifferentAlignment(LayerGeometryProvider *v, const Clipboard &clip) const
Definition: Layer.cpp:209
void setProperties(const QXmlAttributes &attributes) override
Set the particular properties of a layer (those specific to the subclass) from a set of XML attribute...
ChangeEventsCommand * m_editingCommand
void paintPianoVertical(LayerGeometryProvider *v, QPainter &paint, QRect rect, double minf, double maxf)
Definition: PianoScale.cpp:31
int getWidth(LayerGeometryProvider *v, QPainter &paint)
int getPropertyRangeAndValue(const PropertyName &, int *min, int *max, int *deflt) const override
SnapType
Definition: Layer.h:195
virtual sv_frame_t alignToReference(LayerGeometryProvider *v, sv_frame_t frame) const
Definition: Layer.cpp:187
QColor map(double value) const
Map the given value to a colour.
void paintVertical(LayerGeometryProvider *v, const VerticalScaleLayer *layer, QPainter &paint, int x0, double minf, double maxf)
bool isLayerScrollable(const LayerGeometryProvider *v) const override
This should return true if the layer can safely be scrolled automatically by a given view (simply cop...
A class for mapping intensity values onto various colour maps.
Definition: ColourMapper.h:27
int getWidth(LayerGeometryProvider *v, QPainter &paint)
bool getValueExtents(double &min, double &max, bool &logarithmic, QString &unit) const override
Return the minimum and maximum values for the y axis of the model in this layer, as well as whether t...
virtual bool shouldShowFeatureLabels() const =0
void connectSignals(ModelId)
Definition: Layer.cpp:49
virtual int getPaintHeight() const
PropertyType getPropertyType(const PropertyName &) const override
bool valueExtentsMatchMine(LayerGeometryProvider *v) const
Definition: Layer.cpp:628
int getVerticalZoomSteps(int &defaultStep) const override
Get the number of vertical zoom steps available for this layer.
PropertyType getPropertyType(const PropertyName &) const override
QString getText() const
void setVerticalScale(VerticalScale scale)
virtual sv_frame_t getModelsEndFrame() const =0
EventVector getLocalPoints(LayerGeometryProvider *v, int) const
void drawEnd(LayerGeometryProvider *v, QMouseEvent *) override
int getCurrentVerticalZoomStep() const override
Get the current vertical zoom step.
void paintVertical(LayerGeometryProvider *v, const ColourScaleLayer *layer, QPainter &paint, int x0, double minf, double maxf)
int getYForValue(LayerGeometryProvider *, double value) const override
VerticalScaleLayer and ColourScaleLayer methods.
virtual QPen scalePen(QPen pen) const =0
void setProperties(const QXmlAttributes &attributes) override
Set the particular properties of a layer (those specific to the subclass) from a set of XML attribute...
bool setDisplayExtents(double min, double max) override
Set the displayed minimum and maximum values for the y axis to the given range, if supported...
void eraseDrag(LayerGeometryProvider *v, QMouseEvent *) override
QString getFeatureDescription(LayerGeometryProvider *v, QPoint &) const override
QString getLabelPreceding(sv_frame_t) const override
int getCompletion(LayerGeometryProvider *) const override
Return the proportion of background work complete in drawing this view, as a percentage – in most ca...
void setDrawSegmentDivisions(bool)
bool shouldAutoAlign() const
QString getPropertyValueLabel(const PropertyName &, int value) const override
void setVerticalZoomStep(int) override
Set the vertical zoom step.
bool m_drawSegmentDivisions
virtual int getId() const =0
Retrieve the id of this object.
bool editOpen(LayerGeometryProvider *v, QMouseEvent *) override
Open an editor on the item under the mouse (e.g.
int getDefaultColourHint(bool dark, bool &impose) override
static QString getColourMapId(int n)
Return a machine-readable id string for the colour map with the given index.
void setShowDerivative(bool)
virtual int getTextLabelYCoord(const Layer *layer, QPainter &) const =0
Return a y-coordinate at which text labels for individual items in a layer may be drawn...
static void drawVisibleText(const LayerGeometryProvider *, QPainter &p, int x, int y, QString text, TextStyle style)
PlotStyle m_plotStyle
bool needsTextLabelHeight() const override
True if this layer will need to place text labels when it is painted.
virtual bool getVisibleExtentsForUnit(QString unit, double &min, double &max, bool &log) const =0
Return the visible vertical extents for the given unit, if any.
virtual sv_frame_t getEndFrame() const =0
Retrieve the last visible sample frame on the widget.
void moveSelection(Selection s, sv_frame_t newStartFrame) override
void setFillColourMap(int)
void setProperty(const PropertyName &, int value) override
virtual int getXForFrame(sv_frame_t frame) const =0
Return the pixel x-coordinate corresponding to a given sample frame (which may be negative)...
virtual int getPaintWidth() const
void paintVerticalScale(LayerGeometryProvider *v, bool, QPainter &paint, QRect rect) const override
QString getPropertyLabel(const PropertyName &) const override
QString getPropertyLabel(const PropertyName &) const override
QString getPropertyGroupName(const PropertyName &) const override
static ColourDatabase * getInstance()
void paintVertical(LayerGeometryProvider *v, const ColourScaleLayer *layer, QPainter &paint, int x0, double minf, double maxf)
void deleteSelection(Selection s) override
virtual View * getView()=0
void setValue(float value)
void eraseStart(LayerGeometryProvider *v, QMouseEvent *) override
static QString getItem(QWidget *parent, const QString &title, const QString &label, const QStringList &list, int current=0, bool *ok=0)