TimeInstantLayer.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 "TimeInstantLayer.h"
17 
18 #include "data/model/Model.h"
19 #include "base/RealTime.h"
20 #include "view/View.h"
21 #include "base/Profiler.h"
22 #include "base/Clipboard.h"
23 
24 #include "ColourDatabase.h"
25 #include "PaintAssistant.h"
26 
27 #include "data/model/SparseOneDimensionalModel.h"
28 
29 #include "widgets/ItemEditDialog.h"
31 
32 #include <QPainter>
33 #include <QMouseEvent>
34 #include <QTextStream>
35 #include <QMessageBox>
36 
37 #include <iostream>
38 #include <cmath>
39 
40 //#define DEBUG_TIME_INSTANT_LAYER 1
41 
44  m_editing(false),
45  m_editingPoint(0, tr("New Point")),
46  m_editingCommand(nullptr),
47  m_plotStyle(PlotInstants)
48 {
49 }
50 
52 {
53 }
54 
55 int
57 {
58  auto model = ModelById::get(m_model);
59  if (model) return model->getCompletion();
60  else return 0;
61 }
62 
63 void
65 {
66  auto newModel = ModelById::getAs<SparseOneDimensionalModel>(modelId);
67  if (!modelId.isNone() && !newModel) {
68  throw std::logic_error("Not a SparseOneDimensionalModel");
69  }
70 
71  if (m_model == modelId) return;
72  m_model = modelId;
73 
74  if (newModel) {
76  if (newModel->getRDFTypeURI().endsWith("Segment")) {
78  }
79  }
80 
81  emit modelReplaced();
82 }
83 
84 Layer::PropertyList
86 {
87  PropertyList list = SingleColourLayer::getProperties();
88  list.push_back("Plot Type");
89  return list;
90 }
91 
92 QString
93 TimeInstantLayer::getPropertyLabel(const PropertyName &name) const
94 {
95  if (name == "Plot Type") return tr("Plot Type");
97 }
98 
99 Layer::PropertyType
100 TimeInstantLayer::getPropertyType(const PropertyName &name) const
101 {
102  if (name == "Plot Type") return ValueProperty;
104 }
105 
106 int
108  int *min, int *max, int *deflt) const
109 {
110  int val = 0;
111 
112  if (name == "Plot Type") {
113 
114  if (min) *min = 0;
115  if (max) *max = 1;
116  if (deflt) *deflt = 0;
117 
118  val = int(m_plotStyle);
119 
120  } else {
121 
122  val = SingleColourLayer::getPropertyRangeAndValue(name, min, max, deflt);
123  }
124 
125  return val;
126 }
127 
128 QString
130  int value) const
131 {
132  if (name == "Plot Type") {
133  switch (value) {
134  default:
135  case 0: return tr("Instants");
136  case 1: return tr("Segmentation");
137  }
138  }
139  return SingleColourLayer::getPropertyValueLabel(name, value);
140 }
141 
142 void
143 TimeInstantLayer::setProperty(const PropertyName &name, int value)
144 {
145  if (name == "Plot Type") {
146  setPlotStyle(PlotStyle(value));
147  } else {
148  SingleColourLayer::setProperty(name, value);
149  }
150 }
151 
152 void
154 {
155  if (m_plotStyle == style) return;
156  m_plotStyle = style;
157  emit layerParametersChanged();
158 }
159 
160 bool
162 {
163  auto model = ModelById::getAs<SparseOneDimensionalModel>(m_model);
164  if (model) return model->hasTextLabels();
165  else return false;
166 }
167 
168 bool
170 {
171  QPoint discard;
172  return !v->shouldIlluminateLocalFeatures(this, discard);
173 }
174 
175 EventVector
177 {
178  auto model = ModelById::getAs<SparseOneDimensionalModel>(m_model);
179  if (!model) return {};
180 
181  // Return a set of points that all have the same frame number, the
182  // nearest to the given x coordinate, and that are within a
183  // certain fuzz distance of that x coordinate.
184 
185  sv_frame_t frame = v->getFrameForX(x);
186 
187  EventVector exact = model->getEventsStartingAt(frame);
188  if (!exact.empty()) return exact;
189 
190  // overspill == 1, so one event either side of the given span
191  EventVector neighbouring = model->getEventsWithin
192  (frame, model->getResolution(), 1);
193 
194  double fuzz = v->scaleSize(2);
195  sv_frame_t suitable = 0;
196  bool have = false;
197 
198  for (Event e: neighbouring) {
199  sv_frame_t f = e.getFrame();
200  if (f < v->getStartFrame() || f > v->getEndFrame()) {
201  continue;
202  }
203  int px = v->getXForFrame(f);
204  if ((px > x && px - x > fuzz) || (px < x && x - px > fuzz + 3)) {
205  continue;
206  }
207  if (!have) {
208  suitable = f;
209  have = true;
210  } else if (llabs(frame - f) < llabs(suitable - f)) {
211  suitable = f;
212  }
213  }
214 
215  if (have) {
216  return model->getEventsStartingAt(suitable);
217  } else {
218  return {};
219  }
220 }
221 
222 QString
223 TimeInstantLayer::getLabelPreceding(sv_frame_t frame) const
224 {
225  auto model = ModelById::getAs<SparseOneDimensionalModel>(m_model);
226  if (!model || !model->hasTextLabels()) return "";
227 
228  Event e;
229  if (model->getNearestEventMatching
230  (frame,
231  [](Event e) { return e.hasLabel() && e.getLabel() != ""; },
232  EventSeries::Backward,
233  e)) {
234  return e.getLabel();
235  }
236 
237  return "";
238 }
239 
240 QString
242 {
243  int x = pos.x();
244 
245  auto model = ModelById::getAs<SparseOneDimensionalModel>(m_model);
246  if (!model || !model->getSampleRate()) return "";
247 
248  EventVector points = getLocalPoints(v, x);
249 
250  if (points.empty()) {
251  if (!model->isReady()) {
252  return tr("In progress");
253  } else {
254  return tr("No local points");
255  }
256  }
257 
258  sv_frame_t useFrame = points.begin()->getFrame();
259 
260  RealTime rt = RealTime::frame2RealTime(useFrame, model->getSampleRate());
261 
262  QString text;
263 
264  if (points.begin()->getLabel() == "") {
265  text = QString(tr("Time:\t%1\nNo label"))
266  .arg(rt.toText(true).c_str());
267  } else {
268  text = QString(tr("Time:\t%1\nLabel:\t%2"))
269  .arg(rt.toText(true).c_str())
270  .arg(points.begin()->getLabel());
271  }
272 
273  pos = QPoint(v->getXForFrame(useFrame), pos.y());
274  return text;
275 }
276 
277 bool
279  int &resolution,
280  SnapType snap, int ycoord) const
281 {
282  auto model = ModelById::getAs<SparseOneDimensionalModel>(m_model);
283  if (!model) {
284  return Layer::snapToFeatureFrame(v, frame, resolution, snap, ycoord);
285  }
286 
287  // SnapLeft / SnapRight: return frame of nearest feature in that
288  // direction no matter how far away
289  //
290  // SnapNeighbouring: return frame of feature that would be used in
291  // an editing operation, i.e. closest feature in either direction
292  // but only if it is "close enough"
293 
294  resolution = model->getResolution();
295 
296  if (snap == SnapNeighbouring) {
297  EventVector points = getLocalPoints(v, v->getXForFrame(frame));
298  if (points.empty()) return false;
299  frame = points.begin()->getFrame();
300  return true;
301  }
302 
303  Event e;
304  if (model->getNearestEventMatching
305  (frame,
306  [](Event) { return true; },
307  snap == SnapLeft ? EventSeries::Backward : EventSeries::Forward,
308  e)) {
309  frame = e.getFrame();
310  return true;
311  }
312 
313  return false;
314 }
315 
316 void
317 TimeInstantLayer::paint(LayerGeometryProvider *v, QPainter &paint, QRect rect) const
318 {
319  auto model = ModelById::getAs<SparseOneDimensionalModel>(m_model);
320  if (!model || !model->isOK()) return;
321 
322 // Profiler profiler("TimeInstantLayer::paint", true);
323 
324  int x0 = rect.left();
325  int x1 = x0 + rect.width();
326 
327  sv_frame_t resolution = model->getResolution();
328 
329  sv_frame_t frame0 = v->getFrameForX(x0) - resolution;
330  sv_frame_t frame1 = v->getFrameForX(x1);
331 
332 #ifdef DEBUG_TIME_INSTANT_LAYER
333  SVCERR << "TimeInstantLayer[" << this << "]::paint: x0 = "
334  << x0 << ", x1 = " << x1 << ", frame0 = " << frame0
335  << ", frame1 = " << frame1 << endl;
336 #endif
337 
338  int overspill = 0;
339  if (m_plotStyle == PlotSegmentation) {
340  // We need to start painting at the prior point, so we can
341  // fill in the visible part of its segmentation area
342  overspill = 1;
343  }
344 
345  EventVector points(model->getEventsWithin(frame0, frame1 - frame0,
346  overspill));
347 
348 #ifdef DEBUG_TIME_INSTANT_LAYER
349  SVCERR << "TimeInstantLayer[" << this << "]::paint: have " << points.size()
350  << " point(s) with overspill = " << overspill << " from model "
351  << getModel() << endl;
352 #endif
353 
354  bool odd = false;
355  if (m_plotStyle == PlotSegmentation && !points.empty()) {
356  int index = model->getRowForFrame(points.begin()->getFrame());
357  odd = ((index % 2) == 1);
358  }
359 
360  paint.setPen(getBaseQColor());
361 
362  QColor brushColour(getBaseQColor());
363  brushColour.setAlpha(100);
364  paint.setBrush(brushColour);
365 
366  QColor oddBrushColour(brushColour);
367  if (m_plotStyle == PlotSegmentation) {
368  if (getBaseQColor() == Qt::black) {
369  oddBrushColour = Qt::gray;
370  } else if (getBaseQColor() == Qt::darkRed) {
371  oddBrushColour = Qt::red;
372  } else if (getBaseQColor() == Qt::darkBlue) {
373  oddBrushColour = Qt::blue;
374  } else if (getBaseQColor() == Qt::darkGreen) {
375  oddBrushColour = Qt::green;
376  } else {
377  oddBrushColour = oddBrushColour.lighter(150);
378  }
379  oddBrushColour.setAlpha(100);
380  }
381 
382  QPoint localPos;
383  sv_frame_t illuminateFrame = -1;
384 
385  if (v->shouldIlluminateLocalFeatures(this, localPos)) {
386  EventVector localPoints = getLocalPoints(v, localPos.x());
387  if (!localPoints.empty()) {
388  illuminateFrame = localPoints.begin()->getFrame();
389  }
390  }
391 
392  int prevX = -1;
393  int textY = v->getTextLabelYCoord(this, paint);
394 
395  for (EventVector::const_iterator i = points.begin();
396  i != points.end(); ++i) {
397 
398  Event p(*i);
399  EventVector::const_iterator j = i;
400  ++j;
401 
402  int x = v->getXForFrame(p.getFrame());
403 
404 #ifdef DEBUG_TIME_INSTANT_LAYER
405  SVCERR << "point frame = " << p.getFrame() << " -> x = " << x << endl;
406 #endif
407 
408  if (x == prevX && m_plotStyle == PlotInstants &&
409  p.getFrame() != illuminateFrame) {
410 #ifdef DEBUG_TIME_INSTANT_LAYER
411  SVCERR << "(skipping)" << endl;
412 #endif
413  continue;
414  }
415 
416  int iw = v->getXForFrame(p.getFrame() + model->getResolution()) - x;
417  if (iw < 2) {
418  if (iw < 1) {
419  iw = 2;
420  if (j != points.end()) {
421  int nx = v->getXForFrame(j->getFrame());
422  if (nx < x + 3) iw = 1;
423  }
424  } else {
425  iw = 2;
426  }
427  }
428 
429  if (p.getFrame() == illuminateFrame) {
430  paint.setPen(getForegroundQColor(v->getView()));
431  } else {
432  paint.setPen(brushColour);
433  }
434 
435 #ifdef DEBUG_TIME_INSTANT_LAYER
436  SVCERR << "m_plotStyle = " << m_plotStyle << ", iw = " << iw << endl;
437 #endif
438 
439  if (m_plotStyle == PlotInstants) {
440  if (iw > 1) {
441  paint.drawRect(x, 0, iw - 1, v->getPaintHeight() - 1);
442  } else {
443  paint.drawLine(x, 0, x, v->getPaintHeight() - 1);
444  }
445  } else {
446 
447  if (odd) paint.setBrush(oddBrushColour);
448  else paint.setBrush(brushColour);
449 
450  int nx;
451 
452  if (j != points.end()) {
453  Event q(*j);
454  nx = v->getXForFrame(q.getFrame());
455  } else {
456  nx = v->getXForFrame(model->getEndFrame());
457  }
458 
459  if (nx >= x) {
460 
461  if (illuminateFrame != p.getFrame() &&
462  (nx < x + 5 || x >= v->getPaintWidth() - 1)) {
463  paint.setPen(Qt::NoPen);
464  }
465 
466  paint.drawRect(x, -1, nx - x, v->getPaintHeight() + 1);
467  }
468 
469  odd = !odd;
470  }
471 
472  paint.setPen(getBaseQColor());
473 
474  if (p.getLabel() != "") {
475 
476  // Qt 5.13 deprecates QFontMetrics::width(), but its suggested
477  // replacement (horizontalAdvance) was only added in Qt 5.11
478  // which is too new for us
479 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
480 
481  // only draw if there's enough room from here to the next point
482 
483  int lw = paint.fontMetrics().width(p.getLabel());
484  bool good = true;
485 
486  if (j != points.end()) {
487  int nx = v->getXForFrame(j->getFrame());
488  if (nx >= x && nx - x - iw - 3 <= lw) good = false;
489  }
490 
491  if (good) {
493  x + iw + 2, textY,
494  p.getLabel(),
496  }
497  }
498 
499  prevX = x;
500  }
501 }
502 
503 void
505 {
506 #ifdef DEBUG_TIME_INSTANT_LAYER
507  cerr << "TimeInstantLayer::drawStart(" << e->x() << ")" << endl;
508 #endif
509 
510  auto model = ModelById::getAs<SparseOneDimensionalModel>(m_model);
511  if (!model) return;
512 
513  sv_frame_t frame = v->getFrameForX(e->x());
514  if (frame < 0) frame = 0;
515  frame = frame / model->getResolution() * model->getResolution();
516 
517  m_editingPoint = Event(frame, tr("New Point"));
518 
520  m_editingCommand = new ChangeEventsCommand(m_model.untyped, tr("Draw Point"));
522 
523  m_editing = true;
524 }
525 
526 void
528 {
529 #ifdef DEBUG_TIME_INSTANT_LAYER
530  cerr << "TimeInstantLayer::drawDrag(" << e->x() << ")" << endl;
531 #endif
532 
533  auto model = ModelById::getAs<SparseOneDimensionalModel>(m_model);
534  if (!model || !m_editing) return;
535 
536  sv_frame_t frame = v->getFrameForX(e->x());
537  if (frame < 0) frame = 0;
538  frame = frame / model->getResolution() * model->getResolution();
540  m_editingPoint = m_editingPoint.withFrame(frame);
542 }
543 
544 void
546 #ifdef DEBUG_TIME_INSTANT_LAYER
547  e
548 #endif
549  )
550 {
551 #ifdef DEBUG_TIME_INSTANT_LAYER
552  cerr << "TimeInstantLayer::drawEnd(" << e->x() << ")" << endl;
553 #endif
554  auto model = ModelById::getAs<SparseOneDimensionalModel>(m_model);
555  if (!model || !m_editing) return;
556  QString newName = tr("Add Point at %1 s")
557  .arg(RealTime::frame2RealTime(m_editingPoint.getFrame(),
558  model->getSampleRate())
559  .toText(false).c_str());
560  m_editingCommand->setName(newName);
562  m_editingCommand = nullptr;
563  m_editing = false;
564 }
565 
566 void
568 {
569  auto model = ModelById::getAs<SparseOneDimensionalModel>(m_model);
570  if (!model) return;
571 
572  EventVector points = getLocalPoints(v, e->x());
573  if (points.empty()) return;
574 
575  m_editingPoint = *points.begin();
576 
577  if (m_editingCommand) {
579  m_editingCommand = nullptr;
580  }
581 
582  m_editing = true;
583 }
584 
585 void
587 {
588 }
589 
590 void
592 {
593  auto model = ModelById::getAs<SparseOneDimensionalModel>(m_model);
594  if (!model || !m_editing) return;
595 
596  m_editing = false;
597 
598  EventVector points = getLocalPoints(v, e->x());
599  if (points.empty()) return;
600  if (points.begin()->getFrame() != m_editingPoint.getFrame()) return;
601 
602  m_editingCommand = new ChangeEventsCommand(m_model.untyped, tr("Erase Point"));
605  m_editingCommand = nullptr;
606  m_editing = false;
607 }
608 
609 void
611 {
612 #ifdef DEBUG_TIME_INSTANT_LAYER
613  cerr << "TimeInstantLayer::editStart(" << e->x() << ")" << endl;
614 #endif
615 
616  auto model = ModelById::getAs<SparseOneDimensionalModel>(m_model);
617  if (!model) return;
618 
619  EventVector points = getLocalPoints(v, e->x());
620  if (points.empty()) return;
621 
622  m_editingPoint = *points.begin();
623 
624  if (m_editingCommand) {
626  m_editingCommand = nullptr;
627  }
628 
629  m_editing = true;
630 }
631 
632 void
634 {
635 #ifdef DEBUG_TIME_INSTANT_LAYER
636  cerr << "TimeInstantLayer::editDrag(" << e->x() << ")" << endl;
637 #endif
638 
639  auto model = ModelById::getAs<SparseOneDimensionalModel>(m_model);
640  if (!model || !m_editing) return;
641 
642  sv_frame_t frame = v->getFrameForX(e->x());
643  if (frame < 0) frame = 0;
644  frame = frame / model->getResolution() * model->getResolution();
645 
646  if (!m_editingCommand) {
647  m_editingCommand = new ChangeEventsCommand(m_model.untyped, tr("Drag Point"));
648  }
649 
651  m_editingPoint = m_editingPoint.withFrame(frame);
653 }
654 
655 void
657 #ifdef DEBUG_TIME_INSTANT_LAYER
658  e
659 #endif
660  )
661 {
662 #ifdef DEBUG_TIME_INSTANT_LAYER
663  cerr << "TimeInstantLayer::editEnd(" << e->x() << ")" << endl;
664 #endif
665  auto model = ModelById::getAs<SparseOneDimensionalModel>(m_model);
666  if (!model || !m_editing) return;
667  if (m_editingCommand) {
668  QString newName = tr("Move Point to %1 s")
669  .arg(RealTime::frame2RealTime(m_editingPoint.getFrame(),
670  model->getSampleRate())
671  .toText(false).c_str());
672  m_editingCommand->setName(newName);
674  }
675  m_editingCommand = nullptr;
676  m_editing = false;
677 }
678 
679 bool
681 {
682  auto model = ModelById::getAs<SparseOneDimensionalModel>(m_model);
683  if (!model) return false;
684 
685  EventVector points = getLocalPoints(v, e->x());
686  if (points.empty()) return false;
687 
688  Event point = *points.begin();
689 
690  ItemEditDialog *dialog = new ItemEditDialog
691  (model->getSampleRate(),
694 
695  dialog->setFrameTime(point.getFrame());
696  dialog->setText(point.getLabel());
697 
698  if (dialog->exec() == QDialog::Accepted) {
699 
700  Event newPoint = point
701  .withFrame(dialog->getFrameTime())
702  .withLabel(dialog->getText());
703 
704  ChangeEventsCommand *command =
705  new ChangeEventsCommand(m_model.untyped, tr("Edit Point"));
706  command->remove(point);
707  command->add(newPoint);
708  finish(command);
709  }
710 
711  delete dialog;
712  return true;
713 }
714 
715 void
716 TimeInstantLayer::moveSelection(Selection s, sv_frame_t newStartFrame)
717 {
718  auto model = ModelById::getAs<SparseOneDimensionalModel>(m_model);
719  if (!model) return;
720 
721  ChangeEventsCommand *command =
722  new ChangeEventsCommand(m_model.untyped, tr("Drag Selection"));
723 
724  EventVector points =
725  model->getEventsWithin(s.getStartFrame(), s.getDuration());
726 
727  for (auto p: points) {
728  Event newPoint = p
729  .withFrame(p.getFrame() + newStartFrame - s.getStartFrame());
730  command->remove(p);
731  command->add(newPoint);
732  }
733 
734  finish(command);
735 }
736 
737 void
738 TimeInstantLayer::resizeSelection(Selection s, Selection newSize)
739 {
740  auto model = ModelById::getAs<SparseOneDimensionalModel>(m_model);
741  if (!model) return;
742 
743  ChangeEventsCommand *command =
744  new ChangeEventsCommand(m_model.untyped, tr("Resize Selection"));
745 
746  EventVector points =
747  model->getEventsWithin(s.getStartFrame(), s.getDuration());
748 
749  double ratio = double(newSize.getDuration()) / double(s.getDuration());
750  double oldStart = double(s.getStartFrame());
751  double newStart = double(newSize.getStartFrame());
752 
753  for (auto p: points) {
754 
755  double newFrame = (double(p.getFrame()) - oldStart) * ratio + newStart;
756 
757  Event newPoint = p
758  .withFrame(lrint(newFrame));
759  command->remove(p);
760  command->add(newPoint);
761  }
762 
763  finish(command);
764 }
765 
766 void
768 {
769  auto model = ModelById::getAs<SparseOneDimensionalModel>(m_model);
770  if (!model) return;
771 
772  ChangeEventsCommand *command =
773  new ChangeEventsCommand(m_model.untyped, tr("Delete Selection"));
774 
775  EventVector points =
776  model->getEventsWithin(s.getStartFrame(), s.getDuration());
777 
778  for (auto p: points) {
779  command->remove(p);
780  }
781 
782  finish(command);
783 }
784 
785 void
786 TimeInstantLayer::copy(LayerGeometryProvider *v, Selection s, Clipboard &to)
787 {
788  auto model = ModelById::getAs<SparseOneDimensionalModel>(m_model);
789  if (!model) return;
790 
791  EventVector points =
792  model->getEventsWithin(s.getStartFrame(), s.getDuration());
793 
794  for (auto p: points) {
795  to.addPoint(p.withReferenceFrame(alignToReference(v, p.getFrame())));
796  }
797 }
798 
799 bool
801  sv_frame_t frameOffset, bool)
802 {
803  auto model = ModelById::getAs<SparseOneDimensionalModel>(m_model);
804  if (!model) return false;
805 
806  EventVector points = from.getPoints();
807 
808  bool realign = false;
809 
810  if (clipboardHasDifferentAlignment(v, from)) {
811 
812  QMessageBox::StandardButton button =
813  QMessageBox::question(v->getView(), tr("Re-align pasted instants?"),
814  tr("The instants 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?"),
815  QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel,
816  QMessageBox::Yes);
817 
818  if (button == QMessageBox::Cancel) {
819  return false;
820  }
821 
822  if (button == QMessageBox::Yes) {
823  realign = true;
824  }
825  }
826 
827  ChangeEventsCommand *command =
828  new ChangeEventsCommand(m_model.untyped, tr("Paste"));
829 
830  for (EventVector::const_iterator i = points.begin();
831  i != points.end(); ++i) {
832 
833  sv_frame_t frame = 0;
834 
835  if (!realign) {
836 
837  frame = i->getFrame();
838 
839  } else {
840 
841  if (i->hasReferenceFrame()) {
842  frame = i->getReferenceFrame();
843  frame = alignFromReference(v, frame);
844  } else {
845  frame = i->getFrame();
846  }
847  }
848 
849  if (frameOffset > 0) frame += frameOffset;
850  else if (frameOffset < 0) {
851  if (frame > -frameOffset) frame += frameOffset;
852  else frame = 0;
853  }
854 
855  Event newPoint = i->withFrame(frame);
856 
857  if (!i->hasLabel() && i->hasValue()) {
858  newPoint = newPoint.withLabel(QString("%1").arg(i->getValue()));
859  }
860 
861  command->add(newPoint);
862  }
863 
864  finish(command);
865  return true;
866 }
867 
868 int
869 TimeInstantLayer::getDefaultColourHint(bool darkbg, bool &impose)
870 {
871  impose = false;
873  (QString(darkbg ? "Bright Purple" : "Purple"));
874 }
875 
876 void
877 TimeInstantLayer::toXml(QTextStream &stream,
878  QString indent, QString extraAttributes) const
879 {
880  SingleColourLayer::toXml(stream, indent,
881  extraAttributes +
882  QString(" plotStyle=\"%1\"")
883  .arg(m_plotStyle));
884 }
885 
886 void
887 TimeInstantLayer::setProperties(const QXmlAttributes &attributes)
888 {
890 
891  bool ok;
892  PlotStyle style = (PlotStyle)
893  attributes.value("plotStyle").toInt(&ok);
894  if (ok) setPlotStyle(style);
895 }
896 
QString getPropertyValueLabel(const PropertyName &, int value) const override
virtual bool snapToFeatureFrame(LayerGeometryProvider *, sv_frame_t &, int &resolution, SnapType, int) const
Adjust the given frame to snap to the nearest feature, if possible.
Definition: Layer.h:226
QString getLabelPreceding(sv_frame_t) const override
void drawStart(LayerGeometryProvider *v, QMouseEvent *) override
PropertyList getProperties() const override
virtual bool shouldIlluminateLocalFeatures(const Layer *, QPoint &) const =0
ModelId getModel() const override
Return the ID of the model represented in this layer.
void editStart(LayerGeometryProvider *v, QMouseEvent *) override
void modelReplaced()
QString getPropertyLabel(const PropertyName &) const override
void drawEnd(LayerGeometryProvider *v, QMouseEvent *) override
bool needsTextLabelHeight() const override
True if this layer will need to place text labels when it is painted.
virtual QColor getForegroundQColor(LayerGeometryProvider *v) const
bool paste(LayerGeometryProvider *v, const Clipboard &from, sv_frame_t frameOffset, bool interactive) override
Paste from the given clipboard onto the layer at the given frame offset.
void setFrameTime(sv_frame_t frame)
void eraseEnd(LayerGeometryProvider *v, QMouseEvent *) override
sv_frame_t getFrameTime() const
virtual sv_frame_t getFrameForX(int x) const =0
Return the closest frame to the given pixel x-coordinate.
void eraseDrag(LayerGeometryProvider *v, QMouseEvent *) override
void resizeSelection(Selection s, Selection newSize) override
void setProperty(const PropertyName &, int value) override
PropertyType getPropertyType(const PropertyName &) const override
void finish(ChangeEventsCommand *command)
void editDrag(LayerGeometryProvider *v, QMouseEvent *) override
virtual double scaleSize(double size) const =0
virtual QColor getBaseQColor() const
void toXml(QTextStream &stream, QString indent="", QString extraAttributes="") const override
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.
int getCompletion(LayerGeometryProvider *) const override
Return the proportion of background work complete in drawing this view, as a percentage – in most ca...
void copy(LayerGeometryProvider *v, Selection s, Clipboard &to) override
int getColourIndex(QString name) const
Return the index of the colour with the given name, if found in the database.
virtual sv_frame_t alignFromReference(LayerGeometryProvider *v, sv_frame_t frame) const
Definition: Layer.cpp:198
void setPlotStyle(PlotStyle style)
void layerParametersChanged()
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...
PropertyList getProperties() const override
bool clipboardHasDifferentAlignment(LayerGeometryProvider *v, const Clipboard &clip) const
Definition: Layer.cpp:209
ChangeEventsCommand * m_editingCommand
int getPropertyRangeAndValue(const PropertyName &, int *min, int *max, int *deflt) const override
SnapType
Definition: Layer.h:195
virtual sv_frame_t alignToReference(LayerGeometryProvider *v, sv_frame_t frame) const
Definition: Layer.cpp:187
EventVector getLocalPoints(LayerGeometryProvider *v, int) const
void deleteSelection(Selection s) override
void connectSignals(ModelId)
Definition: Layer.cpp:49
virtual int getPaintHeight() const
void drawDrag(LayerGeometryProvider *v, QMouseEvent *) override
PropertyType getPropertyType(const PropertyName &) const override
QString getText() const
void moveSelection(Selection s, sv_frame_t newStartFrame) override
void setProperties(const QXmlAttributes &attributes) override
Set the particular properties of a layer (those specific to the subclass) from a set of XML attribute...
void 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.
virtual ~TimeInstantLayer()
int getDefaultColourHint(bool dark, bool &impose) override
int getPropertyRangeAndValue(const PropertyName &, int *min, int *max, int *deflt) const override
void eraseStart(LayerGeometryProvider *v, QMouseEvent *) override
void setProperties(const QXmlAttributes &attributes) override
Set the particular properties of a layer (those specific to the subclass) from a set of XML attribute...
QString getPropertyValueLabel(const PropertyName &, int value) const override
bool snapToFeatureFrame(LayerGeometryProvider *v, sv_frame_t &frame, int &resolution, SnapType snap, int ycoord) const override
Adjust the given frame to snap to the nearest feature, if possible.
virtual int getTextLabelYCoord(const Layer *layer, QPainter &) const =0
Return a y-coordinate at which text labels for individual items in a layer may be drawn...
static void drawVisibleText(const LayerGeometryProvider *, QPainter &p, int x, int y, QString text, TextStyle style)
void setModel(ModelId model)
virtual sv_frame_t getEndFrame() const =0
Retrieve the last visible sample frame on the widget.
QString getFeatureDescription(LayerGeometryProvider *v, QPoint &) const override
void setProperty(const PropertyName &, int value) override
virtual int getXForFrame(sv_frame_t frame) const =0
Return the pixel x-coordinate corresponding to a given sample frame (which may be negative)...
virtual int getPaintWidth() const
bool editOpen(LayerGeometryProvider *, QMouseEvent *) override
Open an editor on the item under the mouse (e.g.
QString getPropertyLabel(const PropertyName &) const override
void toXml(QTextStream &stream, QString indent="", QString extraAttributes="") const override
static ColourDatabase * getInstance()
virtual View * getView()=0
void editEnd(LayerGeometryProvider *v, QMouseEvent *) override