RegionLayer.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-2008 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 "RegionLayer.h"
17 
18 #include "data/model/Model.h"
19 #include "base/RealTime.h"
20 #include "base/Profiler.h"
21 #include "base/LogRange.h"
22 
23 #include "ColourDatabase.h"
24 #include "ColourMapper.h"
25 #include "LinearNumericalScale.h"
26 #include "LogNumericalScale.h"
27 #include "LinearColourScale.h"
28 #include "LogColourScale.h"
29 #include "PaintAssistant.h"
30 
31 #include "view/View.h"
32 
33 #include "data/model/RegionModel.h"
34 
35 #include "widgets/ItemEditDialog.h"
36 #include "widgets/TextAbbrev.h"
37 
38 #include <QPainter>
39 #include <QPainterPath>
40 #include <QMouseEvent>
41 #include <QTextStream>
42 #include <QMessageBox>
43 
44 #include <iostream>
45 #include <cmath>
46 
49  m_editing(false),
50  m_dragPointX(0),
51  m_dragPointY(0),
52  m_dragStartX(0),
53  m_dragStartY(0),
54  m_originalPoint(0, 0.0, 0, tr("New Region")),
55  m_editingPoint(0, 0.0, 0, tr("New Region")),
56  m_editingCommand(nullptr),
57  m_verticalScale(EqualSpaced),
58  m_colourMap(0),
59  m_colourInverted(false),
60  m_plotStyle(PlotLines)
61 {
62 
63 }
64 
65 int
67 {
68  auto model = ModelById::get(m_model);
69  if (model) return model->getCompletion();
70  else return 0;
71 }
72 
73 void
74 RegionLayer::setModel(ModelId modelId)
75 {
76  auto oldModel = ModelById::getAs<RegionModel>(m_model);
77  auto newModel = ModelById::getAs<RegionModel>(modelId);
78 
79  if (!modelId.isNone() && !newModel) {
80  throw std::logic_error("Not a RegionModel");
81  }
82 
83  if (m_model == modelId) return;
84  m_model = modelId;
85 
86  if (newModel) {
87 
89 
90  connect(newModel.get(), SIGNAL(modelChanged(ModelId)),
91  this, SLOT(recalcSpacing()));
92 
93  recalcSpacing();
94 
95  if (newModel->getRDFTypeURI().endsWith("Segment")) {
97  }
98  if (newModel->getRDFTypeURI().endsWith("Change")) {
100  }
101  }
102 
103  emit modelReplaced();
104 }
105 
106 Layer::PropertyList
108 {
109  PropertyList list = SingleColourLayer::getProperties();
110  list.push_back("Vertical Scale");
111  list.push_back("Scale Units");
112  list.push_back("Plot Type");
113  return list;
114 }
115 
116 QString
117 RegionLayer::getPropertyLabel(const PropertyName &name) const
118 {
119  if (name == "Vertical Scale") return tr("Vertical Scale");
120  if (name == "Scale Units") return tr("Scale Units");
121  if (name == "Plot Type") return tr("Plot Type");
123 }
124 
125 Layer::PropertyType
126 RegionLayer::getPropertyType(const PropertyName &name) const
127 {
128  if (name == "Scale Units") return UnitsProperty;
129  if (name == "Vertical Scale") return ValueProperty;
130  if (name == "Plot Type") return ValueProperty;
131  if (name == "Colour" && m_plotStyle == PlotSegmentation) return ValueProperty;
133 }
134 
135 QString
136 RegionLayer::getPropertyGroupName(const PropertyName &name) const
137 {
138  if (name == "Vertical Scale" || name == "Scale Units") {
139  return tr("Scale");
140  }
142 }
143 
144 int
145 RegionLayer::getPropertyRangeAndValue(const PropertyName &name,
146  int *min, int *max, int *deflt) const
147 {
148  int val = 0;
149 
150  if (name == "Colour" && m_plotStyle == PlotSegmentation) {
151 
152  if (min) *min = 0;
153  if (max) *max = ColourMapper::getColourMapCount() - 1;
154  if (deflt) *deflt = 0;
155 
156  val = m_colourMap;
157 
158  } else if (name == "Plot Type") {
159 
160  if (min) *min = 0;
161  if (max) *max = 1;
162  if (deflt) *deflt = 0;
163 
164  val = int(m_plotStyle);
165 
166  } else if (name == "Vertical Scale") {
167 
168  if (min) *min = 0;
169  if (max) *max = 3;
170  if (deflt) *deflt = int(EqualSpaced);
171 
172  val = int(m_verticalScale);
173 
174  } else if (name == "Scale Units") {
175 
176  if (deflt) *deflt = 0;
177  auto model = ModelById::getAs<RegionModel>(m_model);
178  if (model) {
179  val = UnitDatabase::getInstance()->getUnitId
180  (model->getScaleUnits());
181  }
182 
183  } else {
184 
185  val = SingleColourLayer::getPropertyRangeAndValue(name, min, max, deflt);
186  }
187 
188  return val;
189 }
190 
191 QString
192 RegionLayer::getPropertyValueLabel(const PropertyName &name,
193  int value) const
194 {
195  if (name == "Colour" && m_plotStyle == PlotSegmentation) {
196  return ColourMapper::getColourMapLabel(value);
197  } else if (name == "Plot Type") {
198 
199  switch (value) {
200  default:
201  case 0: return tr("Bars");
202  case 1: return tr("Segmentation");
203  }
204 
205  } else if (name == "Vertical Scale") {
206  switch (value) {
207  default:
208  case 0: return tr("Auto-Align");
209  case 1: return tr("Equal Spaced");
210  case 2: return tr("Linear");
211  case 3: return tr("Log");
212  }
213  }
214  return SingleColourLayer::getPropertyValueLabel(name, value);
215 }
216 
217 void
218 RegionLayer::setProperty(const PropertyName &name, int value)
219 {
220  if (name == "Colour" && m_plotStyle == PlotSegmentation) {
221  setFillColourMap(value);
222  } else if (name == "Plot Type") {
223  setPlotStyle(PlotStyle(value));
224  } else if (name == "Vertical Scale") {
226  } else if (name == "Scale Units") {
227  auto model = ModelById::getAs<RegionModel>(m_model);
228  if (model) {
229  model->setScaleUnits
230  (UnitDatabase::getInstance()->getUnitById(value));
231  emit modelChanged(m_model);
232  }
233  } else {
234  return SingleColourLayer::setProperty(name, value);
235  }
236 }
237 
238 void
240 {
241  if (m_colourMap == map) return;
242  m_colourMap = map;
243  emit layerParametersChanged();
244 }
245 
246 void
248 {
249  if (m_plotStyle == style) return;
250  bool colourTypeChanged = (style == PlotSegmentation ||
252  m_plotStyle = style;
253  if (colourTypeChanged) {
255  }
256  emit layerParametersChanged();
257 }
258 
259 void
261 {
262  if (m_verticalScale == scale) return;
263  m_verticalScale = scale;
264  emit layerParametersChanged();
265 }
266 
267 bool
269 {
270  QPoint discard;
271  return !v->shouldIlluminateLocalFeatures(this, discard);
272 }
273 
274 void
276 {
277  m_spacingMap.clear();
278  m_distributionMap.clear();
279 
280  auto model = ModelById::getAs<RegionModel>(m_model);
281  if (!model) return;
282 
283 // SVDEBUG << "RegionLayer::recalcSpacing" << endl;
284 
285  EventVector allEvents = model->getAllEvents();
286  for (const Event &e: allEvents) {
287  m_distributionMap[e.getValue()]++;
288 // SVDEBUG << "RegionLayer::recalcSpacing: value found: " << e.getValue() << " (now have " << m_distributionMap[e.getValue()] << " of this value)" << endl;
289  }
290 
291  int n = 0;
292 
293  for (SpacingMap::const_iterator i = m_distributionMap.begin();
294  i != m_distributionMap.end(); ++i) {
295  m_spacingMap[i->first] = n++;
296 // SVDEBUG << "RegionLayer::recalcSpacing: " << i->first << " -> " << m_spacingMap[i->first] << endl;
297  }
298 }
299 
300 bool
301 RegionLayer::getValueExtents(double &min, double &max,
302  bool &logarithmic, QString &unit) const
303 {
304  auto model = ModelById::getAs<RegionModel>(m_model);
305  if (!model) return false;
306  min = model->getValueMinimum();
307  max = model->getValueMaximum();
308  unit = getScaleUnits();
309 
310  if (m_verticalScale == LogScale) logarithmic = true;
311 
312  return true;
313 }
314 
315 bool
316 RegionLayer::getDisplayExtents(double &min, double &max) const
317 {
318  auto model = ModelById::getAs<RegionModel>(m_model);
319  if (!model ||
321  m_verticalScale == EqualSpaced) return false;
322 
323  min = model->getValueMinimum();
324  max = model->getValueMaximum();
325 
326  return true;
327 }
328 
329 EventVector
331 {
332  auto model = ModelById::getAs<RegionModel>(m_model);
333  if (!model) return EventVector();
334 
335  sv_frame_t frame = v->getFrameForX(x);
336 
337  EventVector local = model->getEventsCovering(frame);
338  if (!local.empty()) return local;
339 
340  int fuzz = ViewManager::scalePixelSize(2);
341  sv_frame_t start = v->getFrameForX(x - fuzz);
342  sv_frame_t end = v->getFrameForX(x + fuzz);
343 
344  local = model->getEventsStartingWithin(frame, end - frame);
345  if (!local.empty()) return local;
346 
347  local = model->getEventsSpanning(start, frame - start);
348  if (!local.empty()) return local;
349 
350  return {};
351 }
352 
353 bool
354 RegionLayer::getPointToDrag(LayerGeometryProvider *v, int x, int y, Event &point) const
355 {
356  auto model = ModelById::getAs<RegionModel>(m_model);
357  if (!model) return false;
358 
359  sv_frame_t frame = v->getFrameForX(x);
360 
361  EventVector onPoints = model->getEventsCovering(frame);
362  if (onPoints.empty()) return false;
363 
364  int nearestDistance = -1;
365  for (const auto &p: onPoints) {
366  int distance = getYForValue(v, p.getValue()) - y;
367  if (distance < 0) distance = -distance;
368  if (nearestDistance == -1 || distance < nearestDistance) {
369  nearestDistance = distance;
370  point = p;
371  }
372  }
373 
374  return true;
375 }
376 
377 QString
378 RegionLayer::getLabelPreceding(sv_frame_t frame) const
379 {
380  auto model = ModelById::getAs<RegionModel>(m_model);
381  if (!model) return "";
382  EventVector points = model->getEventsStartingWithin
383  (model->getStartFrame(), frame - model->getStartFrame());
384  if (!points.empty()) {
385  for (auto i = points.rbegin(); i != points.rend(); ++i) {
386  if (i->getLabel() != QString()) {
387  return i->getLabel();
388  }
389  }
390  }
391  return QString();
392 }
393 
394 QString
396 {
397  int x = pos.x();
398 
399  auto model = ModelById::getAs<RegionModel>(m_model);
400  if (!model || !model->getSampleRate()) return "";
401 
402  EventVector points = getLocalPoints(v, x);
403 
404  if (points.empty()) {
405  if (!model->isReady()) {
406  return tr("In progress");
407  } else {
408  return tr("No local points");
409  }
410  }
411 
412  Event region;
413  EventVector::iterator i;
414 
417 
418  for (i = points.begin(); i != points.end(); ++i) {
419 
420  int y = getYForValue(v, i->getValue());
421  int h = 3;
422 
423  if (model->getValueQuantization() != 0.0) {
424  h = y - getYForValue
425  (v, i->getValue() + model->getValueQuantization());
426  if (h < 3) h = 3;
427  }
428 
429  if (pos.y() >= y - h && pos.y() <= y) {
430  region = *i;
431  break;
432  }
433  }
434 
435  if (i == points.end()) return tr("No local points");
436 
437  RealTime rt = RealTime::frame2RealTime(region.getFrame(),
438  model->getSampleRate());
439  RealTime rd = RealTime::frame2RealTime(region.getDuration(),
440  model->getSampleRate());
441 
442  QString valueText;
443 
444  valueText = tr("%1 %2").arg(region.getValue()).arg(getScaleUnits());
445 
446  QString text;
447 
448  if (region.getLabel() == "") {
449  text = QString(tr("Time:\t%1\nValue:\t%2\nDuration:\t%3\nNo label"))
450  .arg(rt.toText(true).c_str())
451  .arg(valueText)
452  .arg(rd.toText(true).c_str());
453  } else {
454  text = QString(tr("Time:\t%1\nValue:\t%2\nDuration:\t%3\nLabel:\t%4"))
455  .arg(rt.toText(true).c_str())
456  .arg(valueText)
457  .arg(rd.toText(true).c_str())
458  .arg(region.getLabel());
459  }
460 
461  pos = QPoint(v->getXForFrame(region.getFrame()),
462  getYForValue(v, region.getValue()));
463  return text;
464 }
465 
466 bool
468  int &resolution,
469  SnapType snap, int ycoord) const
470 {
471  auto model = ModelById::getAs<RegionModel>(m_model);
472  if (!model) {
473  return Layer::snapToFeatureFrame(v, frame, resolution, snap, ycoord);
474  }
475 
476  // SnapLeft / SnapRight: return frame of nearest feature in that
477  // direction no matter how far away
478  //
479  // SnapNeighbouring: return frame of feature that would be used in
480  // an editing operation, i.e. closest feature in either direction
481  // but only if it is "close enough"
482 
483  resolution = model->getResolution();
484 
485  if (snap == SnapNeighbouring) {
486  EventVector points = getLocalPoints(v, v->getXForFrame(frame));
487  if (points.empty()) return false;
488  frame = points.begin()->getFrame();
489  return true;
490  }
491 
492  // Normally we snap to the start frame of whichever event we
493  // find. However here, for SnapRight only, if the end frame of
494  // whichever event we would have snapped to had we been snapping
495  // left is closer than the start frame of the next event to the
496  // right, then we snap to that frame instead. Clear?
497 
498  Event left;
499  bool haveLeft = false;
500  if (model->getNearestEventMatching
501  (frame, [](Event) { return true; }, EventSeries::Backward, left)) {
502  haveLeft = true;
503  }
504 
505  if (snap == SnapLeft) {
506  frame = left.getFrame();
507  return haveLeft;
508  }
509 
510  Event right;
511  bool haveRight = false;
512  if (model->getNearestEventMatching
513  (frame, [](Event) { return true; }, EventSeries::Forward, right)) {
514  haveRight = true;
515  }
516 
517  if (haveLeft) {
518  sv_frame_t leftEnd = left.getFrame() + left.getDuration();
519  if (leftEnd > frame) {
520  if (haveRight) {
521  if (leftEnd - frame < right.getFrame() - frame) {
522  frame = leftEnd;
523  } else {
524  frame = right.getFrame();
525  }
526  } else {
527  frame = leftEnd;
528  }
529  return true;
530  }
531  }
532 
533  if (haveRight) {
534  frame = right.getFrame();
535  return true;
536  }
537 
538  return false;
539 }
540 
541 bool
543  int &resolution,
544  SnapType snap) const
545 {
546  auto model = ModelById::getAs<RegionModel>(m_model);
547  if (!model) {
548  return Layer::snapToSimilarFeature(v, frame, resolution, snap);
549  }
550 
551  // snap is only permitted to be SnapLeft or SnapRight here. We
552  // don't do the same trick as in snapToFeatureFrame, of snapping
553  // to the end of a feature sometimes.
554 
555  resolution = model->getResolution();
556 
557  Event ref;
558  Event e;
559  float matchvalue;
560  bool found;
561 
562  found = model->getNearestEventMatching
563  (frame, [](Event) { return true; }, EventSeries::Backward, ref);
564 
565  if (!found) {
566  return false;
567  }
568 
569  matchvalue = ref.getValue();
570 
571  found = model->getNearestEventMatching
572  (frame,
573  [matchvalue](Event e) {
574  double epsilon = 0.0001;
575  return fabs(e.getValue() - matchvalue) < epsilon;
576  },
577  snap == SnapLeft ? EventSeries::Backward : EventSeries::Forward,
578  e);
579 
580  if (!found) {
581  return false;
582  }
583 
584  frame = e.getFrame();
585  return true;
586 }
587 
588 QString
590 {
591  auto model = ModelById::getAs<RegionModel>(m_model);
592  if (model) return model->getScaleUnits();
593  else return "";
594 }
595 
596 void
597 RegionLayer::getScaleExtents(LayerGeometryProvider *v, double &min, double &max, bool &log) const
598 {
599  min = 0.0;
600  max = 0.0;
601  log = false;
602 
603  auto model = ModelById::getAs<RegionModel>(m_model);
604  if (!model) return;
605 
606  QString queryUnits;
607  queryUnits = getScaleUnits();
608 
610 
611  if (!v->getVisibleExtentsForUnit(queryUnits, min, max, log)) {
612 
613  min = model->getValueMinimum();
614  max = model->getValueMaximum();
615 
616 // cerr << "RegionLayer[" << this << "]::getScaleExtents: min = " << min << ", max = " << max << ", log = " << log << endl;
617 
618  } else if (log) {
619 
620  LogRange::mapRange(min, max);
621 
622 // cerr << "RegionLayer[" << this << "]::getScaleExtents: min = " << min << ", max = " << max << ", log = " << log << endl;
623 
624  }
625 
626  } else if (m_verticalScale == EqualSpaced) {
627 
628  if (!m_spacingMap.empty()) {
629  SpacingMap::const_iterator i = m_spacingMap.begin();
630  min = i->second;
631  i = m_spacingMap.end();
632  --i;
633  max = i->second;
634 // cerr << "RegionLayer[" << this << "]::getScaleExtents: equal spaced; min = " << min << ", max = " << max << ", log = " << log << endl;
635  }
636 
637  } else {
638 
639  min = model->getValueMinimum();
640  max = model->getValueMaximum();
641 
642  if (m_verticalScale == LogScale) {
643  LogRange::mapRange(min, max);
644  log = true;
645  }
646  }
647 
648  if (max == min) max = min + 1.0;
649 }
650 
651 int
653 {
654  int h = v->getPaintHeight();
655  int n = int(m_spacingMap.size());
656  // this maps from i (spacing of the value from the spacing
657  // map) and n (number of region types) to y
658  int y = h - (((h * i) / n) + (h / (2 * n)));
659  return y;
660 }
661 
662 double
664 {
665  // we return an inexact result here (double rather than int)
666  int h = v->getPaintHeight();
667  int n = int(m_spacingMap.size());
668  // from y = h - ((h * i) / n) + (h / (2 * n)) as above (vh taking place of i)
669  double vh = double(2*h*n - h - 2*n*y) / double(2*h);
670  return vh;
671 }
672 
673 int
675 {
676  double min = 0.0, max = 0.0;
677  bool logarithmic = false;
678  int h = v->getPaintHeight();
679 
680  if (m_verticalScale == EqualSpaced) {
681 
682  if (m_spacingMap.empty()) return h/2;
683 
684  SpacingMap::const_iterator i = m_spacingMap.lower_bound(val);
686 
687  int y = spacingIndexToY(v, i->second);
688 
689 // SVDEBUG << "RegionLayer::getYForValue: value " << val << " -> i->second " << i->second << " -> y " << y << endl;
690  return y;
691 
692 
693  } else {
694 
695  getScaleExtents(v, min, max, logarithmic);
696 
697 // cerr << "RegionLayer[" << this << "]::getYForValue(" << val << "): min = " << min << ", max = " << max << ", log = " << logarithmic << endl;
698 // cerr << "h = " << h << ", margin = " << margin << endl;
699 
700  if (logarithmic) {
701  val = LogRange::map(val);
702  }
703 
704  return int(h - ((val - min) * h) / (max - min));
705  }
706 }
707 
708 double
710 {
711  return getValueForY(v, y, -1);
712 }
713 
714 double
716 {
717  double min = 0.0, max = 0.0;
718  bool logarithmic = false;
719  int h = v->getPaintHeight();
720 
721  if (m_verticalScale == EqualSpaced) {
722 
723  // if we're equal spaced, we probably want to snap to the
724  // nearest item when close to it, and give some notification
725  // that we're doing so
726 
727  if (m_spacingMap.empty()) return 1.f;
728 
729  // n is the number of distinct regions. if we are close to
730  // one of the m/n divisions in the y scale, we should snap to
731  // the value of the mth region.
732 
733  double vh = yToSpacingIndex(v, y);
734 
735  // spacings in the map are integral, so find the closest one,
736  // map it back to its y coordinate, and see how far we are
737  // from it
738 
739  int n = int(m_spacingMap.size());
740  int ivh = int(lrint(vh));
741  if (ivh < 0) ivh = 0;
742  if (ivh > n-1) ivh = n-1;
743  int iy = spacingIndexToY(v, ivh);
744 
745  int dist = iy - y;
746  int gap = h / n; // between region lines
747 
748 // cerr << "getValueForY: y = " << y << ", vh = " << vh << ", ivh = " << ivh << " of " << n << ", iy = " << iy << ", dist = " << dist << ", gap = " << gap << endl;
749 
750  SpacingMap::const_iterator i = m_spacingMap.begin();
751  while (i != m_spacingMap.end()) {
752  if (i->second == ivh) break;
753  ++i;
754  }
755  if (i == m_spacingMap.end()) i = m_spacingMap.begin();
756 
757 // cerr << "nearest existing value = " << i->first << " at " << iy << endl;
758 
759  double val = 0;
760 
761 // cerr << "note: avoid = " << avoid << ", i->second = " << i->second << endl;
762 
763  if (dist < -gap/3 &&
764  ((avoid == -1) ||
765  (avoid != i->second && avoid != i->second - 1))) {
766  // bisect gap to prior
767  if (i == m_spacingMap.begin()) {
768  val = i->first - 1.f;
769 // cerr << "extended down to " << val << endl;
770  } else {
771  SpacingMap::const_iterator j = i;
772  --j;
773  val = (i->first + j->first) / 2;
774 // cerr << "bisected down to " << val << endl;
775  }
776  } else if (dist > gap/3 &&
777  ((avoid == -1) ||
778  (avoid != i->second && avoid != i->second + 1))) {
779  // bisect gap to following
780  SpacingMap::const_iterator j = i;
781  ++j;
782  if (j == m_spacingMap.end()) {
783  val = i->first + 1.f;
784 // cerr << "extended up to " << val << endl;
785  } else {
786  val = (i->first + j->first) / 2;
787 // cerr << "bisected up to " << val << endl;
788  }
789  } else {
790  // snap
791  val = i->first;
792 // cerr << "snapped to " << val << endl;
793  }
794 
795  return val;
796 
797  } else {
798 
799  getScaleExtents(v, min, max, logarithmic);
800 
801  double val = min + (double(h - y) * double(max - min)) / h;
802 
803  if (logarithmic) {
804  val = pow(10.0, val);
805  }
806 
807  return val;
808  }
809 }
810 
811 QColor
813 {
814  double min, max;
815  bool log;
816  getScaleExtents(v, min, max, log);
817 
818  if (min > max) std::swap(min, max);
819  if (max == min) max = min + 1;
820 
821  if (log) {
822  LogRange::mapRange(min, max);
823  val = LogRange::map(val);
824  }
825 
826 // SVDEBUG << "RegionLayer::getColourForValue: min " << min << ", max "
827 // << max << ", log " << log << ", value " << val << endl;
828 
829  QColor solid = ColourMapper(m_colourMap, m_colourInverted, min, max).map(val);
830  return QColor(solid.red(), solid.green(), solid.blue(), 120);
831 }
832 
833 int
834 RegionLayer::getDefaultColourHint(bool darkbg, bool &impose)
835 {
836  impose = false;
838  (QString(darkbg ? "Bright Blue" : "Blue"));
839 }
840 
841 void
842 RegionLayer::paint(LayerGeometryProvider *v, QPainter &paint, QRect rect) const
843 {
844  auto model = ModelById::getAs<RegionModel>(m_model);
845  if (!model || !model->isOK()) return;
846 
847  sv_samplerate_t sampleRate = model->getSampleRate();
848  if (!sampleRate) return;
849 
850 // Profiler profiler("RegionLayer::paint", true);
851 
852  int x0 = rect.left() - 40;
853  int x1 = x0 + rect.width() + 80;
854 
855  sv_frame_t wholeFrame0 = v->getFrameForX(0);
856  sv_frame_t wholeFrame1 = v->getFrameForX(v->getPaintWidth());
857 
858  EventVector points(model->getEventsSpanning(wholeFrame0,
859  wholeFrame1 - wholeFrame0));
860  if (points.empty()) return;
861 
862  paint.setPen(getBaseQColor());
863 
864  QColor brushColour(getBaseQColor());
865  brushColour.setAlpha(80);
866 
867 // SVDEBUG << "RegionLayer::paint: resolution is "
868 // << model->getResolution() << " frames" << endl;
869 
870  double min = model->getValueMinimum();
871  double max = model->getValueMaximum();
872  if (max == min) max = min + 1.0;
873 
874  QPoint localPos;
875  Event illuminatePoint(0);
876  bool shouldIlluminate = false;
877 
878  if (v->shouldIlluminateLocalFeatures(this, localPos)) {
879  shouldIlluminate = getPointToDrag(v, localPos.x(), localPos.y(),
880  illuminatePoint);
881  }
882 
883  paint.save();
884  paint.setRenderHint(QPainter::Antialiasing, false);
885 
888 
891 
892  int fontHeight = paint.fontMetrics().height();
893 
894  for (EventVector::const_iterator i = points.begin();
895  i != points.end(); ++i) {
896 
897  const Event &p(*i);
898 
899  int x = v->getXForFrame(p.getFrame());
900  int w = v->getXForFrame(p.getFrame() + p.getDuration()) - x;
901  int y = getYForValue(v, p.getValue());
902  int h = 9;
903  int ex = x + w;
904 
905  int gap = v->scalePixelSize(2);
906 
907  EventVector::const_iterator j = i;
908  ++j;
909 
910  if (j != points.end()) {
911  const Event &q(*j);
912  int nx = v->getXForFrame(q.getFrame());
913  if (nx < ex) ex = nx;
914  }
915 
916  if (model->getValueQuantization() != 0.0) {
917  h = y - getYForValue
918  (v, p.getValue() + model->getValueQuantization());
919  if (h < 3) h = 3;
920  }
921 
922  if (w < 1) w = 1;
923 
924  if (m_plotStyle == PlotSegmentation) {
925  paint.setPen(getForegroundQColor(v->getView()));
926  paint.setBrush(getColourForValue(v, p.getValue()));
927  } else {
928  paint.setPen(getBaseQColor());
929  paint.setBrush(brushColour);
930  }
931 
932  if (m_plotStyle == PlotSegmentation) {
933 
934  if (ex <= x) continue;
935 
936  if (!shouldIlluminate || illuminatePoint != p) {
937 
938  paint.setPen(QPen(getForegroundQColor(v->getView()), 1));
939  paint.drawLine(x, 0, x, v->getPaintHeight());
940  paint.setPen(Qt::NoPen);
941 
942  } else {
943  paint.setPen(QPen(getForegroundQColor(v->getView()), 2));
944  }
945 
946  paint.drawRect(x, -1, ex - x, v->getPaintHeight() + gap);
947 
948  } else {
949 
950  if (shouldIlluminate && illuminatePoint == p) {
951 
952  paint.setPen(v->getForeground());
953  paint.setBrush(v->getForeground());
954 
955  // Qt 5.13 deprecates QFontMetrics::width(), but its suggested
956  // replacement (horizontalAdvance) was only added in Qt 5.11
957  // which is too new for us
958 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
959 
960  QString vlabel =
961  QString("%1%2").arg(p.getValue()).arg(getScaleUnits());
963  x - paint.fontMetrics().width(vlabel) - gap,
964  y + paint.fontMetrics().height()/2
965  - paint.fontMetrics().descent(),
967 
968  QString hlabel = RealTime::frame2RealTime
969  (p.getFrame(), model->getSampleRate()).toText(true).c_str();
971  x,
972  y - h/2 - paint.fontMetrics().descent() - gap,
974  }
975 
976  paint.drawLine(x, y-1, x + w, y-1);
977  paint.drawLine(x, y+1, x + w, y+1);
978  paint.drawLine(x, y - h/2, x, y + h/2);
979  paint.drawLine(x+w, y - h/2, x + w, y + h/2);
980  }
981  }
982 
983  int nextLabelMinX = -100;
984  int lastLabelY = 0;
985 
986  for (EventVector::const_iterator i = points.begin();
987  i != points.end(); ++i) {
988 
989  const Event &p(*i);
990 
991  int x = v->getXForFrame(p.getFrame());
992  int w = v->getXForFrame(p.getFrame() + p.getDuration()) - x;
993  int y = getYForValue(v, p.getValue());
994 
995  QString label = p.getLabel();
996  if (label == "") {
997  label = QString("%1%2").arg(p.getValue()).arg(getScaleUnits());
998  }
999  int labelWidth = paint.fontMetrics().width(label);
1000 
1001  int gap = v->scalePixelSize(2);
1002 
1003  if (m_plotStyle == PlotSegmentation) {
1004  if ((x + w < x0 && x + labelWidth + gap < x0) || x > x1) {
1005  continue;
1006  }
1007  } else {
1008  if (x + w < x0 || x - labelWidth - gap > x1) {
1009  continue;
1010  }
1011  }
1012 
1013  bool illuminated = false;
1014 
1015  if (m_plotStyle != PlotSegmentation) {
1016  if (shouldIlluminate && illuminatePoint == p) {
1017  illuminated = true;
1018  }
1019  }
1020 
1021  if (!illuminated) {
1022 
1023  int labelX, labelY;
1024 
1025  if (m_plotStyle != PlotSegmentation) {
1026  labelX = x - labelWidth - gap;
1027  labelY = y + paint.fontMetrics().height()/2
1028  - paint.fontMetrics().descent();
1029  } else {
1030  labelX = x + 5;
1031  labelY = v->getTextLabelYCoord(this, paint);
1032  if (labelX < nextLabelMinX) {
1033  if (lastLabelY < v->getPaintHeight()/2) {
1034  labelY = lastLabelY + fontHeight;
1035  }
1036  }
1037  lastLabelY = labelY;
1038  nextLabelMinX = labelX + labelWidth;
1039  }
1040 
1041  PaintAssistant::drawVisibleText(v, paint, labelX, labelY, label,
1043  }
1044  }
1045 
1046  paint.restore();
1047 }
1048 
1049 int
1051 {
1052  auto model = ModelById::getAs<RegionModel>(m_model);
1053  if (!model ||
1056  return 0;
1057  } else if (m_plotStyle == PlotSegmentation) {
1058  if (m_verticalScale == LogScale) {
1059  return LogColourScale().getWidth(v, paint);
1060  } else {
1061  return LinearColourScale().getWidth(v, paint);
1062  }
1063  } else {
1064  if (m_verticalScale == LogScale) {
1065  return LogNumericalScale().getWidth(v, paint);
1066  } else {
1067  return LinearNumericalScale().getWidth(v, paint);
1068  }
1069  }
1070 }
1071 
1072 void
1074 {
1075  auto model = ModelById::getAs<RegionModel>(m_model);
1076  if (!model || model->isEmpty()) return;
1077 
1078  QString unit;
1079  double min, max;
1080  bool logarithmic;
1081 
1082  int w = getVerticalScaleWidth(v, false, paint);
1083 
1084  if (m_plotStyle == PlotSegmentation) {
1085 
1086  getValueExtents(min, max, logarithmic, unit);
1087 
1088  if (logarithmic) {
1089  LogRange::mapRange(min, max);
1090  LogColourScale().paintVertical(v, this, paint, 0, min, max);
1091  } else {
1092  LinearColourScale().paintVertical(v, this, paint, 0, min, max);
1093  }
1094 
1095  } else {
1096 
1097  getScaleExtents(v, min, max, logarithmic);
1098 
1099  if (logarithmic) {
1100  LogNumericalScale().paintVertical(v, this, paint, 0, min, max);
1101  } else {
1102  LinearNumericalScale().paintVertical(v, this, paint, 0, min, max);
1103  }
1104  }
1105 
1106  if (getScaleUnits() != "") {
1107  int mw = w - 5;
1108  paint.drawText(5,
1109  5 + paint.fontMetrics().ascent(),
1111  paint.fontMetrics(),
1112  mw));
1113  }
1114 }
1115 
1116 void
1118 {
1119  auto model = ModelById::getAs<RegionModel>(m_model);
1120  if (!model) return;
1121 
1122  sv_frame_t frame = v->getFrameForX(e->x());
1123  if (frame < 0) frame = 0;
1124  frame = frame / model->getResolution() * model->getResolution();
1125 
1126  double value = getValueForY(v, e->y());
1127 
1128  m_editingPoint = Event(frame, float(value), 0, "");
1130 
1132  m_editingCommand = new ChangeEventsCommand(m_model.untyped, tr("Draw Region"));
1133  m_editingCommand->add(m_editingPoint);
1134 
1135  recalcSpacing();
1136 
1137  m_editing = true;
1138 }
1139 
1140 void
1142 {
1143  auto model = ModelById::getAs<RegionModel>(m_model);
1144  if (!model || !m_editing) return;
1145 
1146  sv_frame_t frame = v->getFrameForX(e->x());
1147  if (frame < 0) frame = 0;
1148  frame = frame / model->getResolution() * model->getResolution();
1149 
1150  double newValue = m_editingPoint.getValue();
1151  if (m_verticalScale != EqualSpaced) newValue = getValueForY(v, e->y());
1152 
1153  sv_frame_t newFrame = m_editingPoint.getFrame();
1154  sv_frame_t newDuration = frame - newFrame;
1155  if (newDuration < 0) {
1156  newFrame = frame;
1157  newDuration = -newDuration;
1158  } else if (newDuration == 0) {
1159  newDuration = 1;
1160  }
1161 
1164  .withFrame(newFrame)
1165  .withValue(float(newValue))
1166  .withDuration(newDuration);
1168 
1169  recalcSpacing();
1170 }
1171 
1172 void
1174 {
1175  auto model = ModelById::getAs<RegionModel>(m_model);
1176  if (!model || !m_editing) return;
1178  m_editingCommand = nullptr;
1179  m_editing = false;
1180 
1181  recalcSpacing();
1182 }
1183 
1184 void
1186 {
1187  auto model = ModelById::getAs<RegionModel>(m_model);
1188  if (!model) return;
1189 
1190  if (!getPointToDrag(v, e->x(), e->y(), m_editingPoint)) return;
1191 
1192  if (m_editingCommand) {
1194  m_editingCommand = nullptr;
1195  }
1196 
1197  m_editing = true;
1198  recalcSpacing();
1199 }
1200 
1201 void
1203 {
1204 }
1205 
1206 void
1208 {
1209  auto model = ModelById::getAs<RegionModel>(m_model);
1210  if (!model || !m_editing) return;
1211 
1212  m_editing = false;
1213 
1214  Event p(0);
1215  if (!getPointToDrag(v, e->x(), e->y(), p)) return;
1216  if (p.getFrame() != m_editingPoint.getFrame() ||
1217  p.getValue() != m_editingPoint.getValue()) return;
1218 
1219  m_editingCommand = new ChangeEventsCommand
1220  (m_model.untyped, tr("Erase Region"));
1221 
1223 
1225  m_editingCommand = nullptr;
1226  m_editing = false;
1227  recalcSpacing();
1228 }
1229 
1230 void
1232 {
1233  auto model = ModelById::getAs<RegionModel>(m_model);
1234  if (!model) return;
1235 
1236  if (!getPointToDrag(v, e->x(), e->y(), m_editingPoint)) {
1237  return;
1238  }
1239 
1240  m_dragPointX = v->getXForFrame(m_editingPoint.getFrame());
1241  m_dragPointY = getYForValue(v, m_editingPoint.getValue());
1242 
1244 
1245  if (m_editingCommand) {
1247  m_editingCommand = nullptr;
1248  }
1249 
1250  m_editing = true;
1251  m_dragStartX = e->x();
1252  m_dragStartY = e->y();
1253  recalcSpacing();
1254 }
1255 
1256 void
1258 {
1259  auto model = ModelById::getAs<RegionModel>(m_model);
1260  if (!model || !m_editing) return;
1261 
1262  int xdist = e->x() - m_dragStartX;
1263  int ydist = e->y() - m_dragStartY;
1264  int newx = m_dragPointX + xdist;
1265  int newy = m_dragPointY + ydist;
1266 
1267  sv_frame_t frame = v->getFrameForX(newx);
1268  if (frame < 0) frame = 0;
1269  frame = frame / model->getResolution() * model->getResolution();
1270 
1271  // Do not bisect between two values, if one of those values is
1272  // that of the point we're actually moving ...
1273  int avoid = m_spacingMap[m_editingPoint.getValue()];
1274 
1275  // ... unless there are other points with the same value
1276  if (m_distributionMap[m_editingPoint.getValue()] > 1) avoid = -1;
1277 
1278  double value = getValueForY(v, newy, avoid);
1279 
1280  if (!m_editingCommand) {
1281  m_editingCommand = new ChangeEventsCommand(m_model.untyped,
1282  tr("Drag Region"));
1283  }
1284 
1287  .withFrame(frame)
1288  .withValue(float(value));
1290  recalcSpacing();
1291 }
1292 
1293 void
1295 {
1296  auto model = ModelById::getAs<RegionModel>(m_model);
1297  if (!model || !m_editing) return;
1298 
1299  if (m_editingCommand) {
1300 
1301  QString newName = m_editingCommand->getName();
1302 
1303  if (m_editingPoint.getFrame() != m_originalPoint.getFrame()) {
1304  if (m_editingPoint.getValue() != m_originalPoint.getValue()) {
1305  newName = tr("Edit Region");
1306  } else {
1307  newName = tr("Relocate Region");
1308  }
1309  } else {
1310  newName = tr("Change Point Value");
1311  }
1312 
1313  m_editingCommand->setName(newName);
1315  }
1316 
1317  m_editingCommand = nullptr;
1318  m_editing = false;
1319  recalcSpacing();
1320 }
1321 
1322 bool
1324 {
1325  auto model = ModelById::getAs<RegionModel>(m_model);
1326  if (!model) return false;
1327 
1328  Event region(0);
1329  if (!getPointToDrag(v, e->x(), e->y(), region)) return false;
1330 
1331  ItemEditDialog *dialog = new ItemEditDialog
1332  (model->getSampleRate(),
1337  getScaleUnits());
1338 
1339  dialog->setFrameTime(region.getFrame());
1340  dialog->setValue(region.getValue());
1341  dialog->setFrameDuration(region.getDuration());
1342  dialog->setText(region.getLabel());
1343 
1344  if (dialog->exec() == QDialog::Accepted) {
1345 
1346  Event newRegion = region
1347  .withFrame(dialog->getFrameTime())
1348  .withValue(dialog->getValue())
1349  .withDuration(dialog->getFrameDuration())
1350  .withLabel(dialog->getText());
1351 
1352  ChangeEventsCommand *command = new ChangeEventsCommand
1353  (m_model.untyped, tr("Edit Region"));
1354  command->remove(region);
1355  command->add(newRegion);
1356  finish(command);
1357  }
1358 
1359  delete dialog;
1360  recalcSpacing();
1361  return true;
1362 }
1363 
1364 void
1365 RegionLayer::moveSelection(Selection s, sv_frame_t newStartFrame)
1366 {
1367  auto model = ModelById::getAs<RegionModel>(m_model);
1368  if (!model) return;
1369 
1370  ChangeEventsCommand *command =
1371  new ChangeEventsCommand(m_model.untyped, tr("Drag Selection"));
1372 
1373  EventVector points =
1374  model->getEventsStartingWithin(s.getStartFrame(), s.getDuration());
1375 
1376  for (EventVector::iterator i = points.begin();
1377  i != points.end(); ++i) {
1378 
1379  Event newPoint = (*i)
1380  .withFrame(i->getFrame() + newStartFrame - s.getStartFrame());
1381  command->remove(*i);
1382  command->add(newPoint);
1383  }
1384 
1385  finish(command);
1386  recalcSpacing();
1387 }
1388 
1389 void
1390 RegionLayer::resizeSelection(Selection s, Selection newSize)
1391 {
1392  auto model = ModelById::getAs<RegionModel>(m_model);
1393  if (!model || !s.getDuration()) return;
1394 
1395  ChangeEventsCommand *command =
1396  new ChangeEventsCommand(m_model.untyped, tr("Resize Selection"));
1397 
1398  EventVector points =
1399  model->getEventsStartingWithin(s.getStartFrame(), s.getDuration());
1400 
1401  double ratio = double(newSize.getDuration()) / double(s.getDuration());
1402  double oldStart = double(s.getStartFrame());
1403  double newStart = double(newSize.getStartFrame());
1404 
1405  for (Event p: points) {
1406 
1407  double newFrame = (double(p.getFrame()) - oldStart) * ratio + newStart;
1408  double newDuration = double(p.getDuration()) * ratio;
1409 
1410  Event newPoint = p
1411  .withFrame(lrint(newFrame))
1412  .withDuration(lrint(newDuration));
1413  command->remove(p);
1414  command->add(newPoint);
1415  }
1416 
1417  finish(command);
1418  recalcSpacing();
1419 }
1420 
1421 void
1423 {
1424  auto model = ModelById::getAs<RegionModel>(m_model);
1425  if (!model) return;
1426 
1427  ChangeEventsCommand *command =
1428  new ChangeEventsCommand(m_model.untyped, tr("Delete Selected Points"));
1429 
1430  EventVector points =
1431  model->getEventsStartingWithin(s.getStartFrame(), s.getDuration());
1432 
1433  for (EventVector::iterator i = points.begin();
1434  i != points.end(); ++i) {
1435 
1436  if (s.contains(i->getFrame())) {
1437  command->remove(*i);
1438  }
1439  }
1440 
1441  finish(command);
1442  recalcSpacing();
1443 }
1444 
1445 void
1446 RegionLayer::copy(LayerGeometryProvider *v, Selection s, Clipboard &to)
1447 {
1448  auto model = ModelById::getAs<RegionModel>(m_model);
1449  if (!model) return;
1450 
1451  EventVector points =
1452  model->getEventsStartingWithin(s.getStartFrame(), s.getDuration());
1453 
1454  for (Event p: points) {
1455  to.addPoint(p.withReferenceFrame(alignToReference(v, p.getFrame())));
1456  }
1457 }
1458 
1459 bool
1460 RegionLayer::paste(LayerGeometryProvider *v, const Clipboard &from,
1461  sv_frame_t /* frameOffset */, bool /* interactive */)
1462 {
1463  auto model = ModelById::getAs<RegionModel>(m_model);
1464  if (!model) return false;
1465 
1466  const EventVector &points = from.getPoints();
1467 
1468  bool realign = false;
1469 
1470  if (clipboardHasDifferentAlignment(v, from)) {
1471 
1472  QMessageBox::StandardButton button =
1473  QMessageBox::question(v->getView(), tr("Re-align pasted items?"),
1474  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?"),
1475  QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel,
1476  QMessageBox::Yes);
1477 
1478  if (button == QMessageBox::Cancel) {
1479  return false;
1480  }
1481 
1482  if (button == QMessageBox::Yes) {
1483  realign = true;
1484  }
1485  }
1486 
1487  ChangeEventsCommand *command =
1488  new ChangeEventsCommand(m_model.untyped, tr("Paste"));
1489 
1490  for (EventVector::const_iterator i = points.begin();
1491  i != points.end(); ++i) {
1492 
1493  sv_frame_t frame = 0;
1494 
1495  if (!realign) {
1496 
1497  frame = i->getFrame();
1498 
1499  } else {
1500 
1501  if (i->hasReferenceFrame()) {
1502  frame = i->getReferenceFrame();
1503  frame = alignFromReference(v, frame);
1504  } else {
1505  frame = i->getFrame();
1506  }
1507  }
1508 
1509  Event p = i->withFrame(frame);
1510 
1511  Event newPoint = p;
1512  if (!p.hasValue()) {
1513  newPoint = newPoint.withValue((model->getValueMinimum() +
1514  model->getValueMaximum()) / 2);
1515  }
1516  if (!p.hasDuration()) {
1517  sv_frame_t nextFrame = frame;
1518  EventVector::const_iterator j = i;
1519  for (; j != points.end(); ++j) {
1520  if (j != i) break;
1521  }
1522  if (j != points.end()) {
1523  nextFrame = j->getFrame();
1524  }
1525  if (nextFrame == frame) {
1526  newPoint = newPoint.withDuration(model->getResolution());
1527  } else {
1528  newPoint = newPoint.withDuration(nextFrame - frame);
1529  }
1530  }
1531 
1532  command->add(newPoint);
1533  }
1534 
1535  finish(command);
1536  recalcSpacing();
1537  return true;
1538 }
1539 
1540 void
1541 RegionLayer::toXml(QTextStream &stream,
1542  QString indent, QString extraAttributes) const
1543 {
1544  QString s;
1545 
1546  s += QString("verticalScale=\"%1\" "
1547  "plotStyle=\"%2\" ")
1548  .arg(m_verticalScale)
1549  .arg(m_plotStyle);
1550 
1551  // New-style colour map attribute, by string id rather than by
1552  // number
1553 
1554  s += QString("fillColourMap=\"%1\" ")
1556 
1557  // Old-style colour map attribute
1558 
1559  s += QString("colourMap=\"%1\" ")
1561 
1562  SingleColourLayer::toXml(stream, indent, extraAttributes + " " + s);
1563 }
1564 
1565 void
1566 RegionLayer::setProperties(const QXmlAttributes &attributes)
1567 {
1569 
1570  bool ok;
1571  VerticalScale scale = (VerticalScale)
1572  attributes.value("verticalScale").toInt(&ok);
1573  if (ok) setVerticalScale(scale);
1574  PlotStyle style = (PlotStyle)
1575  attributes.value("plotStyle").toInt(&ok);
1576  if (ok) setPlotStyle(style);
1577 
1578  QString colourMapId = attributes.value("fillColourMap");
1579  int colourMap = ColourMapper::getColourMapById(colourMapId);
1580  if (colourMap >= 0) {
1581  setFillColourMap(colourMap);
1582  } else {
1583  colourMap = attributes.value("colourMap").toInt(&ok);
1584  if (ok && colourMap < ColourMapper::getColourMapCount()) {
1585  setFillColourMap(colourMap);
1586  }
1587  }
1588 }
1589 
1590 
virtual int scalePixelSize(int size) const =0
float getValue() const
bool m_colourInverted
Definition: RegionLayer.h:159
int getCompletion(LayerGeometryProvider *) const override
Return the proportion of background work complete in drawing this view, as a percentage – in most ca...
Definition: RegionLayer.cpp:66
double getValueForY(LayerGeometryProvider *v, int y) const override
void drawEnd(LayerGeometryProvider *v, QMouseEvent *) override
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 drawDrag(LayerGeometryProvider *v, QMouseEvent *) override
void setFrameDuration(sv_frame_t frame)
void setPlotStyle(PlotStyle style)
virtual QColor getForeground() const =0
QString getPropertyGroupName(const PropertyName &) const override
QString getFeatureDescription(LayerGeometryProvider *v, QPoint &) const override
void setModel(ModelId model)
Definition: RegionLayer.cpp:74
void getScaleExtents(LayerGeometryProvider *, double &min, double &max, bool &log) const
PropertyList getProperties() const override
virtual bool shouldIlluminateLocalFeatures(const Layer *, QPoint &) const =0
ModelId m_model
Definition: RegionLayer.h:148
void copy(LayerGeometryProvider *v, Selection s, Clipboard &to) override
static int scalePixelSize(int pixels)
Take a "design pixel" size and scale it for the actual display.
void paintVerticalScale(LayerGeometryProvider *v, bool, QPainter &paint, QRect rect) const override
void editStart(LayerGeometryProvider *v, QMouseEvent *) override
void modelReplaced()
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
int getPropertyRangeAndValue(const PropertyName &, int *min, int *max, int *deflt) const override
QColor getColourForValue(LayerGeometryProvider *v, double value) const override
int spacingIndexToY(LayerGeometryProvider *v, int i) const
virtual QColor getForegroundQColor(LayerGeometryProvider *v) const
bool getPointToDrag(LayerGeometryProvider *v, int x, int y, Event &) const
int m_dragStartX
Definition: RegionLayer.h:152
void setFrameTime(sv_frame_t frame)
bool m_editing
Definition: RegionLayer.h:149
void paintVertical(LayerGeometryProvider *v, const VerticalScaleLayer *layer, QPainter &paint, int x0, double minlog, double maxlog)
int m_dragStartY
Definition: RegionLayer.h:153
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...
QString getPropertyLabel(const PropertyName &) const override
sv_frame_t getFrameTime() const
virtual sv_frame_t getFrameForX(int x) const =0
Return the closest frame to the given pixel x-coordinate.
int m_dragPointX
Definition: RegionLayer.h:150
PropertyType getPropertyType(const PropertyName &) const override
void setProperties(const QXmlAttributes &attributes) override
Set the particular properties of a layer (those specific to the subclass) from a set of XML attribute...
void setVerticalScale(VerticalScale scale)
static int getColourMapCount()
Return the number of known colour maps.
void moveSelection(Selection s, sv_frame_t newStartFrame) override
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.
int getWidth(LayerGeometryProvider *v, QPainter &paint)
void setProperty(const PropertyName &, int value) override
virtual QColor getBaseQColor() const
void layerParameterRangesChanged()
void toXml(QTextStream &stream, QString indent="", QString extraAttributes="") const override
int getWidth(LayerGeometryProvider *v, QPainter &paint)
QString getPropertyValueLabel(const PropertyName &, int value) const override
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)
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.
double yToSpacingIndex(LayerGeometryProvider *v, int y) const
EventVector getLocalPoints(LayerGeometryProvider *v, int x) const
VerticalScale m_verticalScale
Definition: RegionLayer.h:157
void deleteSelection(Selection s) override
int m_dragPointY
Definition: RegionLayer.h:151
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
void toXml(QTextStream &stream, QString indent="", QString extraAttributes="") 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...
PropertyList getProperties() const override
void layerParametersChanged()
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.
Event m_originalPoint
Definition: RegionLayer.h:154
bool clipboardHasDifferentAlignment(LayerGeometryProvider *v, const Clipboard &clip) const
Definition: Layer.cpp:209
int getWidth(LayerGeometryProvider *v, QPainter &paint)
int getPropertyRangeAndValue(const PropertyName &, int *min, int *max, int *deflt) const override
SnapType
Definition: Layer.h:195
int getVerticalScaleWidth(LayerGeometryProvider *v, bool, QPainter &) const override
bool editOpen(LayerGeometryProvider *v, QMouseEvent *) override
Open an editor on the item under the mouse (e.g.
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.
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...
void paintVertical(LayerGeometryProvider *v, const VerticalScaleLayer *layer, QPainter &paint, int x0, double minf, double maxf)
A class for mapping intensity values onto various colour maps.
Definition: ColourMapper.h:27
int getWidth(LayerGeometryProvider *v, QPainter &paint)
void editEnd(LayerGeometryProvider *v, QMouseEvent *) override
void connectSignals(ModelId)
Definition: Layer.cpp:49
virtual int getPaintHeight() const
Event m_editingPoint
Definition: RegionLayer.h:155
PropertyType getPropertyType(const PropertyName &) const override
QString getText() const
SpacingMap m_distributionMap
Definition: RegionLayer.h:168
void paintVertical(LayerGeometryProvider *v, const ColourScaleLayer *layer, QPainter &paint, int x0, double minf, double maxf)
void setProperties(const QXmlAttributes &attributes) override
Set the particular properties of a layer (those specific to the subclass) from a set of XML attribute...
void resizeSelection(Selection s, Selection newSize) override
QString getScaleUnits() const override
QString getPropertyValueLabel(const PropertyName &, int value) const override
static QString getColourMapId(int n)
Return a machine-readable id string for the colour map with the given index.
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)
virtual bool getVisibleExtentsForUnit(QString unit, double &min, double &max, bool &log) const =0
Return the visible vertical extents for the given unit, if any.
void eraseStart(LayerGeometryProvider *v, QMouseEvent *) override
PlotStyle m_plotStyle
Definition: RegionLayer.h:160
void eraseEnd(LayerGeometryProvider *v, QMouseEvent *) override
void recalcSpacing()
int getYForValue(LayerGeometryProvider *v, double value) const override
VerticalScaleLayer and ColourScaleLayer methods.
void setFillColourMap(int)
void editDrag(LayerGeometryProvider *v, QMouseEvent *) override
void drawStart(LayerGeometryProvider *v, QMouseEvent *) override
void setProperty(const PropertyName &, int value) override
QString getLabelPreceding(sv_frame_t) const override
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...
virtual int getXForFrame(sv_frame_t frame) const =0
Return the pixel x-coordinate corresponding to a given sample frame (which may be negative)...
ChangeEventsCommand * m_editingCommand
Definition: RegionLayer.h:156
SpacingMap m_spacingMap
Definition: RegionLayer.h:165
virtual int getPaintWidth() const
void eraseDrag(LayerGeometryProvider *v, QMouseEvent *) override
sv_frame_t getFrameDuration() const
int getDefaultColourHint(bool dark, bool &impose) override
QString getPropertyLabel(const PropertyName &) const override
static ColourDatabase * getInstance()
void paintVertical(LayerGeometryProvider *v, const ColourScaleLayer *layer, QPainter &paint, int x0, double minf, double maxf)
void finish(ChangeEventsCommand *command)
Definition: RegionLayer.h:173
virtual View * getView()=0
void setValue(float value)
bool getValueExtents(double &min, double &max, bool &log, 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...
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.