annotate layer/TimeValueLayer.cpp @ 1431:af824022bffd single-point

Begin fixing the various snap operations. Also remove SnapNearest, which is never used and seems to consume more lines of code than the rest!
author Chris Cannam
date Wed, 20 Mar 2019 14:59:34 +0000
parents 31499c3520ee
children 5b9692768beb
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 // SnapNearest: return frame of nearest feature in either
Chris@1431 667 // direction no matter how far away - I'm fairly sure this is
Chris@1431 668 // never actually used
Chris@1431 669 //
Chris@1431 670 // SnapNeighbouring: return frame of feature that would be used in
Chris@1431 671 // an editing operation, i.e. closest feature in either direction
Chris@1431 672 // but only if it is "close enough"
Chris@1431 673
Chris@13 674 resolution = m_model->getResolution();
Chris@13 675
Chris@28 676 if (snap == SnapNeighbouring) {
Chris@1431 677 EventVector points = getLocalPoints(v, v->getXForFrame(frame));
Chris@1266 678 if (points.empty()) return false;
Chris@1429 679 frame = points.begin()->getFrame();
Chris@1266 680 return true;
Chris@13 681 }
Chris@13 682
Chris@1431 683 Event e;
Chris@1431 684 if (m_model->getNearestEventMatching
Chris@1431 685 (frame,
Chris@1431 686 [](Event) { return true; },
Chris@1431 687 snap == SnapLeft ? EventSeries::Backward : EventSeries::Forward,
Chris@1431 688 e)) {
Chris@1431 689 frame = e.getFrame();
Chris@1431 690 return true;
Chris@1431 691 }
Chris@1431 692
Chris@1431 693 return false;
Chris@13 694 }
Chris@13 695
Chris@517 696 bool
Chris@1431 697 TimeValueLayer::snapToSimilarFeature(LayerGeometryProvider *v,
Chris@1431 698 sv_frame_t &frame,
Chris@805 699 int &resolution,
Chris@517 700 SnapType snap) const
Chris@517 701 {
Chris@517 702 if (!m_model) {
Chris@1266 703 return Layer::snapToSimilarFeature(v, frame, resolution, snap);
Chris@517 704 }
Chris@517 705
Chris@517 706 resolution = m_model->getResolution();
Chris@517 707
Chris@1431 708 Event ref;
Chris@1431 709 Event e;
Chris@1431 710 float matchvalue;
Chris@1431 711 bool found;
Chris@517 712
Chris@1431 713 found = m_model->getNearestEventMatching
Chris@1431 714 (frame, [](Event) { return true; }, EventSeries::Backward, ref);
Chris@1429 715
Chris@1431 716 if (!found) {
Chris@1431 717 return false;
Chris@517 718 }
Chris@517 719
Chris@1431 720 matchvalue = ref.getValue();
Chris@1431 721
Chris@1431 722 found = m_model->getNearestEventMatching
Chris@1431 723 (frame,
Chris@1431 724 [matchvalue](Event e) {
Chris@1431 725 double epsilon = 0.0001;
Chris@1431 726 return fabs(e.getValue() - matchvalue) < epsilon;
Chris@1431 727 },
Chris@1431 728 snap == SnapLeft ? EventSeries::Backward : EventSeries::Forward,
Chris@1431 729 e);
Chris@517 730
Chris@1431 731 if (!found) {
Chris@1431 732 return false;
Chris@517 733 }
Chris@517 734
Chris@1431 735 frame = e.getFrame();
Chris@1431 736 return true;
Chris@517 737 }
Chris@517 738
Chris@101 739 void
Chris@918 740 TimeValueLayer::getScaleExtents(LayerGeometryProvider *v, double &min, double &max, bool &log) const
Chris@101 741 {
Chris@101 742 min = 0.0;
Chris@101 743 max = 0.0;
Chris@101 744 log = false;
Chris@101 745
Chris@296 746 if (shouldAutoAlign()) {
Chris@101 747
Chris@698 748 if (!v->getValueExtents(getScaleUnits(), min, max, log)) {
Chris@101 749 min = m_model->getValueMinimum();
Chris@101 750 max = m_model->getValueMaximum();
Chris@668 751 } else if (log) {
Chris@668 752 LogRange::mapRange(min, max);
Chris@101 753 }
Chris@101 754
Chris@101 755 } else if (m_verticalScale == PlusMinusOneScale) {
Chris@101 756
Chris@101 757 min = -1.0;
Chris@101 758 max = 1.0;
Chris@101 759
Chris@101 760 } else {
Chris@101 761
Chris@437 762 getDisplayExtents(min, max);
Chris@632 763
Chris@101 764 if (m_verticalScale == LogScale) {
Chris@197 765 LogRange::mapRange(min, max);
Chris@101 766 log = true;
Chris@101 767 }
Chris@101 768 }
Chris@101 769
Chris@629 770 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@682 771 cerr << "TimeValueLayer::getScaleExtents: min = " << min << ", max = " << max << endl;
Chris@629 772 #endif
Chris@101 773 }
Chris@101 774
Chris@21 775 int
Chris@918 776 TimeValueLayer::getYForValue(LayerGeometryProvider *v, double val) const
Chris@21 777 {
Chris@908 778 double min = 0.0, max = 0.0;
Chris@101 779 bool logarithmic = false;
Chris@918 780 int h = v->getPaintHeight();
Chris@79 781
Chris@101 782 getScaleExtents(v, min, max, logarithmic);
Chris@101 783
Chris@526 784 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@682 785 cerr << "getYForValue(" << val << "): min " << min << ", max "
Chris@682 786 << max << ", log " << logarithmic << endl;
Chris@526 787 #endif
Chris@101 788
Chris@101 789 if (logarithmic) {
Chris@197 790 val = LogRange::map(val);
Chris@79 791 }
Chris@79 792
Chris@66 793 return int(h - ((val - min) * h) / (max - min));
Chris@21 794 }
Chris@21 795
Chris@908 796 double
Chris@918 797 TimeValueLayer::getValueForY(LayerGeometryProvider *v, int y) const
Chris@21 798 {
Chris@908 799 double min = 0.0, max = 0.0;
Chris@101 800 bool logarithmic = false;
Chris@918 801 int h = v->getPaintHeight();
Chris@21 802
Chris@101 803 getScaleExtents(v, min, max, logarithmic);
Chris@101 804
Chris@908 805 double val = min + (double(h - y) * double(max - min)) / h;
Chris@101 806
Chris@101 807 if (logarithmic) {
Chris@437 808 val = LogRange::map(val);
Chris@101 809 }
Chris@101 810
Chris@101 811 return val;
Chris@21 812 }
Chris@21 813
Chris@296 814 bool
Chris@296 815 TimeValueLayer::shouldAutoAlign() const
Chris@296 816 {
Chris@296 817 if (!m_model) return false;
Chris@698 818 QString unit = getScaleUnits();
Chris@296 819 return (m_verticalScale == AutoAlignScale && unit != "");
Chris@296 820 }
Chris@296 821
Chris@68 822 QColor
Chris@918 823 TimeValueLayer::getColourForValue(LayerGeometryProvider *v, double val) const
Chris@68 824 {
Chris@908 825 double min, max;
Chris@101 826 bool log;
Chris@101 827 getScaleExtents(v, min, max, log);
Chris@68 828
Chris@197 829 if (min > max) std::swap(min, max);
Chris@197 830 if (max == min) max = min + 1;
Chris@197 831
Chris@101 832 if (log) {
Chris@197 833 val = LogRange::map(val);
Chris@68 834 }
Chris@68 835
Chris@526 836 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@682 837 cerr << "TimeValueLayer::getColourForValue: min " << min << ", max "
Chris@682 838 << max << ", log " << log << ", value " << val << endl;
Chris@526 839 #endif
Chris@68 840
Chris@1362 841 QColor solid = ColourMapper(m_colourMap, m_colourInverted, min, max).map(val);
Chris@197 842 return QColor(solid.red(), solid.green(), solid.blue(), 120);
Chris@68 843 }
Chris@68 844
Chris@287 845 int
Chris@287 846 TimeValueLayer::getDefaultColourHint(bool darkbg, bool &impose)
Chris@287 847 {
Chris@287 848 impose = false;
Chris@287 849 return ColourDatabase::getInstance()->getColourIndex
Chris@287 850 (QString(darkbg ? "Bright Green" : "Green"));
Chris@287 851 }
Chris@287 852
Chris@0 853 void
Chris@916 854 TimeValueLayer::paint(LayerGeometryProvider *v, QPainter &paint, QRect rect) const
Chris@0 855 {
Chris@0 856 if (!m_model || !m_model->isOK()) return;
Chris@0 857
Chris@908 858 sv_samplerate_t sampleRate = m_model->getSampleRate();
Chris@0 859 if (!sampleRate) return;
Chris@0 860
Chris@353 861 paint.setRenderHint(QPainter::Antialiasing, false);
Chris@353 862
Chris@0 863 // Profiler profiler("TimeValueLayer::paint", true);
Chris@0 864
Chris@0 865 int x0 = rect.left(), x1 = rect.right();
Chris@908 866 sv_frame_t frame0 = v->getFrameForX(x0);
Chris@908 867 sv_frame_t frame1 = v->getFrameForX(x1);
Chris@553 868 if (m_derivative) --frame0;
Chris@0 869
Chris@1430 870 EventVector points(m_model->getEventsWithin(frame0, frame1 - frame0, 1));
Chris@11 871 if (points.empty()) return;
Chris@0 872
Chris@287 873 paint.setPen(getBaseQColor());
Chris@0 874
Chris@287 875 QColor brushColour(getBaseQColor());
Chris@0 876 brushColour.setAlpha(80);
Chris@0 877 paint.setBrush(brushColour);
Chris@0 878
Chris@526 879 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@682 880 cerr << "TimeValueLayer::paint: resolution is "
Chris@1430 881 << m_model->getResolution() << " frames" << endl;
Chris@526 882 #endif
Chris@0 883
Chris@908 884 double min = m_model->getValueMinimum();
Chris@908 885 double max = m_model->getValueMaximum();
Chris@0 886 if (max == min) max = min + 1.0;
Chris@0 887
Chris@918 888 int origin = int(nearbyint(v->getPaintHeight() -
Chris@1266 889 (-min * v->getPaintHeight()) / (max - min)));
Chris@0 890
Chris@0 891 QPoint localPos;
Chris@908 892 sv_frame_t illuminateFrame = -1;
Chris@0 893
Chris@44 894 if (v->shouldIlluminateLocalFeatures(this, localPos)) {
Chris@1429 895 EventVector localPoints = getLocalPoints(v, localPos.x());
Chris@526 896 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@682 897 cerr << "TimeValueLayer: " << localPoints.size() << " local points" << endl;
Chris@526 898 #endif
Chris@1429 899 if (!localPoints.empty()) {
Chris@1429 900 illuminateFrame = localPoints.begin()->getFrame();
Chris@1429 901 }
Chris@0 902 }
Chris@6 903
Chris@20 904 int w =
Chris@1266 905 v->getXForFrame(frame0 + m_model->getResolution()) -
Chris@1266 906 v->getXForFrame(frame0);
Chris@7 907
Chris@629 908 if (m_plotStyle == PlotStems) {
Chris@629 909 if (w < 2) w = 2;
Chris@629 910 } else {
Chris@629 911 if (w < 1) w = 1;
Chris@629 912 }
Chris@629 913
Chris@6 914 paint.save();
Chris@6 915
Chris@6 916 QPainterPath path;
Chris@55 917 int pointCount = 0;
Chris@79 918
Chris@79 919 int textY = 0;
Chris@79 920 if (m_plotStyle == PlotSegmentation) {
Chris@79 921 textY = v->getTextLabelHeight(this, paint);
Chris@554 922 } else {
Chris@554 923 int originY = getYForValue(v, 0.f);
Chris@918 924 if (originY > 0 && originY < v->getPaintHeight()) {
Chris@554 925 paint.save();
Chris@554 926 paint.setPen(getPartialShades(v)[1]);
Chris@554 927 paint.drawLine(x0, originY, x1, originY);
Chris@554 928 paint.restore();
Chris@554 929 }
Chris@79 930 }
Chris@6 931
Chris@908 932 sv_frame_t prevFrame = 0;
Chris@615 933
Chris@1429 934 for (EventVector::const_iterator i = points.begin();
Chris@1266 935 i != points.end(); ++i) {
Chris@0 936
Chris@553 937 if (m_derivative && i == points.begin()) continue;
Chris@721 938
Chris@1429 939 Event p(*i);
Chris@0 940
Chris@1429 941 double value = p.getValue();
Chris@553 942 if (m_derivative) {
Chris@1429 943 EventVector::const_iterator j = i;
Chris@553 944 --j;
Chris@1429 945 value -= j->getValue();
Chris@553 946 }
Chris@553 947
Chris@1429 948 int x = v->getXForFrame(p.getFrame());
Chris@1266 949 int y = getYForValue(v, value);
Chris@0 950
Chris@615 951 bool gap = false;
Chris@615 952 if (m_plotStyle == PlotDiscreteCurves) {
Chris@721 953 if (value == 0.0) {
Chris@721 954 // Treat zeros as gaps
Chris@721 955 continue;
Chris@721 956 }
Chris@1429 957 gap = (p.getFrame() > prevFrame &&
Chris@1429 958 (p.getFrame() - prevFrame >= m_model->getResolution() * 2));
Chris@615 959 }
Chris@615 960
Chris@79 961 if (m_plotStyle != PlotSegmentation) {
Chris@79 962 textY = y - paint.fontMetrics().height()
Chris@631 963 + paint.fontMetrics().ascent() - 1;
Chris@372 964 if (textY < paint.fontMetrics().ascent() + 1) {
Chris@372 965 textY = paint.fontMetrics().ascent() + 1;
Chris@372 966 }
Chris@79 967 }
Chris@79 968
Chris@1266 969 bool haveNext = false;
Chris@908 970 double nvalue = 0.f;
Chris@908 971 sv_frame_t nf = v->getModelsEndFrame();
Chris@1266 972 int nx = v->getXForFrame(nf);
Chris@1266 973 int ny = y;
Chris@34 974
Chris@1429 975 EventVector::const_iterator j = i;
Chris@1266 976 ++j;
Chris@34 977
Chris@1266 978 if (j != points.end()) {
Chris@1429 979 Event q(*j);
Chris@1429 980 nvalue = q.getValue();
Chris@1429 981 if (m_derivative) nvalue -= p.getValue();
Chris@1429 982 nf = q.getFrame();
Chris@1266 983 nx = v->getXForFrame(nf);
Chris@1266 984 ny = getYForValue(v, nvalue);
Chris@1266 985 haveNext = true;
Chris@76 986 }
Chris@76 987
Chris@1429 988 // cout << "frame = " << p.getFrame() << ", x = " << x << ", haveNext = " << haveNext
Chris@682 989 // << ", nx = " << nx << endl;
Chris@34 990
Chris@1229 991 QPen pen(getBaseQColor());
Chris@1229 992 QBrush brush(brushColour);
Chris@1229 993
Chris@615 994 if (m_plotStyle == PlotDiscreteCurves) {
Chris@1229 995 pen = QPen(getBaseQColor(), 3);
Chris@1229 996 brush = QBrush(Qt::NoBrush);
Chris@615 997 } else if (m_plotStyle == PlotSegmentation) {
Chris@1229 998 pen = QPen(getForegroundQColor(v));
Chris@1229 999 brush = QBrush(getColourForValue(v, value));
Chris@1266 1000 } else if (m_plotStyle == PlotLines ||
Chris@1266 1001 m_plotStyle == PlotCurve) {
Chris@1229 1002 brush = QBrush(Qt::NoBrush);
Chris@1266 1003 }
Chris@1229 1004
Chris@1401 1005 paint.setPen(v->scalePen(pen));
Chris@1229 1006 paint.setBrush(brush);
Chris@1229 1007
Chris@1266 1008 if (m_plotStyle == PlotStems) {
Chris@1266 1009 if (y < origin - 1) {
Chris@1266 1010 paint.drawLine(x + w/2, y + 1, x + w/2, origin);
Chris@1266 1011 } else if (y > origin + 1) {
Chris@1266 1012 paint.drawLine(x + w/2, origin, x + w/2, y - 1);
Chris@1266 1013 }
Chris@1266 1014 }
Chris@0 1015
Chris@631 1016 bool illuminate = false;
Chris@631 1017
Chris@1429 1018 if (illuminateFrame == p.getFrame()) {
Chris@6 1019
Chris@1266 1020 // not equipped to illuminate the right section in line
Chris@1266 1021 // or curve mode
Chris@6 1022
Chris@1266 1023 if (m_plotStyle != PlotCurve &&
Chris@615 1024 m_plotStyle != PlotDiscreteCurves &&
Chris@1266 1025 m_plotStyle != PlotLines) {
Chris@631 1026 illuminate = true;
Chris@631 1027 }
Chris@631 1028 }
Chris@0 1029
Chris@1266 1030 if (m_plotStyle != PlotLines &&
Chris@1266 1031 m_plotStyle != PlotCurve &&
Chris@615 1032 m_plotStyle != PlotDiscreteCurves &&
Chris@1266 1033 m_plotStyle != PlotSegmentation) {
Chris@631 1034 if (illuminate) {
Chris@631 1035 paint.save();
Chris@1401 1036 paint.setPen(v->scalePen(getForegroundQColor(v)));
Chris@631 1037 paint.setBrush(getForegroundQColor(v));
Chris@631 1038 }
Chris@326 1039 if (m_plotStyle != PlotStems ||
Chris@326 1040 w > 1) {
Chris@326 1041 paint.drawRect(x, y - 1, w, 2);
Chris@326 1042 }
Chris@631 1043 if (illuminate) {
Chris@631 1044 paint.restore();
Chris@631 1045 }
Chris@1266 1046 }
Chris@0 1047
Chris@1266 1048 if (m_plotStyle == PlotConnectedPoints ||
Chris@1266 1049 m_plotStyle == PlotLines ||
Chris@615 1050 m_plotStyle == PlotDiscreteCurves ||
Chris@1266 1051 m_plotStyle == PlotCurve) {
Chris@0 1052
Chris@1266 1053 if (haveNext) {
Chris@3 1054
Chris@1266 1055 if (m_plotStyle == PlotConnectedPoints) {
Chris@1266 1056
Chris@79 1057 paint.save();
Chris@1401 1058 paint.setPen(v->scalePen(brushColour));
Chris@1266 1059 paint.drawLine(x + w, y, nx, ny);
Chris@79 1060 paint.restore();
Chris@6 1061
Chris@1266 1062 } else if (m_plotStyle == PlotLines) {
Chris@430 1063
Chris@430 1064 if (pointCount == 0) {
Chris@430 1065 path.moveTo(x + w/2, y);
Chris@430 1066 }
Chris@6 1067
Chris@1266 1068 // paint.drawLine(x + w/2, y, nx + w/2, ny);
Chris@430 1069 path.lineTo(nx + w/2, ny);
Chris@6 1070
Chris@1266 1071 } else {
Chris@6 1072
Chris@1266 1073 double x0 = x + double(w)/2;
Chris@1266 1074 double x1 = nx + double(w)/2;
Chris@1266 1075
Chris@1266 1076 double y0 = y;
Chris@1266 1077 double y1 = ny;
Chris@55 1078
Chris@615 1079 if (m_plotStyle == PlotDiscreteCurves) {
Chris@721 1080 bool nextGap =
Chris@721 1081 (nvalue == 0.0) ||
Chris@1429 1082 (nf - p.getFrame() >= m_model->getResolution() * 2);
Chris@615 1083 if (nextGap) {
Chris@615 1084 x1 = x0;
Chris@615 1085 y1 = y0;
Chris@615 1086 }
Chris@615 1087 }
Chris@615 1088
Chris@1266 1089 if (pointCount == 0 || gap) {
Chris@1266 1090 path.moveTo((x0 + x1) / 2, (y0 + y1) / 2);
Chris@1266 1091 }
Chris@6 1092
Chris@1266 1093 if (nx - x > 5) {
Chris@1266 1094 path.cubicTo(x0, y0,
Chris@1266 1095 x0, y0,
Chris@1266 1096 (x0 + x1) / 2, (y0 + y1) / 2);
Chris@55 1097
Chris@1266 1098 // // or
Chris@1266 1099 // path.quadTo(x0, y0, (x0 + x1) / 2, (y0 + y1) / 2);
Chris@55 1100
Chris@1266 1101 } else {
Chris@883 1102 path.lineTo(x0, y0);
Chris@1266 1103 path.lineTo((x0 + x1) / 2, (y0 + y1) / 2);
Chris@1266 1104 }
Chris@1266 1105 }
Chris@1266 1106 }
Chris@1266 1107 }
Chris@0 1108
Chris@1266 1109 if (m_plotStyle == PlotSegmentation) {
Chris@76 1110
Chris@526 1111 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@682 1112 cerr << "drawing rect" << endl;
Chris@526 1113 #endif
Chris@1266 1114
Chris@1266 1115 if (nx <= x) continue;
Chris@26 1116
Chris@1401 1117 paint.setPen(v->scalePen(QPen(getForegroundQColor(v), 2)));
Chris@559 1118
Chris@631 1119 if (!illuminate) {
Chris@513 1120 if (!m_drawSegmentDivisions ||
Chris@513 1121 nx < x + 5 ||
Chris@918 1122 x >= v->getPaintWidth() - 1) {
Chris@513 1123 paint.setPen(Qt::NoPen);
Chris@513 1124 }
Chris@1266 1125 }
Chris@26 1126
Chris@1266 1127 paint.drawRect(x, -1, nx - x, v->getPaintHeight() + 1);
Chris@1266 1128 }
Chris@26 1129
Chris@741 1130 if (v->shouldShowFeatureLabels()) {
Chris@629 1131
Chris@1429 1132 QString label = p.getLabel();
Chris@741 1133 bool italic = false;
Chris@741 1134
Chris@741 1135 if (label == "" &&
Chris@741 1136 (m_plotStyle == PlotPoints ||
Chris@741 1137 m_plotStyle == PlotSegmentation ||
Chris@741 1138 m_plotStyle == PlotConnectedPoints)) {
Chris@741 1139 char lc[20];
Chris@1429 1140 snprintf(lc, 20, "%.3g", p.getValue());
Chris@741 1141 label = lc;
Chris@741 1142 italic = true;
Chris@741 1143 }
Chris@741 1144
Chris@741 1145 if (label != "") {
Chris@741 1146 // Quick test for 20px before we do the slower test using metrics
Chris@741 1147 bool haveRoom = (nx > x + 20);
Chris@741 1148 haveRoom = (haveRoom &&
Chris@741 1149 (nx > x + 6 + paint.fontMetrics().width(label)));
Chris@741 1150 if (haveRoom ||
Chris@741 1151 (!haveNext &&
Chris@741 1152 (pointCount == 0 || !italic))) {
Chris@1429 1153 PaintAssistant::drawVisibleText
Chris@1429 1154 (v, paint, x + 5, textY, label,
Chris@1429 1155 italic ?
Chris@1429 1156 PaintAssistant::OutlinedItalicText :
Chris@1429 1157 PaintAssistant::OutlinedText);
Chris@741 1158 }
Chris@741 1159 }
Chris@629 1160 }
Chris@629 1161
Chris@1429 1162 prevFrame = p.getFrame();
Chris@632 1163 ++pointCount;
Chris@0 1164 }
Chris@6 1165
Chris@691 1166 if (m_plotStyle == PlotDiscreteCurves) {
Chris@691 1167 paint.setRenderHint(QPainter::Antialiasing, true);
Chris@1266 1168 paint.drawPath(path);
Chris@691 1169 } else if ((m_plotStyle == PlotCurve || m_plotStyle == PlotLines)
Chris@691 1170 && !path.isEmpty()) {
Chris@1266 1171 paint.setRenderHint(QPainter::Antialiasing, pointCount <= v->getPaintWidth());
Chris@1266 1172 paint.drawPath(path);
Chris@6 1173 }
Chris@6 1174
Chris@6 1175 paint.restore();
Chris@6 1176
Chris@6 1177 // looks like save/restore doesn't deal with this:
Chris@6 1178 paint.setRenderHint(QPainter::Antialiasing, false);
Chris@6 1179 }
Chris@6 1180
Chris@42 1181 int
Chris@918 1182 TimeValueLayer::getVerticalScaleWidth(LayerGeometryProvider *v, bool, QPainter &paint) const
Chris@42 1183 {
Chris@1315 1184 if (!m_model) {
Chris@1315 1185 return 0;
Chris@1315 1186 } else if (shouldAutoAlign() && !valueExtentsMatchMine(v)) {
Chris@701 1187 return 0;
Chris@701 1188 } else if (m_plotStyle == PlotSegmentation) {
Chris@699 1189 if (m_verticalScale == LogScale) {
Chris@699 1190 return LogColourScale().getWidth(v, paint);
Chris@699 1191 } else {
Chris@699 1192 return LinearColourScale().getWidth(v, paint);
Chris@699 1193 }
Chris@698 1194 } else {
Chris@699 1195 if (m_verticalScale == LogScale) {
Chris@699 1196 return LogNumericalScale().getWidth(v, paint) + 10; // for piano
Chris@699 1197 } else {
Chris@699 1198 return LinearNumericalScale().getWidth(v, paint);
Chris@699 1199 }
Chris@698 1200 }
Chris@42 1201 }
Chris@42 1202
Chris@42 1203 void
Chris@918 1204 TimeValueLayer::paintVerticalScale(LayerGeometryProvider *v, bool, QPainter &paint, QRect) const
Chris@42 1205 {
Chris@1429 1206 if (!m_model || m_model->isEmpty()) return;
Chris@42 1207
Chris@699 1208 QString unit;
Chris@908 1209 double min, max;
Chris@698 1210 bool logarithmic;
Chris@698 1211
Chris@698 1212 int w = getVerticalScaleWidth(v, false, paint);
Chris@918 1213 int h = v->getPaintHeight();
Chris@698 1214
Chris@698 1215 if (m_plotStyle == PlotSegmentation) {
Chris@698 1216
Chris@699 1217 getValueExtents(min, max, logarithmic, unit);
Chris@699 1218
Chris@699 1219 if (logarithmic) {
Chris@699 1220 LogRange::mapRange(min, max);
Chris@699 1221 LogColourScale().paintVertical(v, this, paint, 0, min, max);
Chris@699 1222 } else {
Chris@699 1223 LinearColourScale().paintVertical(v, this, paint, 0, min, max);
Chris@699 1224 }
Chris@698 1225
Chris@698 1226 } else {
Chris@698 1227
Chris@699 1228 getScaleExtents(v, min, max, logarithmic);
Chris@699 1229
Chris@698 1230 if (logarithmic) {
Chris@698 1231 LogNumericalScale().paintVertical(v, this, paint, 0, min, max);
Chris@698 1232 } else {
Chris@698 1233 LinearNumericalScale().paintVertical(v, this, paint, 0, min, max);
Chris@698 1234 }
Chris@698 1235
Chris@698 1236 if (logarithmic && (getScaleUnits() == "Hz")) {
Chris@698 1237 PianoScale().paintPianoVertical
Chris@698 1238 (v, paint, QRect(w - 10, 0, 10, h),
Chris@698 1239 LogRange::unmap(min),
Chris@698 1240 LogRange::unmap(max));
Chris@698 1241 paint.drawLine(w, 0, w, h);
Chris@698 1242 }
Chris@698 1243 }
Chris@698 1244
Chris@698 1245 if (getScaleUnits() != "") {
Chris@701 1246 int mw = w - 5;
Chris@701 1247 paint.drawText(5,
Chris@701 1248 5 + paint.fontMetrics().ascent(),
Chris@701 1249 TextAbbrev::abbreviate(getScaleUnits(),
Chris@701 1250 paint.fontMetrics(),
Chris@701 1251 mw));
Chris@691 1252 }
Chris@42 1253 }
Chris@42 1254
Chris@21 1255 void
Chris@918 1256 TimeValueLayer::drawStart(LayerGeometryProvider *v, QMouseEvent *e)
Chris@21 1257 {
Chris@526 1258 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@682 1259 cerr << "TimeValueLayer::drawStart(" << e->x() << "," << e->y() << ")" << endl;
Chris@526 1260 #endif
Chris@21 1261
Chris@21 1262 if (!m_model) return;
Chris@21 1263
Chris@908 1264 sv_frame_t frame = v->getFrameForX(e->x());
Chris@908 1265 int resolution = m_model->getResolution();
Chris@21 1266 if (frame < 0) frame = 0;
Chris@76 1267 frame = (frame / resolution) * resolution;
Chris@21 1268
Chris@908 1269 double value = getValueForY(v, e->y());
Chris@21 1270
Chris@76 1271 bool havePoint = false;
Chris@76 1272
Chris@1429 1273 EventVector points = getLocalPoints(v, e->x());
Chris@76 1274 if (!points.empty()) {
Chris@1429 1275 for (EventVector::iterator i = points.begin();
Chris@76 1276 i != points.end(); ++i) {
Chris@1429 1277 if (((i->getFrame() / resolution) * resolution) != frame) {
Chris@526 1278 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@1429 1279 cerr << "ignoring out-of-range frame at " << i->getFrame() << endl;
Chris@526 1280 #endif
Chris@76 1281 continue;
Chris@76 1282 }
Chris@76 1283 m_editingPoint = *i;
Chris@76 1284 havePoint = true;
Chris@76 1285 }
Chris@76 1286 }
Chris@76 1287
Chris@76 1288 if (!havePoint) {
Chris@1429 1289 m_editingPoint = Event(frame, float(value), tr("New Point"));
Chris@76 1290 }
Chris@76 1291
Chris@23 1292 m_originalPoint = m_editingPoint;
Chris@22 1293
Chris@376 1294 if (m_editingCommand) finish(m_editingCommand);
Chris@1429 1295 m_editingCommand = new ChangeEventsCommand(m_model, tr("Draw Point"));
Chris@76 1296 if (!havePoint) {
Chris@1429 1297 m_editingCommand->add(m_editingPoint);
Chris@76 1298 }
Chris@22 1299
Chris@21 1300 m_editing = true;
Chris@21 1301 }
Chris@21 1302
Chris@21 1303 void
Chris@918 1304 TimeValueLayer::drawDrag(LayerGeometryProvider *v, QMouseEvent *e)
Chris@21 1305 {
Chris@526 1306 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@682 1307 cerr << "TimeValueLayer::drawDrag(" << e->x() << "," << e->y() << ")" << endl;
Chris@526 1308 #endif
Chris@21 1309
Chris@21 1310 if (!m_model || !m_editing) return;
Chris@21 1311
Chris@908 1312 sv_frame_t frame = v->getFrameForX(e->x());
Chris@908 1313 int resolution = m_model->getResolution();
Chris@21 1314 if (frame < 0) frame = 0;
Chris@76 1315 frame = (frame / resolution) * resolution;
Chris@21 1316
Chris@908 1317 double value = getValueForY(v, e->y());
Chris@21 1318
Chris@1429 1319 EventVector points = getLocalPoints(v, e->x());
Chris@76 1320
Chris@526 1321 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@682 1322 cerr << points.size() << " points" << endl;
Chris@526 1323 #endif
Chris@76 1324
Chris@76 1325 bool havePoint = false;
Chris@76 1326
Chris@76 1327 if (!points.empty()) {
Chris@1429 1328 for (EventVector::iterator i = points.begin();
Chris@76 1329 i != points.end(); ++i) {
Chris@1429 1330 if (i->getFrame() == m_editingPoint.getFrame() &&
Chris@1429 1331 i->getValue() == m_editingPoint.getValue()) {
Chris@526 1332 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@1429 1333 cerr << "ignoring current editing point at " << i->getFrame() << ", " << i->getValue() << endl;
Chris@526 1334 #endif
Chris@76 1335 continue;
Chris@76 1336 }
Chris@1429 1337 if (((i->getFrame() / resolution) * resolution) != frame) {
Chris@526 1338 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@1429 1339 cerr << "ignoring out-of-range frame at " << i->getFrame() << endl;
Chris@526 1340 #endif
Chris@76 1341 continue;
Chris@76 1342 }
Chris@526 1343 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@1429 1344 cerr << "adjusting to new point at " << i->getFrame() << ", " << i->getValue() << endl;
Chris@526 1345 #endif
Chris@76 1346 m_editingPoint = *i;
Chris@76 1347 m_originalPoint = m_editingPoint;
Chris@1429 1348 m_editingCommand->remove(m_editingPoint);
Chris@76 1349 havePoint = true;
Chris@76 1350 }
Chris@76 1351 }
Chris@76 1352
Chris@76 1353 if (!havePoint) {
Chris@1429 1354 if (frame == m_editingPoint.getFrame()) {
Chris@1429 1355 m_editingCommand->remove(m_editingPoint);
Chris@76 1356 }
Chris@76 1357 }
Chris@76 1358
Chris@1429 1359 m_editingPoint = m_editingPoint
Chris@1429 1360 .withFrame(frame)
Chris@1429 1361 .withValue(float(value));
Chris@1429 1362 m_editingCommand->add(m_editingPoint);
Chris@21 1363 }
Chris@21 1364
Chris@21 1365 void
Chris@918 1366 TimeValueLayer::drawEnd(LayerGeometryProvider *, QMouseEvent *)
Chris@21 1367 {
Chris@526 1368 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@682 1369 cerr << "TimeValueLayer::drawEnd" << endl;
Chris@526 1370 #endif
Chris@21 1371 if (!m_model || !m_editing) return;
Chris@376 1372 finish(m_editingCommand);
Chris@1408 1373 m_editingCommand = nullptr;
Chris@21 1374 m_editing = false;
Chris@21 1375 }
Chris@21 1376
Chris@21 1377 void
Chris@918 1378 TimeValueLayer::eraseStart(LayerGeometryProvider *v, QMouseEvent *e)
Chris@335 1379 {
Chris@335 1380 if (!m_model) return;
Chris@335 1381
Chris@1429 1382 EventVector points = getLocalPoints(v, e->x());
Chris@335 1383 if (points.empty()) return;
Chris@335 1384
Chris@335 1385 m_editingPoint = *points.begin();
Chris@335 1386
Chris@335 1387 if (m_editingCommand) {
Chris@1266 1388 finish(m_editingCommand);
Chris@1408 1389 m_editingCommand = nullptr;
Chris@335 1390 }
Chris@335 1391
Chris@335 1392 m_editing = true;
Chris@335 1393 }
Chris@335 1394
Chris@335 1395 void
Chris@918 1396 TimeValueLayer::eraseDrag(LayerGeometryProvider *, QMouseEvent *)
Chris@335 1397 {
Chris@335 1398 }
Chris@335 1399
Chris@335 1400 void
Chris@918 1401 TimeValueLayer::eraseEnd(LayerGeometryProvider *v, QMouseEvent *e)
Chris@335 1402 {
Chris@335 1403 if (!m_model || !m_editing) return;
Chris@335 1404
Chris@335 1405 m_editing = false;
Chris@335 1406
Chris@1429 1407 EventVector points = getLocalPoints(v, e->x());
Chris@335 1408 if (points.empty()) return;
Chris@1429 1409 if (points.begin()->getFrame() != m_editingPoint.getFrame() ||
Chris@1429 1410 points.begin()->getValue() != m_editingPoint.getValue()) return;
Chris@335 1411
Chris@1429 1412 m_editingCommand = new ChangeEventsCommand(m_model, tr("Erase Point"));
Chris@1429 1413 m_editingCommand->remove(m_editingPoint);
Chris@376 1414 finish(m_editingCommand);
Chris@1408 1415 m_editingCommand = nullptr;
Chris@335 1416 m_editing = false;
Chris@335 1417 }
Chris@335 1418
Chris@335 1419 void
Chris@918 1420 TimeValueLayer::editStart(LayerGeometryProvider *v, QMouseEvent *e)
Chris@21 1421 {
Chris@526 1422 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@682 1423 cerr << "TimeValueLayer::editStart(" << e->x() << "," << e->y() << ")" << endl;
Chris@526 1424 #endif
Chris@21 1425
Chris@21 1426 if (!m_model) return;
Chris@21 1427
Chris@1429 1428 EventVector points = getLocalPoints(v, e->x());
Chris@21 1429 if (points.empty()) return;
Chris@21 1430
Chris@21 1431 m_editingPoint = *points.begin();
Chris@23 1432 m_originalPoint = m_editingPoint;
Chris@22 1433
Chris@22 1434 if (m_editingCommand) {
Chris@1266 1435 finish(m_editingCommand);
Chris@1408 1436 m_editingCommand = nullptr;
Chris@22 1437 }
Chris@22 1438
Chris@21 1439 m_editing = true;
Chris@21 1440 }
Chris@21 1441
Chris@21 1442 void
Chris@918 1443 TimeValueLayer::editDrag(LayerGeometryProvider *v, QMouseEvent *e)
Chris@21 1444 {
Chris@526 1445 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@682 1446 cerr << "TimeValueLayer::editDrag(" << e->x() << "," << e->y() << ")" << endl;
Chris@526 1447 #endif
Chris@21 1448
Chris@21 1449 if (!m_model || !m_editing) return;
Chris@21 1450
Chris@908 1451 sv_frame_t frame = v->getFrameForX(e->x());
Chris@21 1452 if (frame < 0) frame = 0;
Chris@21 1453 frame = frame / m_model->getResolution() * m_model->getResolution();
Chris@21 1454
Chris@908 1455 double value = getValueForY(v, e->y());
Chris@21 1456
Chris@22 1457 if (!m_editingCommand) {
Chris@1429 1458 m_editingCommand = new ChangeEventsCommand(m_model, tr("Drag Point"));
Chris@22 1459 }
Chris@22 1460
Chris@1429 1461 m_editingCommand->remove(m_editingPoint);
Chris@1429 1462 m_editingPoint = m_editingPoint
Chris@1429 1463 .withFrame(frame)
Chris@1429 1464 .withValue(float(value));
Chris@1429 1465 m_editingCommand->add(m_editingPoint);
Chris@21 1466 }
Chris@21 1467
Chris@21 1468 void
Chris@918 1469 TimeValueLayer::editEnd(LayerGeometryProvider *, QMouseEvent *)
Chris@21 1470 {
Chris@526 1471 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@682 1472 cerr << "TimeValueLayer::editEnd" << endl;
Chris@526 1473 #endif
Chris@21 1474 if (!m_model || !m_editing) return;
Chris@23 1475
Chris@23 1476 if (m_editingCommand) {
Chris@23 1477
Chris@1266 1478 QString newName = m_editingCommand->getName();
Chris@23 1479
Chris@1429 1480 if (m_editingPoint.getFrame() != m_originalPoint.getFrame()) {
Chris@1429 1481 if (m_editingPoint.getValue() != m_originalPoint.getValue()) {
Chris@1266 1482 newName = tr("Edit Point");
Chris@1266 1483 } else {
Chris@1266 1484 newName = tr("Relocate Point");
Chris@1266 1485 }
Chris@1266 1486 } else {
Chris@1266 1487 newName = tr("Change Point Value");
Chris@1266 1488 }
Chris@23 1489
Chris@1266 1490 m_editingCommand->setName(newName);
Chris@1266 1491 finish(m_editingCommand);
Chris@23 1492 }
Chris@23 1493
Chris@1408 1494 m_editingCommand = nullptr;
Chris@21 1495 m_editing = false;
Chris@21 1496 }
Chris@21 1497
Chris@255 1498 bool
Chris@918 1499 TimeValueLayer::editOpen(LayerGeometryProvider *v, QMouseEvent *e)
Chris@70 1500 {
Chris@255 1501 if (!m_model) return false;
Chris@70 1502
Chris@1429 1503 EventVector points = getLocalPoints(v, e->x());
Chris@255 1504 if (points.empty()) return false;
Chris@70 1505
Chris@1429 1506 Event point = *points.begin();
Chris@70 1507
Chris@70 1508 ItemEditDialog *dialog = new ItemEditDialog
Chris@70 1509 (m_model->getSampleRate(),
Chris@70 1510 ItemEditDialog::ShowTime |
Chris@70 1511 ItemEditDialog::ShowValue |
Chris@73 1512 ItemEditDialog::ShowText,
Chris@698 1513 getScaleUnits());
Chris@70 1514
Chris@1429 1515 dialog->setFrameTime(point.getFrame());
Chris@1429 1516 dialog->setValue(point.getValue());
Chris@1429 1517 dialog->setText(point.getLabel());
Chris@70 1518
Chris@70 1519 if (dialog->exec() == QDialog::Accepted) {
Chris@70 1520
Chris@1429 1521 Event newPoint = point
Chris@1429 1522 .withFrame(dialog->getFrameTime())
Chris@1429 1523 .withValue(dialog->getValue())
Chris@1429 1524 .withLabel(dialog->getText());
Chris@70 1525
Chris@1429 1526 ChangeEventsCommand *command =
Chris@1429 1527 new ChangeEventsCommand(m_model, tr("Edit Point"));
Chris@1429 1528 command->remove(point);
Chris@1429 1529 command->add(newPoint);
Chris@376 1530 finish(command);
Chris@70 1531 }
Chris@70 1532
Chris@70 1533 delete dialog;
Chris@255 1534 return true;
Chris@70 1535 }
Chris@70 1536
Chris@70 1537 void
Chris@908 1538 TimeValueLayer::moveSelection(Selection s, sv_frame_t newStartFrame)
Chris@43 1539 {
Chris@99 1540 if (!m_model) return;
Chris@99 1541
Chris@1429 1542 ChangeEventsCommand *command =
Chris@1429 1543 new ChangeEventsCommand(m_model, tr("Drag Selection"));
Chris@43 1544
Chris@1429 1545 EventVector points =
Chris@1429 1546 m_model->getEventsWithin(s.getStartFrame(), s.getDuration());
Chris@43 1547
Chris@1429 1548 for (Event p: points) {
Chris@43 1549
Chris@1429 1550 Event newPoint = p.withFrame
Chris@1429 1551 (p.getFrame() + newStartFrame - s.getStartFrame());
Chris@1429 1552 command->remove(p);
Chris@1429 1553 command->add(newPoint);
Chris@43 1554 }
Chris@43 1555
Chris@376 1556 finish(command);
Chris@43 1557 }
Chris@43 1558
Chris@43 1559 void
Chris@43 1560 TimeValueLayer::resizeSelection(Selection s, Selection newSize)
Chris@43 1561 {
Chris@1429 1562 if (!m_model || !s.getDuration()) return;
Chris@99 1563
Chris@1429 1564 ChangeEventsCommand *command =
Chris@1429 1565 new ChangeEventsCommand(m_model, tr("Resize Selection"));
Chris@43 1566
Chris@1429 1567 EventVector points =
Chris@1429 1568 m_model->getEventsWithin(s.getStartFrame(), s.getDuration());
Chris@43 1569
Chris@1429 1570 double ratio = double(newSize.getDuration()) / double(s.getDuration());
Chris@1429 1571 double oldStart = double(s.getStartFrame());
Chris@1429 1572 double newStart = double(newSize.getStartFrame());
Chris@43 1573
Chris@1429 1574 for (Event p: points) {
Chris@1429 1575
Chris@1429 1576 double newFrame = (double(p.getFrame()) - oldStart) * ratio + newStart;
Chris@43 1577
Chris@1429 1578 Event newPoint = p
Chris@1429 1579 .withFrame(lrint(newFrame));
Chris@1429 1580 command->remove(p);
Chris@1429 1581 command->add(newPoint);
Chris@43 1582 }
Chris@43 1583
Chris@376 1584 finish(command);
Chris@43 1585 }
Chris@43 1586
Chris@76 1587 void
Chris@76 1588 TimeValueLayer::deleteSelection(Selection s)
Chris@76 1589 {
Chris@99 1590 if (!m_model) return;
Chris@99 1591
Chris@1429 1592 ChangeEventsCommand *command =
Chris@1429 1593 new ChangeEventsCommand(m_model, tr("Delete Selected Points"));
Chris@76 1594
Chris@1429 1595 EventVector points =
Chris@1429 1596 m_model->getEventsWithin(s.getStartFrame(), s.getDuration());
Chris@76 1597
Chris@1429 1598 for (Event p: points) {
Chris@1429 1599 command->remove(p);
Chris@76 1600 }
Chris@76 1601
Chris@376 1602 finish(command);
Chris@76 1603 }
Chris@76 1604
Chris@76 1605 void
Chris@918 1606 TimeValueLayer::copy(LayerGeometryProvider *v, Selection s, Clipboard &to)
Chris@76 1607 {
Chris@99 1608 if (!m_model) return;
Chris@99 1609
Chris@1429 1610 EventVector points =
Chris@1429 1611 m_model->getEventsWithin(s.getStartFrame(), s.getDuration());
Chris@76 1612
Chris@1429 1613 for (Event p: points) {
Chris@1429 1614 to.addPoint(p.withReferenceFrame(alignToReference(v, p.getFrame())));
Chris@76 1615 }
Chris@76 1616 }
Chris@76 1617
Chris@125 1618 bool
Chris@918 1619 TimeValueLayer::paste(LayerGeometryProvider *v, const Clipboard &from, sv_frame_t /* frameOffset */,
Chris@125 1620 bool interactive)
Chris@76 1621 {
Chris@125 1622 if (!m_model) return false;
Chris@99 1623
Chris@1423 1624 const EventVector &points = from.getPoints();
Chris@76 1625
Chris@360 1626 bool realign = false;
Chris@360 1627
Chris@360 1628 if (clipboardHasDifferentAlignment(v, from)) {
Chris@360 1629
Chris@360 1630 QMessageBox::StandardButton button =
Chris@919 1631 QMessageBox::question(v->getView(), tr("Re-align pasted items?"),
Chris@360 1632 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 1633 QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel,
Chris@360 1634 QMessageBox::Yes);
Chris@360 1635
Chris@360 1636 if (button == QMessageBox::Cancel) {
Chris@360 1637 return false;
Chris@360 1638 }
Chris@360 1639
Chris@360 1640 if (button == QMessageBox::Yes) {
Chris@360 1641 realign = true;
Chris@360 1642 }
Chris@360 1643 }
Chris@360 1644
Chris@1429 1645 ChangeEventsCommand *command =
Chris@1429 1646 new ChangeEventsCommand(m_model, tr("Paste"));
Chris@76 1647
Chris@125 1648 enum ValueAvailability {
Chris@125 1649 UnknownAvailability,
Chris@125 1650 NoValues,
Chris@125 1651 SomeValues,
Chris@125 1652 AllValues
Chris@125 1653 };
Chris@125 1654
Chris@340 1655 Labeller::ValueType generation = Labeller::ValueNone;
Chris@125 1656
Chris@125 1657 bool haveUsableLabels = false;
Chris@340 1658 Labeller labeller;
Chris@340 1659 labeller.setSampleRate(m_model->getSampleRate());
Chris@125 1660
Chris@125 1661 if (interactive) {
Chris@125 1662
Chris@125 1663 ValueAvailability availability = UnknownAvailability;
Chris@125 1664
Chris@1423 1665 for (EventVector::const_iterator i = points.begin();
Chris@125 1666 i != points.end(); ++i) {
Chris@125 1667
Chris@125 1668 if (availability == UnknownAvailability) {
Chris@1423 1669 if (i->hasValue()) availability = AllValues;
Chris@125 1670 else availability = NoValues;
Chris@125 1671 continue;
Chris@125 1672 }
Chris@125 1673
Chris@1423 1674 if (i->hasValue()) {
Chris@125 1675 if (availability == NoValues) {
Chris@125 1676 availability = SomeValues;
Chris@125 1677 }
Chris@125 1678 } else {
Chris@125 1679 if (availability == AllValues) {
Chris@125 1680 availability = SomeValues;
Chris@125 1681 }
Chris@125 1682 }
Chris@125 1683
Chris@125 1684 if (!haveUsableLabels) {
Chris@1423 1685 if (i->hasLabel()) {
Chris@125 1686 if (i->getLabel().contains(QRegExp("[0-9]"))) {
Chris@125 1687 haveUsableLabels = true;
Chris@125 1688 }
Chris@125 1689 }
Chris@125 1690 }
Chris@125 1691
Chris@125 1692 if (availability == SomeValues && haveUsableLabels) break;
Chris@125 1693 }
Chris@125 1694
Chris@125 1695 if (availability == NoValues || availability == SomeValues) {
Chris@125 1696
Chris@125 1697 QString text;
Chris@125 1698 if (availability == NoValues) {
Chris@125 1699 text = tr("The items you are pasting do not have values.\nWhat values do you want to use for these items?");
Chris@125 1700 } else {
Chris@125 1701 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 1702 }
Chris@125 1703
Chris@340 1704 Labeller::TypeNameMap names = labeller.getTypeNames();
Chris@340 1705
Chris@125 1706 QStringList options;
Chris@340 1707 std::vector<Labeller::ValueType> genopts;
Chris@125 1708
Chris@340 1709 for (Labeller::TypeNameMap::const_iterator i = names.begin();
Chris@340 1710 i != names.end(); ++i) {
Chris@340 1711 if (i->first == Labeller::ValueNone) options << tr("Zero for all items");
Chris@340 1712 else options << i->second;
Chris@340 1713 genopts.push_back(i->first);
Chris@125 1714 }
Chris@125 1715
Chris@125 1716 static int prevSelection = 0;
Chris@125 1717
Chris@125 1718 bool ok = false;
Chris@125 1719 QString selected = ListInputDialog::getItem
Chris@1408 1720 (nullptr, tr("Choose value calculation"),
Chris@125 1721 text, options, prevSelection, &ok);
Chris@125 1722
Chris@852 1723 if (!ok) {
Chris@852 1724 delete command;
Chris@852 1725 return false;
Chris@852 1726 }
Chris@125 1727 int selection = 0;
Chris@340 1728 generation = Labeller::ValueNone;
Chris@125 1729
Chris@125 1730 for (QStringList::const_iterator i = options.begin();
Chris@125 1731 i != options.end(); ++i) {
Chris@125 1732 if (selected == *i) {
Chris@340 1733 generation = genopts[selection];
Chris@125 1734 break;
Chris@125 1735 }
Chris@125 1736 ++selection;
Chris@125 1737 }
Chris@340 1738
Chris@340 1739 labeller.setType(generation);
Chris@340 1740
Chris@340 1741 if (generation == Labeller::ValueFromCyclicalCounter ||
Chris@340 1742 generation == Labeller::ValueFromTwoLevelCounter) {
Chris@616 1743 int cycleSize = QInputDialog::getInt
Chris@1408 1744 (nullptr, tr("Select cycle size"),
Chris@340 1745 tr("Cycle size:"), 4, 2, 16, 1);
Chris@340 1746 labeller.setCounterCycleSize(cycleSize);
Chris@340 1747 }
Chris@125 1748
Chris@125 1749 prevSelection = selection;
Chris@125 1750 }
Chris@125 1751 }
Chris@125 1752
Chris@1429 1753 Event prevPoint;
Chris@125 1754
Chris@1423 1755 for (EventVector::const_iterator i = points.begin();
Chris@76 1756 i != points.end(); ++i) {
Chris@76 1757
Chris@908 1758 sv_frame_t frame = 0;
Chris@360 1759
Chris@360 1760 if (!realign) {
Chris@360 1761
Chris@360 1762 frame = i->getFrame();
Chris@360 1763
Chris@360 1764 } else {
Chris@360 1765
Chris@1423 1766 if (i->hasReferenceFrame()) {
Chris@360 1767 frame = i->getReferenceFrame();
Chris@360 1768 frame = alignFromReference(v, frame);
Chris@360 1769 } else {
Chris@360 1770 frame = i->getFrame();
Chris@360 1771 }
Chris@76 1772 }
Chris@360 1773
Chris@1429 1774 Event newPoint = *i;
Chris@1429 1775 if (!i->hasLabel() && i->hasValue()) {
Chris@1429 1776 newPoint = newPoint.withLabel(QString("%1").arg(i->getValue()));
Chris@125 1777 }
Chris@125 1778
Chris@372 1779 bool usePrev = false;
Chris@1429 1780 Event formerPrevPoint = prevPoint;
Chris@372 1781
Chris@1429 1782 if (!i->hasValue()) {
Chris@526 1783 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@1429 1784 cerr << "Setting value on point at " << newPoint.getFrame() << " from labeller";
Chris@526 1785 if (i == points.begin()) {
Chris@682 1786 cerr << ", no prev point" << endl;
Chris@526 1787 } else {
Chris@1429 1788 cerr << ", prev point is at " << prevPoint.getFrame() << endl;
Chris@526 1789 }
Chris@526 1790 #endif
Chris@1429 1791
Chris@1429 1792 Labeller::Revaluing valuing =
Chris@1429 1793 labeller.revalue
Chris@1408 1794 (newPoint, (i == points.begin()) ? nullptr : &prevPoint);
Chris@1429 1795
Chris@526 1796 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@1429 1797 cerr << "New point value = " << newPoint.getValue() << endl;
Chris@526 1798 #endif
Chris@1429 1799 if (valuing.first == Labeller::AppliesToPreviousEvent) {
Chris@372 1800 usePrev = true;
Chris@1429 1801 prevPoint = valuing.second;
Chris@1429 1802 } else {
Chris@1429 1803 newPoint = valuing.second;
Chris@372 1804 }
Chris@372 1805 }
Chris@372 1806
Chris@372 1807 if (usePrev) {
Chris@1429 1808 command->remove(formerPrevPoint);
Chris@1429 1809 command->add(prevPoint);
Chris@340 1810 }
Chris@125 1811
Chris@340 1812 prevPoint = newPoint;
Chris@1429 1813 command->add(newPoint);
Chris@76 1814 }
Chris@76 1815
Chris@376 1816 finish(command);
Chris@125 1817 return true;
Chris@360 1818 }
Chris@76 1819
Chris@316 1820 void
Chris@316 1821 TimeValueLayer::toXml(QTextStream &stream,
Chris@316 1822 QString indent, QString extraAttributes) const
Chris@6 1823 {
Chris@1362 1824 QString s;
Chris@1362 1825
Chris@1362 1826 s += QString("plotStyle=\"%1\" "
Chris@1362 1827 "verticalScale=\"%2\" "
Chris@1362 1828 "scaleMinimum=\"%3\" "
Chris@1362 1829 "scaleMaximum=\"%4\" "
Chris@1362 1830 "drawDivisions=\"%5\" "
Chris@1362 1831 "derivative=\"%6\" ")
Chris@1362 1832 .arg(m_plotStyle)
Chris@1362 1833 .arg(m_verticalScale)
Chris@1362 1834 .arg(m_scaleMinimum)
Chris@1362 1835 .arg(m_scaleMaximum)
Chris@1362 1836 .arg(m_drawSegmentDivisions ? "true" : "false")
Chris@1362 1837 .arg(m_derivative ? "true" : "false");
Chris@1362 1838
Chris@1362 1839 // New-style colour map attribute, by string id rather than by
Chris@1362 1840 // number
Chris@1362 1841
Chris@1362 1842 s += QString("fillColourMap=\"%1\" ")
Chris@1362 1843 .arg(ColourMapper::getColourMapId(m_colourMap));
Chris@1362 1844
Chris@1362 1845 // Old-style colour map attribute
Chris@1362 1846
Chris@1362 1847 s += QString("colourMap=\"%1\" ")
Chris@1362 1848 .arg(ColourMapper::getBackwardCompatibilityColourMap(m_colourMap));
Chris@1362 1849
Chris@1362 1850 SingleColourLayer::toXml(stream, indent, extraAttributes + " " + s);
Chris@0 1851 }
Chris@0 1852
Chris@11 1853 void
Chris@11 1854 TimeValueLayer::setProperties(const QXmlAttributes &attributes)
Chris@11 1855 {
Chris@287 1856 SingleColourLayer::setProperties(attributes);
Chris@11 1857
Chris@445 1858 bool ok, alsoOk;
Chris@287 1859
Chris@1362 1860 QString colourMapId = attributes.value("fillColourMap");
Chris@1362 1861 int colourMap = ColourMapper::getColourMapById(colourMapId);
Chris@1362 1862 if (colourMap >= 0) {
Chris@1362 1863 setFillColourMap(colourMap);
Chris@1362 1864 } else {
Chris@1362 1865 colourMap = attributes.value("colourMap").toInt(&ok);
Chris@1362 1866 if (ok && colourMap < ColourMapper::getColourMapCount()) {
Chris@1362 1867 setFillColourMap(colourMap);
Chris@1362 1868 }
Chris@1362 1869 }
Chris@287 1870
Chris@11 1871 PlotStyle style = (PlotStyle)
Chris@1266 1872 attributes.value("plotStyle").toInt(&ok);
Chris@11 1873 if (ok) setPlotStyle(style);
Chris@286 1874
Chris@286 1875 VerticalScale scale = (VerticalScale)
Chris@1266 1876 attributes.value("verticalScale").toInt(&ok);
Chris@286 1877 if (ok) setVerticalScale(scale);
Chris@445 1878
Chris@513 1879 bool draw = (attributes.value("drawDivisions").trimmed() == "true");
Chris@513 1880 setDrawSegmentDivisions(draw);
Chris@513 1881
Chris@553 1882 bool derivative = (attributes.value("derivative").trimmed() == "true");
Chris@553 1883 setShowDerivative(derivative);
Chris@553 1884
Chris@445 1885 float min = attributes.value("scaleMinimum").toFloat(&ok);
Chris@445 1886 float max = attributes.value("scaleMaximum").toFloat(&alsoOk);
Chris@526 1887 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@682 1888 cerr << "from properties: min = " << min << ", max = " << max << endl;
Chris@526 1889 #endif
Chris@526 1890 if (ok && alsoOk && min != max) setDisplayExtents(min, max);
Chris@11 1891 }
Chris@11 1892