annotate layer/TimeValueLayer.cpp @ 1447:8afea53332f3 single-point

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