17 #include "data/model/Model.h" 18 #include "base/RealTime.h" 19 #include "base/Profiler.h" 20 #include "base/LogRange.h" 30 #include "data/model/BoxModel.h" 36 #include <QPainterPath> 37 #include <QMouseEvent> 38 #include <QTextStream> 39 #include <QMessageBox> 51 m_originalPoint(0, 0.0, 0, tr(
"New Box")),
52 m_editingPoint(0, 0.0, 0, tr(
"New Box")),
53 m_editingCommand(nullptr),
54 m_verticalScale(AutoAlignScale)
62 auto model = ModelById::get(
m_model);
63 if (model)
return model->getCompletion();
70 auto oldModel = ModelById::getAs<BoxModel>(
m_model);
71 auto newModel = ModelById::getAs<BoxModel>(modelId);
73 if (!modelId.isNone() && !newModel) {
74 throw std::logic_error(
"Not a BoxModel");
91 list.push_back(
"Vertical Scale");
92 list.push_back(
"Scale Units");
99 if (name ==
"Vertical Scale")
return tr(
"Vertical Scale");
100 if (name ==
"Scale Units")
return tr(
"Scale Units");
107 if (name ==
"Vertical Scale")
return ValueProperty;
108 if (name ==
"Scale Units")
return UnitsProperty;
115 if (name ==
"Vertical Scale" || name ==
"Scale Units") {
123 int *min,
int *max,
int *deflt)
const 127 if (name ==
"Vertical Scale") {
135 }
else if (name ==
"Scale Units") {
137 if (deflt) *deflt = 0;
138 auto model = ModelById::getAs<BoxModel>(
m_model);
140 val = UnitDatabase::getInstance()->getUnitId
141 (model->getScaleUnits());
155 if (name ==
"Vertical Scale") {
158 case 0:
return tr(
"Auto-Align");
159 case 1:
return tr(
"Linear");
160 case 2:
return tr(
"Log");
169 if (name ==
"Vertical Scale") {
171 }
else if (name ==
"Scale Units") {
172 auto model = ModelById::getAs<BoxModel>(
m_model);
175 (UnitDatabase::getInstance()->getUnitById(value));
200 bool &logarithmic, QString &unit)
const 202 auto model = ModelById::getAs<BoxModel>(
m_model);
203 if (!model)
return false;
204 min = model->getValueMinimum();
205 max = model->getValueMaximum();
216 auto model = ModelById::getAs<BoxModel>(
m_model);
219 min = model->getValueMinimum();
220 max = model->getValueMaximum();
228 auto model = ModelById::getAs<BoxModel>(
m_model);
229 if (!model)
return false;
231 SVDEBUG <<
"BoxLayer[" <<
this <<
"]::adoptExtents: min " << min
232 <<
", max " << max <<
", unit " << unit << endl;
234 if (model->getScaleUnits() ==
"") {
235 model->setScaleUnits(unit);
246 auto model = ModelById::getAs<BoxModel>(
m_model);
247 if (!model || !model->isReady())
return false;
251 EventVector onPoints = model->getEventsCovering(frame);
252 if (onPoints.empty())
return false;
254 Event bestContaining;
255 for (
const auto &p: onPoints) {
258 SVCERR <<
"inPoints: rejecting " << p.toXmlString() << endl;
261 SVCERR <<
"inPoints: looking at " << p.toXmlString() << endl;
262 if (bestContaining == Event()) {
267 if (r.first < br.first && r.second > br.second) {
270 if (r.first > br.first && r.second < br.second) {
274 if (p.getFrame() > bestContaining.getFrame() &&
275 p.getFrame() + p.getDuration() <
276 bestContaining.getFrame() + bestContaining.getDuration()) {
282 if (bestContaining != Event()) {
283 point = bestContaining;
285 int nearestDistance = -1;
286 for (
const auto &p: onPoints) {
288 int distance = std::min
291 if (distance < 0) distance = -distance;
292 if (nearestDistance == -1 || distance < nearestDistance) {
293 nearestDistance = distance;
305 auto model = ModelById::getAs<BoxModel>(
m_model);
306 if (!model)
return "";
307 EventVector points = model->getEventsStartingWithin
308 (model->getStartFrame(), frame - model->getStartFrame());
309 if (!points.empty()) {
310 for (
auto i = points.rbegin(); i != points.rend(); ++i) {
311 if (i->getLabel() != QString()) {
312 return i->getLabel();
323 auto model = ModelById::getAs<BoxModel>(
m_model);
324 if (!model || !model->getSampleRate())
return "";
329 if (!model->isReady()) {
330 return tr(
"In progress");
332 return tr(
"No local points");
336 RealTime rt = RealTime::frame2RealTime(box.getFrame(),
337 model->getSampleRate());
338 RealTime rd = RealTime::frame2RealTime(box.getDuration(),
339 model->getSampleRate());
344 rangeText = tr(
"%1 %2 - %3 %4")
350 if (box.getLabel() ==
"") {
351 text = QString(tr(
"Time:\t%1\nDuration:\t%2\nValue:\t%3\nNo label"))
352 .arg(rt.toText(
true).c_str())
353 .arg(rd.toText(
true).c_str())
356 text = QString(tr(
"Time:\t%1\nDuration:\t%2\nValue:\t%3\nLabel:\t%4"))
357 .arg(rt.toText(
true).c_str())
358 .arg(rd.toText(
true).c_str())
360 .arg(box.getLabel());
375 auto model = ModelById::getAs<BoxModel>(
m_model);
387 resolution = model->getResolution();
397 frame = containing.getFrame();
401 frame = containing.getFrame() + containing.getDuration();
416 if (model->getNearestEventMatching
417 (frame, [](Event) { return true; }, EventSeries::Backward, e)) {
419 if (e.getFrame() + e.getDuration() < frame) {
420 frame = e.getFrame() + e.getDuration();
422 frame = e.getFrame();
429 if (model->getNearestEventMatching
430 (frame, [](Event) { return true; }, EventSeries::Forward, e)) {
432 frame = e.getFrame();
443 auto model = ModelById::getAs<BoxModel>(
m_model);
444 if (model)
return model->getScaleUnits();
450 double &min,
double &max,
457 auto model = ModelById::getAs<BoxModel>(
m_model);
467 min = model->getValueMinimum();
468 max = model->getValueMaximum();
474 LogRange::mapRange(min, max);
482 min = model->getValueMinimum();
483 max = model->getValueMaximum();
486 LogRange::mapRange(min, max);
491 if (max == min) max = min + 1.0;
497 double min = 0.0, max = 0.0;
498 bool logarithmic =
false;
507 val = LogRange::map(val);
510 return int(h - ((val - min) * h) / (max - min));
516 double min = 0.0, max = 0.0;
517 bool logarithmic =
false;
522 double val = min + (double(h - y) * double(max - min)) / h;
525 val = pow(10.0, val);
535 auto model = ModelById::getAs<BoxModel>(
m_model);
536 if (!model || !model->isOK())
return;
538 sv_samplerate_t sampleRate = model->getSampleRate();
539 if (!sampleRate)
return;
543 int x0 = rect.left() - 40;
544 int x1 = x0 + rect.width() + 80;
549 EventVector points(model->getEventsSpanning(wholeFrame0,
550 wholeFrame1 - wholeFrame0));
551 if (points.empty())
return;
558 double min = model->getValueMinimum();
559 double max = model->getValueMaximum();
560 if (max == min) max = min + 1.0;
563 Event illuminatePoint(0);
564 bool shouldIlluminate =
false;
567 shouldIlluminate =
getLocalPoint(v, localPos.x(), localPos.y(),
572 paint.setRenderHint(QPainter::Antialiasing,
false);
574 QFontMetrics fm = paint.fontMetrics();
576 for (EventVector::const_iterator i = points.begin();
577 i != points.end(); ++i) {
583 int w = v->
getXForFrame(p.getFrame() + p.getDuration()) - x;
589 EventVector::const_iterator j = i;
592 if (j != points.end()) {
595 if (nx < ex) ex = nx;
601 paint.setBrush(Qt::NoBrush);
603 if ((shouldIlluminate && illuminatePoint == p) ||
611 #pragma GCC diagnostic ignored "-Wdeprecated-declarations" 613 if (abs(h) > 2 * fm.height()) {
615 QString y0label = QString(
"%1 %2")
619 QString y1label = QString(
"%1 %2")
625 x - fm.width(y0label) - gap,
631 x - fm.width(y1label) - gap,
637 QString ylabel = QString(
"%1 %2 - %3 %4")
645 x - fm.width(ylabel) - gap,
650 QString t0label = RealTime::frame2RealTime
651 (p.getFrame(), model->getSampleRate()).toText(
true).c_str();
653 QString t1label = RealTime::frame2RealTime
654 (p.getFrame() + p.getDuration(), model->getSampleRate())
655 .toText(
true).c_str();
658 (v, paint, x, y + fm.ascent() + gap,
661 if (w > fm.width(t0label) + fm.width(t1label) + gap * 3) {
665 x + w - fm.width(t1label),
666 y + fm.ascent() + gap,
673 x + w - fm.width(t1label),
674 y + fm.ascent() + fm.height() + gap,
679 paint.drawRect(x, y, w, h);
682 for (EventVector::const_iterator i = points.begin();
683 i != points.end(); ++i) {
687 QString label = p.getLabel();
688 if (label ==
"")
continue;
690 if (shouldIlluminate && illuminatePoint == p) {
695 int w = v->
getXForFrame(p.getFrame() + p.getDuration()) - x;
698 int labelWidth = fm.width(label);
702 if (x + w < x0 || x - labelWidth - gap > x1) {
708 labelX = x - labelWidth - gap;
709 labelY = y - fm.descent();
720 bool, QPainter &
paint)
const 722 auto model = ModelById::getAs<BoxModel>(
m_model);
736 bool, QPainter &
paint, QRect)
const 738 auto model = ModelById::getAs<BoxModel>(
m_model);
739 if (!model || model->isEmpty())
return;
758 5 + paint.fontMetrics().ascent(),
768 auto model = ModelById::getAs<BoxModel>(
m_model);
772 if (frame < 0) frame = 0;
773 frame = frame / model->getResolution() * model->getResolution();
791 auto model = ModelById::getAs<BoxModel>(
m_model);
795 if (dragFrame < 0) dragFrame = 0;
796 dragFrame = dragFrame / model->getResolution() * model->getResolution();
799 sv_frame_t eventDuration = dragFrame - eventFrame;
800 if (eventDuration < 0) {
801 eventFrame = eventFrame + eventDuration;
802 eventDuration = -eventDuration;
803 }
else if (eventDuration == 0) {
804 eventDuration = model->getResolution();
810 double eventFreqDiff = dragValue - eventValue;
811 if (eventFreqDiff < 0) {
812 eventValue = eventValue + eventFreqDiff;
813 eventFreqDiff = -eventFreqDiff;
818 .withFrame(eventFrame)
819 .withDuration(eventDuration)
820 .withValue(
float(eventValue))
821 .withLevel(
float(eventFreqDiff));
828 auto model = ModelById::getAs<BoxModel>(
m_model);
838 auto model = ModelById::getAs<BoxModel>(
m_model);
859 auto model = ModelById::getAs<BoxModel>(
m_model);
870 (
m_model.untyped, tr(
"Erase Box"));
882 auto model = ModelById::getAs<BoxModel>(
m_model);
907 auto model = ModelById::getAs<BoxModel>(
m_model);
916 if (frame < 0) frame = 0;
917 frame = frame / model->getResolution() * model->getResolution();
930 .withValue(
float(value));
937 auto model = ModelById::getAs<BoxModel>(
m_model);
946 newName = tr(
"Edit Box");
948 newName = tr(
"Relocate Box");
951 newName = tr(
"Change Point Value");
965 auto model = ModelById::getAs<BoxModel>(
m_model);
966 if (!model)
return false;
972 labelOptions.
valueLabel = tr(
"Minimum Value");
978 (model->getSampleRate(),
987 dialog->
setValue(region.getValue());
988 dialog->
setLevel(region.getLevel());
990 dialog->
setText(region.getLabel());
992 if (dialog->exec() == QDialog::Accepted) {
994 Event newBox = region
1001 ChangeEventsCommand *command =
new ChangeEventsCommand
1002 (
m_model.untyped, tr(
"Edit Box"));
1003 command->remove(region);
1004 command->add(newBox);
1015 auto model = ModelById::getAs<BoxModel>(
m_model);
1018 ChangeEventsCommand *command =
1019 new ChangeEventsCommand(
m_model.untyped, tr(
"Drag Selection"));
1021 EventVector points =
1022 model->getEventsStartingWithin(s.getStartFrame(), s.getDuration());
1024 for (EventVector::iterator i = points.begin();
1025 i != points.end(); ++i) {
1027 Event newPoint = (*i)
1028 .withFrame(i->getFrame() + newStartFrame - s.getStartFrame());
1029 command->remove(*i);
1030 command->add(newPoint);
1039 auto model = ModelById::getAs<BoxModel>(
m_model);
1040 if (!model || !s.getDuration())
return;
1042 ChangeEventsCommand *command =
1043 new ChangeEventsCommand(
m_model.untyped, tr(
"Resize Selection"));
1045 EventVector points =
1046 model->getEventsStartingWithin(s.getStartFrame(), s.getDuration());
1048 double ratio = double(newSize.getDuration()) /
double(s.getDuration());
1049 double oldStart = double(s.getStartFrame());
1050 double newStart = double(newSize.getStartFrame());
1052 for (Event p: points) {
1054 double newFrame = (double(p.getFrame()) - oldStart) * ratio + newStart;
1055 double newDuration = double(p.getDuration()) * ratio;
1058 .withFrame(lrint(newFrame))
1059 .withDuration(lrint(newDuration));
1061 command->add(newPoint);
1070 auto model = ModelById::getAs<BoxModel>(
m_model);
1073 ChangeEventsCommand *command =
1074 new ChangeEventsCommand(
m_model.untyped, tr(
"Delete Selected Points"));
1076 EventVector points =
1077 model->getEventsStartingWithin(s.getStartFrame(), s.getDuration());
1079 for (EventVector::iterator i = points.begin();
1080 i != points.end(); ++i) {
1082 if (s.contains(i->getFrame())) {
1083 command->remove(*i);
1093 auto model = ModelById::getAs<BoxModel>(
m_model);
1096 EventVector points =
1097 model->getEventsStartingWithin(s.getStartFrame(), s.getDuration());
1099 for (Event p: points) {
1108 auto model = ModelById::getAs<BoxModel>(
m_model);
1109 if (!model)
return false;
1111 const EventVector &points = from.getPoints();
1113 bool realign =
false;
1117 QMessageBox::StandardButton button =
1118 QMessageBox::question(v->
getView(), tr(
"Re-align pasted items?"),
1119 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?"),
1120 QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel,
1123 if (button == QMessageBox::Cancel) {
1127 if (button == QMessageBox::Yes) {
1132 ChangeEventsCommand *command =
1133 new ChangeEventsCommand(
m_model.untyped, tr(
"Paste"));
1135 for (EventVector::const_iterator i = points.begin();
1136 i != points.end(); ++i) {
1138 sv_frame_t frame = 0;
1142 frame = i->getFrame();
1146 if (i->hasReferenceFrame()) {
1147 frame = i->getReferenceFrame();
1150 frame = i->getFrame();
1154 Event p = i->withFrame(frame);
1157 if (!p.hasValue()) {
1158 newPoint = newPoint.withValue((model->getValueMinimum() +
1159 model->getValueMaximum()) / 2);
1161 if (!p.hasDuration()) {
1162 sv_frame_t nextFrame = frame;
1163 EventVector::const_iterator j = i;
1164 for (; j != points.end(); ++j) {
1167 if (j != points.end()) {
1168 nextFrame = j->getFrame();
1170 if (nextFrame == frame) {
1171 newPoint = newPoint.withDuration(model->getResolution());
1173 newPoint = newPoint.withDuration(nextFrame - frame);
1177 command->add(newPoint);
1186 QString indent, QString extraAttributes)
const 1202 attributes.value(
"verticalScale").toInt(&ok);
virtual int scalePixelSize(int size) const =0
QString getPropertyGroupName(const PropertyName &) 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.
bool getLocalPoint(LayerGeometryProvider *v, int x, int y, Event &) const
void copy(LayerGeometryProvider *v, Selection s, Clipboard &to) override
void setFrameDuration(sv_frame_t frame)
PropertyType getPropertyType(const PropertyName &) const override
PropertyList getProperties() const override
void paintVerticalScale(LayerGeometryProvider *v, bool, QPainter &paint, QRect rect) const override
PropertyList getProperties() const override
virtual bool shouldIlluminateLocalFeatures(const Layer *, QPoint &) const =0
QString getPropertyLabel(const PropertyName &) const override
bool editOpen(LayerGeometryProvider *v, QMouseEvent *) override
Open an editor on the item under the mouse (e.g.
double getValueForY(LayerGeometryProvider *v, int y) const override
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...
void finish(ChangeEventsCommand *command)
void setFrameTime(sv_frame_t frame)
void toXml(QTextStream &stream, QString indent="", QString extraAttributes="") const override
void paintVertical(LayerGeometryProvider *v, const VerticalScaleLayer *layer, QPainter &paint, int x0, double minlog, double maxlog)
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 setProperty(const PropertyName &, int value) override
void editDrag(LayerGeometryProvider *v, QMouseEvent *) override
bool adoptExtents(double min, double max, QString unit) override
Consider using the given value extents and units for this layer.
void drawEnd(LayerGeometryProvider *v, QMouseEvent *) override
int getWidth(LayerGeometryProvider *v, QPainter &paint)
virtual QColor getBaseQColor() const
void toXml(QTextStream &stream, QString indent="", QString extraAttributes="") const override
int getWidth(LayerGeometryProvider *v, QPainter &paint)
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.
VerticalScale m_verticalScale
int getYForValue(LayerGeometryProvider *v, double value) const override
VerticalScaleLayer methods.
QString getPropertyValueLabel(const PropertyName &, int value) const override
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...
virtual sv_frame_t alignFromReference(LayerGeometryProvider *v, sv_frame_t frame) const
QString getFeatureDescription(LayerGeometryProvider *v, QPoint &) const override
QString getPropertyGroupName(const PropertyName &) const override
void setVerticalScale(VerticalScale scale)
std::pair< float, float > getRange(const Event &e) const
void layerParametersChanged()
void eraseDrag(LayerGeometryProvider *v, QMouseEvent *) override
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.
bool clipboardHasDifferentAlignment(LayerGeometryProvider *v, const Clipboard &clip) const
int getPropertyRangeAndValue(const PropertyName &, int *min, int *max, int *deflt) const override
bool getDisplayExtents(double &min, double &max) const override
Return the minimum and maximum values within the visible area for the y axis of this layer...
void drawDrag(LayerGeometryProvider *v, QMouseEvent *) override
virtual sv_frame_t alignToReference(LayerGeometryProvider *v, sv_frame_t frame) const
void paintVertical(LayerGeometryProvider *v, const VerticalScaleLayer *layer, QPainter &paint, int x0, double minf, double maxf)
void connectSignals(ModelId)
virtual int getPaintHeight() const
void editEnd(LayerGeometryProvider *v, QMouseEvent *) override
PropertyType getPropertyType(const PropertyName &) const override
QString getScaleUnits() const override
void getScaleExtents(LayerGeometryProvider *, double &min, double &max, bool &log) const
int getPropertyRangeAndValue(const PropertyName &, int *min, int *max, int *deflt) const override
void setProperties(const QXmlAttributes &attributes) override
Set the particular properties of a layer (those specific to the subclass) from a set of XML attribute...
int getVerticalScaleWidth(LayerGeometryProvider *v, bool, QPainter &) const override
ChangeEventsCommand * m_editingCommand
void eraseStart(LayerGeometryProvider *v, QMouseEvent *) override
void paint(LayerGeometryProvider *v, QPainter &paint, QRect rect) const override
Paint the given rectangle of this layer onto the given view using the given painter, superimposing it on top of any existing material in that view.
QString getPropertyValueLabel(const PropertyName &, int value) const override
void setProperties(const QXmlAttributes &attributes) override
Set the particular properties of a layer (those specific to the subclass) from a set of XML attribute...
void deleteSelection(Selection s) override
void resizeSelection(Selection s, Selection newSize) override
static void drawVisibleText(const LayerGeometryProvider *, QPainter &p, int x, int y, QString text, TextStyle style)
void setModel(ModelId model)
void drawStart(LayerGeometryProvider *v, QMouseEvent *) override
virtual bool getVisibleExtentsForUnit(QString unit, double &min, double &max, bool &log) const =0
Return the visible vertical extents for the given unit, if any.
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...
void editStart(LayerGeometryProvider *v, QMouseEvent *) override
void setProperty(const PropertyName &, int value) override
QString getLabelPreceding(sv_frame_t) const override
void setLevel(float level)
virtual int getXForFrame(sv_frame_t frame) const =0
Return the pixel x-coordinate corresponding to a given sample frame (which may be negative)...
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 getPaintWidth() const
sv_frame_t getFrameDuration() const
QString getPropertyLabel(const PropertyName &) const override
virtual View * getView()=0
void eraseEnd(LayerGeometryProvider *v, QMouseEvent *) override
void setValue(float value)
int getCompletion(LayerGeometryProvider *) const override
Return the proportion of background work complete in drawing this view, as a percentage – in most ca...
void moveSelection(Selection s, sv_frame_t newStartFrame) override