annotate layer/TimeValueLayer.cpp @ 1330:c1f719094c25 zoom

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