NoteLayer.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 "NoteLayer.h"
17 
18 #include "data/model/Model.h"
19 #include "base/RealTime.h"
20 #include "base/Profiler.h"
21 #include "base/Pitch.h"
22 #include "base/LogRange.h"
23 #include "base/RangeMapper.h"
24 #include "view/View.h"
25 
26 #include "ColourDatabase.h"
27 #include "PianoScale.h"
28 #include "LinearNumericalScale.h"
29 #include "LogNumericalScale.h"
30 #include "PaintAssistant.h"
31 
32 #include "data/model/NoteModel.h"
33 
34 #include "widgets/ItemEditDialog.h"
35 #include "widgets/TextAbbrev.h"
36 
37 #include <QPainter>
38 #include <QPainterPath>
39 #include <QMouseEvent>
40 #include <QTextStream>
41 #include <QMessageBox>
42 
43 #include <iostream>
44 #include <cmath>
45 #include <utility>
46 
47 //#define DEBUG_NOTE_LAYER 1
48 
51  m_modelUsesHz(true),
52  m_editing(false),
53  m_dragPointX(0),
54  m_dragPointY(0),
55  m_dragStartX(0),
56  m_dragStartY(0),
57  m_originalPoint(0, 0.0, 0, 1.f, tr("New Point")),
58  m_editingPoint(0, 0.0, 0, 1.f, tr("New Point")),
59  m_editingCommand(nullptr),
60  m_editIsOpen(false),
61  m_verticalScale(AutoAlignScale),
62  m_scaleMinimum(0),
63  m_scaleMaximum(0)
64 {
65  SVDEBUG << "constructed NoteLayer" << endl;
66 }
67 
68 int
70 {
71  auto model = ModelById::get(m_model);
72  if (model) return model->getCompletion();
73  else return 0;
74 }
75 
76 void
77 NoteLayer::setModel(ModelId modelId)
78 {
79  auto newModel = ModelById::getAs<NoteModel>(modelId);
80 
81  if (!modelId.isNone() && !newModel) {
82  throw std::logic_error("Not a NoteModel");
83  }
84 
85  if (m_model == modelId) return;
86  m_model = modelId;
87 
88  if (newModel) {
90 
91  QString unit = newModel->getScaleUnits();
92  m_modelUsesHz = (unit.toLower() == "hz");
93  }
94 
95  m_scaleMinimum = 0;
96  m_scaleMaximum = 0;
97 
98  emit modelReplaced();
99 }
100 
101 Layer::PropertyList
103 {
104  PropertyList list = SingleColourLayer::getProperties();
105  list.push_back("Vertical Scale");
106  list.push_back("Scale Units");
107  return list;
108 }
109 
110 QString
111 NoteLayer::getPropertyLabel(const PropertyName &name) const
112 {
113  if (name == "Vertical Scale") return tr("Vertical Scale");
114  if (name == "Scale Units") return tr("Scale Units");
116 }
117 
118 Layer::PropertyType
119 NoteLayer::getPropertyType(const PropertyName &name) const
120 {
121  if (name == "Scale Units") return UnitsProperty;
122  if (name == "Vertical Scale") return ValueProperty;
124 }
125 
126 QString
127 NoteLayer::getPropertyGroupName(const PropertyName &name) const
128 {
129  if (name == "Vertical Scale" || name == "Scale Units") {
130  return tr("Scale");
131  }
133 }
134 
135 QString
137 {
138  return "Hz";
139 }
140 
141 int
142 NoteLayer::getPropertyRangeAndValue(const PropertyName &name,
143  int *min, int *max, int *deflt) const
144 {
145  int val = 0;
146 
147  if (name == "Vertical Scale") {
148 
149  if (min) *min = 0;
150  if (max) *max = 3;
151  if (deflt) *deflt = int(AutoAlignScale);
152 
153  val = int(m_verticalScale);
154 
155  } else if (name == "Scale Units") {
156 
157  if (deflt) *deflt = 0;
158  auto model = ModelById::getAs<NoteModel>(m_model);
159  if (model) {
160  val = UnitDatabase::getInstance()->getUnitId
161  (model->getScaleUnits());
162  }
163 
164  } else {
165 
166  val = SingleColourLayer::getPropertyRangeAndValue(name, min, max, deflt);
167  }
168 
169  return val;
170 }
171 
172 QString
173 NoteLayer::getPropertyValueLabel(const PropertyName &name,
174  int value) const
175 {
176  if (name == "Vertical Scale") {
177  switch (value) {
178  default:
179  case 0: return tr("Auto-Align");
180  case 1: return tr("Linear");
181  case 2: return tr("Log");
182  case 3: return tr("MIDI Notes");
183  }
184  }
185  return SingleColourLayer::getPropertyValueLabel(name, value);
186 }
187 
188 void
189 NoteLayer::setProperty(const PropertyName &name, int value)
190 {
191  if (name == "Vertical Scale") {
193  } else if (name == "Scale Units") {
194  auto model = ModelById::getAs<NoteModel>(m_model);
195  if (model) {
196  QString unit = UnitDatabase::getInstance()->getUnitById(value);
197  model->setScaleUnits(unit);
198  m_modelUsesHz = (unit.toLower() == "hz");
199  emit modelChanged(m_model);
200  }
201  } else {
202  return SingleColourLayer::setProperty(name, value);
203  }
204 }
205 
206 void
208 {
209  if (m_verticalScale == scale) return;
210  m_verticalScale = scale;
211  emit layerParametersChanged();
212 }
213 
214 bool
216 {
217  QPoint discard;
218  return !v->shouldIlluminateLocalFeatures(this, discard);
219 }
220 
221 double
222 NoteLayer::valueOf(const Event &e) const
223 {
224  return convertValueFromEventValue(e.getValue());
225 }
226 
227 Event
228 NoteLayer::eventWithValue(const Event &e, double value) const
229 {
230  return e.withValue(convertValueToEventValue(value));
231 }
232 
233 double
235 {
236  if (m_modelUsesHz) {
237  return eventValue;
238  } else {
239  double v = eventValue;
240  if (v < 0) v = 0;
241  if (v > 127) v = 127;
242  int p = int(round(v));
243  double c = 100.0 * (v - p);
244  return Pitch::getFrequencyForPitch(p, c);
245  }
246 }
247 
248 float
250 {
251  if (m_modelUsesHz) {
252  return float(value);
253  } else {
254  float c = 0;
255  int p = Pitch::getPitchForFrequency(value, &c);
256  return float(p) + c / 100.f;
257  }
258 }
259 
260 bool
261 NoteLayer::getValueExtents(double &min, double &max,
262  bool &logarithmic, QString &unit) const
263 {
264  auto model = ModelById::getAs<NoteModel>(m_model);
265  if (!model) return false;
266 
267  min = convertValueFromEventValue(model->getValueMinimum());
268  max = convertValueFromEventValue(model->getValueMaximum());
269  min /= 1.06;
270  max *= 1.06;
271  unit = "Hz";
272 
273  if (m_verticalScale != LinearScale) {
274  logarithmic = true;
275  }
276 
277  return true;
278 }
279 
280 bool
281 NoteLayer::getDisplayExtents(double &min, double &max) const
282 {
283  auto model = ModelById::getAs<NoteModel>(m_model);
284  if (!model || shouldAutoAlign()) return false;
285 
287  min = Pitch::getFrequencyForPitch(0);
288  max = Pitch::getFrequencyForPitch(127);
289  return true;
290  }
291 
293  QString unit;
294  bool log = false;
295  getValueExtents(min, max, log, unit);
296  } else {
297  min = m_scaleMinimum;
298  max = m_scaleMaximum;
299  }
300 
301 #ifdef DEBUG_NOTE_LAYER
302  SVCERR << "NoteLayer::getDisplayExtents: min = " << min << ", max = " << max << " (m_scaleMinimum = " << m_scaleMinimum << ", m_scaleMaximum = " << m_scaleMaximum << ")" << endl;
303 #endif
304 
305  return true;
306 }
307 
308 bool
309 NoteLayer::setDisplayExtents(double min, double max)
310 {
311  if (m_model.isNone()) return false;
312 
313  if (min == max) {
314  if (min == 0.f) {
315  max = 1.f;
316  } else {
317  max = min * 1.0001;
318  }
319  }
320 
321  m_scaleMinimum = min;
322  m_scaleMaximum = max;
323 
324 #ifdef DEBUG_NOTE_LAYER
325  SVCERR << "NoteLayer::setDisplayExtents: min = " << min << ", max = " << max << endl;
326 #endif
327 
328  emit layerParametersChanged();
329  return true;
330 }
331 
332 int
333 NoteLayer::getVerticalZoomSteps(int &defaultStep) const
334 {
335  if (shouldAutoAlign() || m_model.isNone()) return 0;
336  defaultStep = 0;
337  return 100;
338 }
339 
340 int
342 {
343  if (shouldAutoAlign() || m_model.isNone()) return 0;
344 
345  RangeMapper *mapper = getNewVerticalZoomRangeMapper();
346  if (!mapper) return 0;
347 
348  double dmin, dmax;
349  getDisplayExtents(dmin, dmax);
350 
351  int nr = mapper->getPositionForValue(dmax - dmin);
352 
353  delete mapper;
354 
355  return 100 - nr;
356 }
357 
359 
360 void
362 {
363  if (shouldAutoAlign() || m_model.isNone()) return;
364 
365  RangeMapper *mapper = getNewVerticalZoomRangeMapper();
366  if (!mapper) return;
367 
368  double min, max;
369  bool logarithmic;
370  QString unit;
371  getValueExtents(min, max, logarithmic, unit);
372 
373  double dmin, dmax;
374  getDisplayExtents(dmin, dmax);
375 
376  double newdist = mapper->getValueForPosition(100 - step);
377 
378  double newmin, newmax;
379 
380  if (logarithmic) {
381 
382  // see SpectrogramLayer::setVerticalZoomStep
383 
384  newmax = (newdist + sqrt(newdist*newdist + 4*dmin*dmax)) / 2;
385  newmin = newmax - newdist;
386 
387 // cerr << "newmin = " << newmin << ", newmax = " << newmax << endl;
388 
389  } else {
390  double dmid = (dmax + dmin) / 2;
391  newmin = dmid - newdist / 2;
392  newmax = dmid + newdist / 2;
393  }
394 
395  if (newmin < min) {
396  newmax += (min - newmin);
397  newmin = min;
398  }
399  if (newmax > max) {
400  newmax = max;
401  }
402 
403 #ifdef DEBUG_NOTE_LAYER
404  SVCERR << "NoteLayer::setVerticalZoomStep: " << step << ": " << newmin << " -> " << newmax << " (range " << newdist << ")" << endl;
405 #endif
406 
407  setDisplayExtents(newmin, newmax);
408 }
409 
410 RangeMapper *
412 {
413  if (m_model.isNone()) return nullptr;
414 
415  RangeMapper *mapper;
416 
417  double min, max;
418  bool logarithmic;
419  QString unit;
420  getValueExtents(min, max, logarithmic, unit);
421 
422  if (min == max) return nullptr;
423 
424  if (logarithmic) {
425  mapper = new LogRangeMapper(0, 100, min, max, unit);
426  } else {
427  mapper = new LinearRangeMapper(0, 100, min, max, unit);
428  }
429 
430  return mapper;
431 }
432 
433 EventVector
435 {
436  auto model = ModelById::getAs<NoteModel>(m_model);
437  if (!model) return {};
438 
439  sv_frame_t frame = v->getFrameForX(x);
440 
441  EventVector local = model->getEventsCovering(frame);
442  if (!local.empty()) return local;
443 
444  int fuzz = ViewManager::scalePixelSize(2);
445  sv_frame_t start = v->getFrameForX(x - fuzz);
446  sv_frame_t end = v->getFrameForX(x + fuzz);
447 
448  local = model->getEventsStartingWithin(frame, end - frame);
449  if (!local.empty()) return local;
450 
451  local = model->getEventsSpanning(start, frame - start);
452  if (!local.empty()) return local;
453 
454  return {};
455 }
456 
457 bool
458 NoteLayer::getPointToDrag(LayerGeometryProvider *v, int x, int y, Event &point) const
459 {
460  auto model = ModelById::getAs<NoteModel>(m_model);
461  if (!model) return false;
462 
463  sv_frame_t frame = v->getFrameForX(x);
464 
465  EventVector onPoints = model->getEventsCovering(frame);
466  if (onPoints.empty()) return false;
467 
468  int nearestDistance = -1;
469  for (const auto &p: onPoints) {
470  int distance = getYForValue(v, valueOf(p)) - y;
471  if (distance < 0) distance = -distance;
472  if (nearestDistance == -1 || distance < nearestDistance) {
473  nearestDistance = distance;
474  point = p;
475  }
476  }
477 
478  return true;
479 }
480 
481 QString
483 {
484  int x = pos.x();
485 
486  auto model = ModelById::getAs<NoteModel>(m_model);
487  if (!model || !model->getSampleRate()) return "";
488 
489  EventVector points = getLocalPoints(v, x);
490 
491  if (points.empty()) {
492  if (!model->isReady()) {
493  return tr("In progress");
494  } else {
495  return tr("No local points");
496  }
497  }
498 
499  Event note;
500  EventVector::iterator i;
501 
502  for (i = points.begin(); i != points.end(); ++i) {
503 
504  int y = getYForValue(v, valueOf(*i));
505  int h = 3;
506 
507  if (model->getValueQuantization() != 0.0) {
508  h = y - getYForValue
509  (v, convertValueFromEventValue(i->getValue() +
510  model->getValueQuantization()));
511  if (h < 3) h = 3;
512  }
513 
514  if (pos.y() >= y - h && pos.y() <= y) {
515  note = *i;
516  break;
517  }
518  }
519 
520  if (i == points.end()) return tr("No local points");
521 
522  RealTime rt = RealTime::frame2RealTime(note.getFrame(),
523  model->getSampleRate());
524  RealTime rd = RealTime::frame2RealTime(note.getDuration(),
525  model->getSampleRate());
526 
527  QString pitchText;
528 
529  if (m_modelUsesHz) {
530 
531  float value = note.getValue();
532 
533  pitchText = tr("%1 Hz (%2, %3)")
534  .arg(value)
535  .arg(Pitch::getPitchLabelForFrequency(value))
536  .arg(Pitch::getPitchForFrequency(value));
537 
538  } else {
539 
540  float eventValue = note.getValue();
541  double value = convertValueFromEventValue(eventValue);
542 
543  int mnote = int(lrint(eventValue));
544  int cents = int(lrint((eventValue - float(mnote)) * 100));
545 
546  pitchText = tr("%1 (%2, %3 Hz)")
547  .arg(Pitch::getPitchLabel(mnote, cents))
548  .arg(eventValue)
549  .arg(value);
550  }
551 
552  QString text;
553 
554  if (note.getLabel() == "") {
555  text = QString(tr("Time:\t%1\nPitch:\t%2\nDuration:\t%3\nNo label"))
556  .arg(rt.toText(true).c_str())
557  .arg(pitchText)
558  .arg(rd.toText(true).c_str());
559  } else {
560  text = QString(tr("Time:\t%1\nPitch:\t%2\nDuration:\t%3\nLabel:\t%4"))
561  .arg(rt.toText(true).c_str())
562  .arg(pitchText)
563  .arg(rd.toText(true).c_str())
564  .arg(note.getLabel());
565  }
566 
567  pos = QPoint(v->getXForFrame(note.getFrame()),
568  getYForValue(v, valueOf(note)));
569  return text;
570 }
571 
572 bool
574  int &resolution,
575  SnapType snap, int ycoord) const
576 {
577  auto model = ModelById::getAs<NoteModel>(m_model);
578  if (!model) {
579  return Layer::snapToFeatureFrame(v, frame, resolution, snap, ycoord);
580  }
581 
582  // SnapLeft / SnapRight: return frame of nearest feature in that
583  // direction no matter how far away
584  //
585  // SnapNeighbouring: return frame of feature that would be used in
586  // an editing operation, i.e. closest feature in either direction
587  // but only if it is "close enough"
588 
589  resolution = model->getResolution();
590 
591  if (snap == SnapNeighbouring) {
592  EventVector points = getLocalPoints(v, v->getXForFrame(frame));
593  if (points.empty()) return false;
594  frame = points.begin()->getFrame();
595  return true;
596  }
597 
598  Event e;
599  if (model->getNearestEventMatching
600  (frame,
601  [](Event) { return true; },
602  snap == SnapLeft ? EventSeries::Backward : EventSeries::Forward,
603  e)) {
604  frame = e.getFrame();
605  return true;
606  }
607 
608  return false;
609 }
610 
611 void
612 NoteLayer::getScaleExtents(LayerGeometryProvider *v, double &min, double &max, bool &log) const
613 {
614  min = 0.0;
615  max = 0.0;
616  log = false;
617 
618  auto model = ModelById::getAs<NoteModel>(m_model);
619  if (!model) return;
620 
621  if (shouldAutoAlign()) {
622 
623  if (!v->getVisibleExtentsForUnit("Hz", min, max, log)) {
624 
625  QString unit;
626  getValueExtents(min, max, log, unit);
627 
628 #ifdef DEBUG_NOTE_LAYER
629  SVCERR << "NoteLayer[" << this << "]::getScaleExtents: min = " << min << ", max = " << max << ", log = " << log << endl;
630 #endif
631 
632  } else if (log) {
633 
634  LogRange::mapRange(min, max);
635 
636 #ifdef DEBUG_NOTE_LAYER
637  SVCERR << "NoteLayer[" << this << "]::getScaleExtents: min = " << min << ", max = " << max << ", log = " << log << endl;
638 #endif
639  }
640 
641  } else {
642 
643  getDisplayExtents(min, max);
644 
645  if (m_verticalScale != LinearScale) {
646  LogRange::mapRange(min, max);
647  log = true;
648  }
649  }
650 
651  if (max == min) max = min + 1.0;
652 }
653 
654 int
656 {
657  double min = 0.0, max = 0.0;
658  bool logarithmic = false;
659  int h = v->getPaintHeight();
660 
661  getScaleExtents(v, min, max, logarithmic);
662 
663 #ifdef DEBUG_NOTE_LAYER
664  SVCERR << "NoteLayer[" << this << "]::getYForValue(" << val << "): min = " << min << ", max = " << max << ", log = " << logarithmic << endl;
665 #endif
666 
667  if (logarithmic) {
668  val = LogRange::map(val);
669 #ifdef DEBUG_NOTE_LAYER
670  SVCERR << "logarithmic true, val now = " << val << endl;
671 #endif
672  }
673 
674  int y = int(h - ((val - min) * h) / (max - min)) - 1;
675 #ifdef DEBUG_NOTE_LAYER
676  SVCERR << "y = " << y << endl;
677 #endif
678  return y;
679 }
680 
681 double
683 {
684  double min = 0.0, max = 0.0;
685  bool logarithmic = false;
686  int h = v->getPaintHeight();
687 
688  getScaleExtents(v, min, max, logarithmic);
689 
690  double val = min + (double(h - y) * double(max - min)) / h;
691 
692  if (logarithmic) {
693  val = pow(10.0, val);
694  }
695 
696  return val;
697 }
698 
699 bool
701 {
702  if (m_model.isNone()) return false;
703  return (m_verticalScale == AutoAlignScale);
704 }
705 
706 void
707 NoteLayer::paint(LayerGeometryProvider *v, QPainter &paint, QRect rect) const
708 {
709  auto model = ModelById::getAs<NoteModel>(m_model);
710  if (!model || !model->isOK()) return;
711 
712  sv_samplerate_t sampleRate = model->getSampleRate();
713  if (!sampleRate) return;
714 
715 // Profiler profiler("NoteLayer::paint", true);
716 
717  int x0 = rect.left();
718  int x1 = x0 + rect.width();
719 
720  sv_frame_t frame0 = v->getFrameForX(x0);
721  sv_frame_t frame1 = v->getFrameForX(x1);
722 
723  EventVector points(model->getEventsSpanning(frame0, frame1 - frame0));
724  if (points.empty()) return;
725 
726  paint.setPen(getBaseQColor());
727 
728  QColor brushColour(getBaseQColor());
729  brushColour.setAlpha(80);
730 
731 // SVDEBUG << "NoteLayer::paint: resolution is "
732 // << model->getResolution() << " frames" << endl;
733 
734  double min = convertValueFromEventValue(model->getValueMinimum());
735  double max = convertValueFromEventValue(model->getValueMaximum());
736  if (max == min) max = min + 1.0;
737 
738  QPoint localPos;
739  Event illuminatePoint;
740  bool shouldIlluminate = false;
741 
742  if (m_editing || m_editIsOpen) {
743  shouldIlluminate = true;
744  illuminatePoint = m_editingPoint;
745  } else if (v->shouldIlluminateLocalFeatures(this, localPos)) {
746  shouldIlluminate = getPointToDrag(v, localPos.x(), localPos.y(),
747  illuminatePoint);
748  }
749 
750  paint.save();
751  paint.setRenderHint(QPainter::Antialiasing, false);
752 
753  for (EventVector::const_iterator i = points.begin();
754  i != points.end(); ++i) {
755 
756  const Event &p(*i);
757 
758  int x = v->getXForFrame(p.getFrame());
759  int y = getYForValue(v, valueOf(p));
760  int w = v->getXForFrame(p.getFrame() + p.getDuration()) - x;
761  int h = 3;
762 
763  if (model->getValueQuantization() != 0.0) {
764  h = y - getYForValue
766  (p.getValue() + model->getValueQuantization()));
767  if (h < 3) h = 3;
768  }
769 
770  if (w < 1) w = 1;
771  paint.setPen(getBaseQColor());
772  paint.setBrush(brushColour);
773 
774  if (shouldIlluminate && illuminatePoint == p) {
775 
776  paint.setPen(v->getForeground());
777  paint.setBrush(v->getForeground());
778 
779  // Qt 5.13 deprecates QFontMetrics::width(), but its suggested
780  // replacement (horizontalAdvance) was only added in Qt 5.11
781  // which is too new for us
782 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
783 
784  QString vlabel;
785  if (m_modelUsesHz) {
786  vlabel = QString("%1%2")
787  .arg(p.getValue())
788  .arg(model->getScaleUnits());
789  } else {
790  vlabel = QString("%1 %2")
791  .arg(p.getValue())
792  .arg(model->getScaleUnits());
793  }
794 
796  x - paint.fontMetrics().width(vlabel) - 2,
797  y + paint.fontMetrics().height()/2
798  - paint.fontMetrics().descent(),
800 
801  QString hlabel = RealTime::frame2RealTime
802  (p.getFrame(), model->getSampleRate()).toText(true).c_str();
804  x,
805  y - h/2 - paint.fontMetrics().descent() - 2,
807  }
808 
809  paint.drawRect(x, y - h/2, w, h);
810  }
811 
812  paint.restore();
813 }
814 
815 int
817 {
818  if (m_model.isNone()) {
819  return 0;
820  }
821 
822  if (shouldAutoAlign() && !valueExtentsMatchMine(v)) {
823  return 0;
824  }
825 
826  if (m_verticalScale != LinearScale) {
827  return LogNumericalScale().getWidth(v, paint) + 10; // for piano
828  } else {
829  return LinearNumericalScale().getWidth(v, paint);
830  }
831 }
832 
833 void
835 {
836  auto model = ModelById::getAs<NoteModel>(m_model);
837  if (!model || model->isEmpty()) return;
838 
839  QString unit;
840  double min, max;
841  bool logarithmic;
842 
843  int w = getVerticalScaleWidth(v, false, paint);
844  int h = v->getPaintHeight();
845 
846  getScaleExtents(v, min, max, logarithmic);
847 
848  if (logarithmic) {
849  LogNumericalScale().paintVertical(v, this, paint, 0, min, max);
850  } else {
851  LinearNumericalScale().paintVertical(v, this, paint, 0, min, max);
852  }
853 
854  if (logarithmic) {
856  (v, paint, QRect(w - 10, 0, 10, h),
857  LogRange::unmap(min),
858  LogRange::unmap(max));
859  paint.drawLine(w, 0, w, h);
860  }
861 
862  if (getScaleUnits() != "") {
863  int mw = w - 5;
864  paint.drawText(5,
865  5 + paint.fontMetrics().ascent(),
867  paint.fontMetrics(),
868  mw));
869  }
870 }
871 
872 void
874 {
875 // SVDEBUG << "NoteLayer::drawStart(" << e->x() << "," << e->y() << ")" << endl;
876 
877  auto model = ModelById::getAs<NoteModel>(m_model);
878  if (!model) return;
879 
880  sv_frame_t frame = v->getFrameForX(e->x());
881  if (frame < 0) frame = 0;
882  frame = frame / model->getResolution() * model->getResolution();
883 
884  double value = getValueForY(v, e->y());
885  float eventValue = convertValueToEventValue(value);
886  eventValue = roundf(eventValue);
887 
888  m_editingPoint = Event(frame, eventValue, 0, 0.8f, tr("New Point"));
890 
892  m_editingCommand = new ChangeEventsCommand(m_model.untyped, tr("Draw Point"));
894 
895  m_editing = true;
896 }
897 
898 void
900 {
901 // SVDEBUG << "NoteLayer::drawDrag(" << e->x() << "," << e->y() << ")" << endl;
902 
903  auto model = ModelById::getAs<NoteModel>(m_model);
904  if (!model || !m_editing) return;
905 
906  sv_frame_t frame = v->getFrameForX(e->x());
907  if (frame < 0) frame = 0;
908  frame = frame / model->getResolution() * model->getResolution();
909 
910  double newValue = getValueForY(v, e->y());
911  float newEventValue = convertValueToEventValue(newValue);
912  newEventValue = roundf(newEventValue);
913 
914  sv_frame_t newFrame = m_editingPoint.getFrame();
915  sv_frame_t newDuration = frame - newFrame;
916  if (newDuration < 0) {
917  newFrame = frame;
918  newDuration = -newDuration;
919  } else if (newDuration == 0) {
920  newDuration = 1;
921  }
922 
925  .withFrame(newFrame)
926  .withDuration(newDuration)
927  .withValue(newEventValue);
929 }
930 
931 void
933 {
934 // SVDEBUG << "NoteLayer::drawEnd(" << e->x() << "," << e->y() << ")" << endl;
935  auto model = ModelById::getAs<NoteModel>(m_model);
936  if (!model || !m_editing) return;
938  m_editingCommand = nullptr;
939  m_editing = false;
940 }
941 
942 void
944 {
945  auto model = ModelById::getAs<NoteModel>(m_model);
946  if (!model) return;
947 
948  if (!getPointToDrag(v, e->x(), e->y(), m_editingPoint)) return;
949 
950  if (m_editingCommand) {
952  m_editingCommand = nullptr;
953  }
954 
955  m_editing = true;
956 }
957 
958 void
960 {
961 }
962 
963 void
965 {
966  auto model = ModelById::getAs<NoteModel>(m_model);
967  if (!model || !m_editing) return;
968 
969  m_editing = false;
970 
971  Event p(0);
972  if (!getPointToDrag(v, e->x(), e->y(), p)) return;
973  if (p.getFrame() != m_editingPoint.getFrame() ||
974  p.getValue() != m_editingPoint.getValue()) return;
975 
976  m_editingCommand = new ChangeEventsCommand(m_model.untyped, tr("Erase Point"));
977 
979 
981  m_editingCommand = nullptr;
982  m_editing = false;
983 }
984 
985 void
987 {
988 // SVDEBUG << "NoteLayer::editStart(" << e->x() << "," << e->y() << ")" << endl;
989 
990  auto model = ModelById::getAs<NoteModel>(m_model);
991  if (!model) return;
992 
993  if (!getPointToDrag(v, e->x(), e->y(), m_editingPoint)) return;
995 
996  m_dragPointX = v->getXForFrame(m_editingPoint.getFrame());
998 
999  if (m_editingCommand) {
1001  m_editingCommand = nullptr;
1002  }
1003 
1004  m_editing = true;
1005  m_dragStartX = e->x();
1006  m_dragStartY = e->y();
1007 }
1008 
1009 void
1011 {
1012 // SVDEBUG << "NoteLayer::editDrag(" << e->x() << "," << e->y() << ")" << endl;
1013 
1014  auto model = ModelById::getAs<NoteModel>(m_model);
1015  if (!model || !m_editing) return;
1016 
1017  int xdist = e->x() - m_dragStartX;
1018  int ydist = e->y() - m_dragStartY;
1019  int newx = m_dragPointX + xdist;
1020  int newy = m_dragPointY + ydist;
1021 
1022  sv_frame_t frame = v->getFrameForX(newx);
1023  if (frame < 0) frame = 0;
1024  frame = frame / model->getResolution() * model->getResolution();
1025 
1026  double newValue = getValueForY(v, newy);
1027  float newEventValue = convertValueToEventValue(newValue);
1028  newEventValue = roundf(newEventValue);
1029 
1030  if (!m_editingCommand) {
1031  m_editingCommand = new ChangeEventsCommand
1032  (m_model.untyped, tr("Drag Point"));
1033  }
1034 
1037  .withFrame(frame)
1038  .withValue(newEventValue);
1040 }
1041 
1042 void
1044 {
1045 // SVDEBUG << "NoteLayer::editEnd(" << e->x() << "," << e->y() << ")" << endl;
1046  auto model = ModelById::getAs<NoteModel>(m_model);
1047  if (!model || !m_editing) return;
1048 
1049  if (m_editingCommand) {
1050 
1051  QString newName = m_editingCommand->getName();
1052 
1053  if (m_editingPoint.getFrame() != m_originalPoint.getFrame()) {
1054  if (m_editingPoint.getValue() != m_originalPoint.getValue()) {
1055  newName = tr("Edit Point");
1056  } else {
1057  newName = tr("Relocate Point");
1058  }
1059  } else {
1060  newName = tr("Change Point Value");
1061  }
1062 
1063  m_editingCommand->setName(newName);
1065  }
1066 
1067  m_editingCommand = nullptr;
1068  m_editing = false;
1069 }
1070 
1071 bool
1073 {
1074  auto model = ModelById::getAs<NoteModel>(m_model);
1075  if (!model) return false;
1076 
1077  Event note(0);
1078  if (!getPointToDrag(v, e->x(), e->y(), note)) return false;
1079 
1080  ItemEditDialog *dialog = new ItemEditDialog
1081  (model->getSampleRate(),
1087  getScaleUnits());
1088 
1089  dialog->setFrameTime(note.getFrame());
1090  dialog->setValue(note.getValue());
1091  dialog->setFrameDuration(note.getDuration());
1092  dialog->setText(note.getLabel());
1093 
1094  m_editingPoint = note;
1095  m_editIsOpen = true;
1096 
1097  if (dialog->exec() == QDialog::Accepted) {
1098 
1099  Event newNote = note
1100  .withFrame(dialog->getFrameTime())
1101  .withValue(dialog->getValue())
1102  .withDuration(dialog->getFrameDuration())
1103  .withLabel(dialog->getText());
1104 
1105  ChangeEventsCommand *command = new ChangeEventsCommand
1106  (m_model.untyped, tr("Edit Point"));
1107  command->remove(note);
1108  command->add(newNote);
1109  finish(command);
1110  }
1111 
1112  m_editingPoint = 0;
1113  m_editIsOpen = false;
1114 
1115  delete dialog;
1116  return true;
1117 }
1118 
1119 void
1120 NoteLayer::moveSelection(Selection s, sv_frame_t newStartFrame)
1121 {
1122  auto model = ModelById::getAs<NoteModel>(m_model);
1123  if (!model) return;
1124 
1125  ChangeEventsCommand *command =
1126  new ChangeEventsCommand(m_model.untyped, tr("Drag Selection"));
1127 
1128  EventVector points =
1129  model->getEventsStartingWithin(s.getStartFrame(), s.getDuration());
1130 
1131  for (Event p: points) {
1132  command->remove(p);
1133  Event moved = p.withFrame(p.getFrame() +
1134  newStartFrame - s.getStartFrame());
1135  command->add(moved);
1136  }
1137 
1138  finish(command);
1139 }
1140 
1141 void
1142 NoteLayer::resizeSelection(Selection s, Selection newSize)
1143 {
1144  auto model = ModelById::getAs<NoteModel>(m_model);
1145  if (!model || !s.getDuration()) return;
1146 
1147  ChangeEventsCommand *command =
1148  new ChangeEventsCommand(m_model.untyped, tr("Resize Selection"));
1149 
1150  EventVector points =
1151  model->getEventsStartingWithin(s.getStartFrame(), s.getDuration());
1152 
1153  double ratio = double(newSize.getDuration()) / double(s.getDuration());
1154  double oldStart = double(s.getStartFrame());
1155  double newStart = double(newSize.getStartFrame());
1156 
1157  for (Event p: points) {
1158 
1159  double newFrame = (double(p.getFrame()) - oldStart) * ratio + newStart;
1160  double newDuration = double(p.getDuration()) * ratio;
1161 
1162  Event newPoint = p
1163  .withFrame(lrint(newFrame))
1164  .withDuration(lrint(newDuration));
1165  command->remove(p);
1166  command->add(newPoint);
1167  }
1168 
1169  finish(command);
1170 }
1171 
1172 void
1174 {
1175  auto model = ModelById::getAs<NoteModel>(m_model);
1176  if (!model) return;
1177 
1178  ChangeEventsCommand *command =
1179  new ChangeEventsCommand(m_model.untyped, tr("Delete Selected Points"));
1180 
1181  EventVector points =
1182  model->getEventsStartingWithin(s.getStartFrame(), s.getDuration());
1183 
1184  for (Event p: points) {
1185  command->remove(p);
1186  }
1187 
1188  finish(command);
1189 }
1190 
1191 void
1192 NoteLayer::copy(LayerGeometryProvider *v, Selection s, Clipboard &to)
1193 {
1194  auto model = ModelById::getAs<NoteModel>(m_model);
1195  if (!model) return;
1196 
1197  EventVector points =
1198  model->getEventsStartingWithin(s.getStartFrame(), s.getDuration());
1199 
1200  for (Event p: points) {
1201  to.addPoint(p.withReferenceFrame(alignToReference(v, p.getFrame())));
1202  }
1203 }
1204 
1205 bool
1206 NoteLayer::paste(LayerGeometryProvider *v, const Clipboard &from,
1207  sv_frame_t /* frameOffset */, bool /* interactive */)
1208 {
1209  auto model = ModelById::getAs<NoteModel>(m_model);
1210  if (!model) return false;
1211 
1212  const EventVector &points = from.getPoints();
1213 
1214  bool realign = false;
1215 
1216  if (clipboardHasDifferentAlignment(v, from)) {
1217 
1218  QMessageBox::StandardButton button =
1219  QMessageBox::question(v->getView(), tr("Re-align pasted items?"),
1220  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?"),
1221  QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel,
1222  QMessageBox::Yes);
1223 
1224  if (button == QMessageBox::Cancel) {
1225  return false;
1226  }
1227 
1228  if (button == QMessageBox::Yes) {
1229  realign = true;
1230  }
1231  }
1232 
1233  ChangeEventsCommand *command =
1234  new ChangeEventsCommand(m_model.untyped, tr("Paste"));
1235 
1236  for (EventVector::const_iterator i = points.begin();
1237  i != points.end(); ++i) {
1238 
1239  sv_frame_t frame = 0;
1240 
1241  if (!realign) {
1242 
1243  frame = i->getFrame();
1244 
1245  } else {
1246 
1247  if (i->hasReferenceFrame()) {
1248  frame = i->getReferenceFrame();
1249  frame = alignFromReference(v, frame);
1250  } else {
1251  frame = i->getFrame();
1252  }
1253  }
1254 
1255  Event p = i->withFrame(frame);
1256 
1257  Event newPoint = p;
1258  if (!p.hasValue()) {
1259  newPoint = newPoint.withValue((model->getValueMinimum() +
1260  model->getValueMaximum()) / 2);
1261  }
1262  if (!p.hasDuration()) {
1263  sv_frame_t nextFrame = frame;
1264  EventVector::const_iterator j = i;
1265  for (; j != points.end(); ++j) {
1266  if (j != i) break;
1267  }
1268  if (j != points.end()) {
1269  nextFrame = j->getFrame();
1270  }
1271  if (nextFrame == frame) {
1272  newPoint = newPoint.withDuration(model->getResolution());
1273  } else {
1274  newPoint = newPoint.withDuration(nextFrame - frame);
1275  }
1276  }
1277 
1278  command->add(newPoint);
1279  }
1280 
1281  finish(command);
1282  return true;
1283 }
1284 
1285 void
1286 NoteLayer::addNoteOn(sv_frame_t frame, int pitch, int velocity)
1287 {
1288  double value = Pitch::getFrequencyForPitch(pitch);
1289  float eventValue = convertValueToEventValue(value);
1290  m_pendingNoteOns.insert(Event(frame, eventValue, 0,
1291  float(velocity) / 127.f, QString()));
1292 }
1293 
1294 void
1295 NoteLayer::addNoteOff(sv_frame_t frame, int pitch)
1296 {
1297  auto model = ModelById::getAs<NoteModel>(m_model);
1298 
1299  for (NoteSet::iterator i = m_pendingNoteOns.begin();
1300  i != m_pendingNoteOns.end(); ++i) {
1301 
1302  Event p = *i;
1303  double value = valueOf(p);
1304  int eventPitch = Pitch::getPitchForFrequency(value);
1305 
1306  if (eventPitch == pitch) {
1307  m_pendingNoteOns.erase(i);
1308  Event note = p.withDuration(frame - p.getFrame());
1309  if (model) {
1310  ChangeEventsCommand *c = new ChangeEventsCommand
1311  (m_model.untyped, tr("Record Note"));
1312  c->add(note);
1313  // execute and bundle:
1314  CommandHistory::getInstance()->addCommand(c, true, true);
1315  }
1316  break;
1317  }
1318  }
1319 }
1320 
1321 void
1323 {
1324  m_pendingNoteOns.clear();
1325 }
1326 
1327 int
1328 NoteLayer::getDefaultColourHint(bool darkbg, bool &impose)
1329 {
1330  impose = false;
1332  (QString(darkbg ? "White" : "Black"));
1333 }
1334 
1335 void
1336 NoteLayer::toXml(QTextStream &stream,
1337  QString indent, QString extraAttributes) const
1338 {
1339  SingleColourLayer::toXml(stream, indent, extraAttributes +
1340  QString(" verticalScale=\"%1\" scaleMinimum=\"%2\" scaleMaximum=\"%3\" ")
1341  .arg(m_verticalScale)
1342  .arg(m_scaleMinimum)
1343  .arg(m_scaleMaximum));
1344 }
1345 
1346 void
1347 NoteLayer::setProperties(const QXmlAttributes &attributes)
1348 {
1350 
1351  bool ok, alsoOk;
1352  VerticalScale scale = (VerticalScale)
1353  attributes.value("verticalScale").toInt(&ok);
1354  if (ok) setVerticalScale(scale);
1355 
1356  float min = attributes.value("scaleMinimum").toFloat(&ok);
1357  float max = attributes.value("scaleMaximum").toFloat(&alsoOk);
1358  if (ok && alsoOk && min != max) setDisplayExtents(min, max);
1359 }
1360 
1361 
float getValue() const
QString getFeatureDescription(LayerGeometryProvider *v, QPoint &) const override
Definition: NoteLayer.cpp:482
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 moveSelection(Selection s, sv_frame_t newStartFrame) override
Definition: NoteLayer.cpp:1120
int getCurrentVerticalZoomStep() const override
Get the current vertical zoom step.
Definition: NoteLayer.cpp:341
void setProperty(const PropertyName &, int value) override
Definition: NoteLayer.cpp:189
void editDrag(LayerGeometryProvider *v, QMouseEvent *) override
Definition: NoteLayer.cpp:1010
int m_dragPointX
Definition: NoteLayer.h:168
void setFrameDuration(sv_frame_t frame)
virtual QColor getForeground() const =0
void drawStart(LayerGeometryProvider *v, QMouseEvent *) override
Definition: NoteLayer.cpp:873
int getVerticalScaleWidth(LayerGeometryProvider *v, bool, QPainter &) const override
Definition: NoteLayer.cpp:816
Event m_originalPoint
Definition: NoteLayer.h:172
PropertyList getProperties() const override
virtual bool shouldIlluminateLocalFeatures(const Layer *, QPoint &) const =0
static int scalePixelSize(int pixels)
Take a "design pixel" size and scale it for the actual display.
bool m_editIsOpen
Definition: NoteLayer.h:175
float convertValueToEventValue(double value) const
Definition: NoteLayer.cpp:249
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 m_dragPointY
Definition: NoteLayer.h:169
double m_scaleMaximum
Definition: NoteLayer.h:182
PropertyType getPropertyType(const PropertyName &) const override
Definition: NoteLayer.cpp:119
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...
Definition: NoteLayer.cpp:215
void addCommand(Command *command)
Add a command to the command history.
double getValueForY(LayerGeometryProvider *v, int y) const override
Definition: NoteLayer.cpp:682
void setFrameTime(sv_frame_t frame)
void paintVertical(LayerGeometryProvider *v, const VerticalScaleLayer *layer, QPainter &paint, int x0, double minlog, double maxlog)
int getCompletion(LayerGeometryProvider *) const override
Return the proportion of background work complete in drawing this view, as a percentage – in most ca...
Definition: NoteLayer.cpp:69
void drawEnd(LayerGeometryProvider *v, QMouseEvent *) override
Definition: NoteLayer.cpp:932
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 getWidth(LayerGeometryProvider *v, QPainter &paint)
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...
Definition: NoteLayer.cpp:261
virtual QColor getBaseQColor() const
VerticalScale m_verticalScale
Definition: NoteLayer.h:176
void toXml(QTextStream &stream, QString indent="", QString extraAttributes="") const override
int getVerticalZoomSteps(int &defaultStep) const override
Get the number of vertical zoom steps available for this layer.
Definition: NoteLayer.cpp:333
int getWidth(LayerGeometryProvider *v, QPainter &paint)
void eraseDrag(LayerGeometryProvider *v, QMouseEvent *) override
Definition: NoteLayer.cpp:959
double valueOf(const Event &e) const
Definition: NoteLayer.cpp:222
ChangeEventsCommand * m_editingCommand
Definition: NoteLayer.h:174
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.
NoteSet m_pendingNoteOns
Definition: NoteLayer.h:179
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.
Definition: NoteLayer.cpp:707
int m_dragStartX
Definition: NoteLayer.h:170
bool setDisplayExtents(double min, double max) override
Set the displayed minimum and maximum values for the y axis to the given range, if supported...
Definition: NoteLayer.cpp:309
int getColourIndex(QString name) const
Return the index of the colour with the given name, if found in the database.
void getScaleExtents(LayerGeometryProvider *, double &min, double &max, bool &log) const
Definition: NoteLayer.cpp:612
virtual sv_frame_t alignFromReference(LayerGeometryProvider *v, sv_frame_t frame) const
Definition: Layer.cpp:198
void addNoteOn(sv_frame_t frame, int pitch, int velocity)
Add a note-on.
Definition: NoteLayer.cpp:1286
QString getPropertyGroupName(const PropertyName &) const override
PropertyList getProperties() const override
Definition: NoteLayer.cpp:102
QString getPropertyValueLabel(const PropertyName &, int value) const override
Definition: NoteLayer.cpp:173
void layerParametersChanged()
Event m_editingPoint
Definition: NoteLayer.h:173
bool clipboardHasDifferentAlignment(LayerGeometryProvider *v, const Clipboard &clip) const
Definition: Layer.cpp:209
void paintPianoVertical(LayerGeometryProvider *v, QPainter &paint, QRect rect, double minf, double maxf)
Definition: PianoScale.cpp:31
int getPropertyRangeAndValue(const PropertyName &, int *min, int *max, int *deflt) const override
int getPropertyRangeAndValue(const PropertyName &, int *min, int *max, int *deflt) const override
Definition: NoteLayer.cpp:142
SnapType
Definition: Layer.h:195
void drawDrag(LayerGeometryProvider *v, QMouseEvent *) override
Definition: NoteLayer.cpp:899
QString getScaleUnits() const override
Definition: NoteLayer.cpp:136
virtual sv_frame_t alignToReference(LayerGeometryProvider *v, sv_frame_t frame) const
Definition: Layer.cpp:187
void paintVertical(LayerGeometryProvider *v, const VerticalScaleLayer *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...
Definition: NoteLayer.cpp:1347
static CommandHistory * getInstance()
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.
Definition: NoteLayer.cpp:1206
void abandonNoteOns()
Abandon all pending note-on events.
Definition: NoteLayer.cpp:1322
void connectSignals(ModelId)
Definition: Layer.cpp:49
virtual int getPaintHeight() const
bool valueExtentsMatchMine(LayerGeometryProvider *v) const
Definition: Layer.cpp:628
void finish(ChangeEventsCommand *command)
Definition: NoteLayer.h:186
PropertyType getPropertyType(const PropertyName &) const override
bool getPointToDrag(LayerGeometryProvider *v, int x, int y, Event &) const
Definition: NoteLayer.cpp:458
QString getText() const
void editEnd(LayerGeometryProvider *v, QMouseEvent *) override
Definition: NoteLayer.cpp:1043
Event eventWithValue(const Event &e, double value) const
Definition: NoteLayer.cpp:228
void setVerticalZoomStep(int) override
!! lots of duplication with TimeValueLayer
Definition: NoteLayer.cpp:361
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 copy(LayerGeometryProvider *v, Selection s, Clipboard &to) override
Definition: NoteLayer.cpp:1192
bool editOpen(LayerGeometryProvider *v, QMouseEvent *) override
Open an editor on the item under the mouse (e.g.
Definition: NoteLayer.cpp:1072
RangeMapper * getNewVerticalZoomRangeMapper() const override
Create and return a range mapper for vertical zoom step values.
Definition: NoteLayer.cpp:411
void addNoteOff(sv_frame_t frame, int pitch)
Add a note-off.
Definition: NoteLayer.cpp:1295
double m_scaleMinimum
Definition: NoteLayer.h:181
void editStart(LayerGeometryProvider *v, QMouseEvent *) override
Definition: NoteLayer.cpp:986
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.
Definition: NoteLayer.cpp:573
int m_dragStartY
Definition: NoteLayer.h:171
EventVector getLocalPoints(LayerGeometryProvider *v, int) const
Definition: NoteLayer.cpp:434
bool shouldAutoAlign() const
Definition: NoteLayer.cpp:700
QString getPropertyValueLabel(const PropertyName &, int value) const override
QString getPropertyLabel(const PropertyName &) const override
Definition: NoteLayer.cpp:111
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...
Definition: NoteLayer.cpp:281
bool m_editing
Definition: NoteLayer.h:167
ModelId m_model
Definition: NoteLayer.h:165
void eraseStart(LayerGeometryProvider *v, QMouseEvent *) override
Definition: NoteLayer.cpp:943
QString getPropertyGroupName(const PropertyName &) const override
Definition: NoteLayer.cpp:127
static void drawVisibleText(const LayerGeometryProvider *, QPainter &p, int x, int y, QString text, TextStyle style)
double convertValueFromEventValue(float eventValue) const
Definition: NoteLayer.cpp:234
int getYForValue(LayerGeometryProvider *v, double value) const override
VerticalScaleLayer methods.
Definition: NoteLayer.cpp:655
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 deleteSelection(Selection s) override
Definition: NoteLayer.cpp:1173
void setVerticalScale(VerticalScale scale)
Definition: NoteLayer.cpp:207
void resizeSelection(Selection s, Selection newSize) override
Definition: NoteLayer.cpp:1142
void paintVerticalScale(LayerGeometryProvider *v, bool, QPainter &paint, QRect rect) const override
Definition: NoteLayer.cpp:834
void eraseEnd(LayerGeometryProvider *v, QMouseEvent *) override
Definition: NoteLayer.cpp:964
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)...
sv_frame_t getFrameDuration() const
QString getPropertyLabel(const PropertyName &) const override
void setModel(ModelId model)
Definition: NoteLayer.cpp:77
static ColourDatabase * getInstance()
void toXml(QTextStream &stream, QString indent="", QString extraAttributes="") const override
Definition: NoteLayer.cpp:1336
virtual View * getView()=0
void setValue(float value)
bool m_modelUsesHz
Definition: NoteLayer.h:166
int getDefaultColourHint(bool dark, bool &impose) override
Definition: NoteLayer.cpp:1328