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