annotate layer/TimeValueLayer.cpp @ 1135:628cd329c241 spectrogram-minor-refactor

Use a count of bins rather than min and max bins (because the name maxbin tells us nothing about whether the range is inclusive or not)
author Chris Cannam
date Wed, 03 Aug 2016 14:20:27 +0100
parents ee01a4062747
children 69ff93e0c624
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@287 135 if (name == "Colour" && m_plotStyle == PlotSegmentation) return ValueProperty;
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@216 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@0 176
Chris@10 177 if (min) *min = 0;
Chris@615 178 if (max) *max = 6;
Chris@216 179 if (deflt) *deflt = int(PlotConnectedPoints);
Chris@0 180
Chris@216 181 val = int(m_plotStyle);
Chris@0 182
Chris@87 183 } else if (name == "Vertical Scale") {
Chris@66 184
Chris@66 185 if (min) *min = 0;
Chris@66 186 if (max) *max = 3;
Chris@216 187 if (deflt) *deflt = int(AutoAlignScale);
Chris@66 188
Chris@216 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@0 214
Chris@287 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@0 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@0 228 switch (value) {
Chris@0 229 default:
Chris@0 230 case 0: return tr("Points");
Chris@0 231 case 1: return tr("Stems");
Chris@6 232 case 2: return tr("Connected Points");
Chris@6 233 case 3: return tr("Lines");
Chris@6 234 case 4: return tr("Curve");
Chris@26 235 case 5: return tr("Segmentation");
Chris@615 236 case 6: return tr("Discrete Curves");
Chris@0 237 }
Chris@87 238 } else if (name == "Vertical Scale") {
Chris@66 239 switch (value) {
Chris@66 240 default:
Chris@101 241 case 0: return tr("Auto-Align");
Chris@198 242 case 1: return tr("Linear");
Chris@198 243 case 2: return tr("Log");
Chris@198 244 case 3: return tr("+/-1");
Chris@66 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@0 256 setPlotStyle(PlotStyle(value));
Chris@87 257 } else if (name == "Vertical Scale") {
Chris@66 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@615 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@0 541 m_model->getPoints(frame);
Chris@0 542
Chris@0 543 if (!onPoints.empty()) {
Chris@0 544 return onPoints;
Chris@0 545 }
Chris@0 546
Chris@0 547 SparseTimeValueModel::PointList prevPoints =
Chris@0 548 m_model->getPreviousPoints(frame);
Chris@0 549 SparseTimeValueModel::PointList nextPoints =
Chris@0 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@0 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@44 559 !(nextPoints.begin()->frame > v->getEndFrame())) {
Chris@0 560 usePoints = nextPoints;
Chris@0 561 } else if (nextPoints.begin()->frame - frame <
Chris@0 562 frame - prevPoints.begin()->frame) {
Chris@0 563 usePoints = nextPoints;
Chris@0 564 }
Chris@0 565
Chris@28 566 if (!usePoints.empty()) {
Chris@28 567 int fuzz = 2;
Chris@44 568 int px = v->getXForFrame(usePoints.begin()->frame);
Chris@28 569 if ((px > x && px - x > fuzz) ||
Chris@631 570 (px < x && x - px > fuzz + 3)) {
Chris@28 571 usePoints.clear();
Chris@28 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@0 600 if (!m_model->isReady()) {
Chris@25 601 return tr("In progress");
Chris@25 602 } else {
Chris@25 603 return tr("No local points");
Chris@0 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@960 629 text = QString(tr("Time:\t%1\nValue:\t%2\nNo label"))
Chris@25 630 .arg(rt.toText(true).c_str())
Chris@960 631 .arg(valueText);
Chris@101 632 } else {
Chris@960 633 text = QString(tr("Time:\t%1\nValue:\t%2\nLabel:\t%4"))
Chris@101 634 .arg(rt.toText(true).c_str())
Chris@960 635 .arg(valueText)
Chris@25 636 .arg(points.begin()->label);
Chris@25 637 }
Chris@0 638
Chris@44 639 pos = QPoint(v->getXForFrame(useFrame),
Chris@44 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@805 646 int &resolution,
Chris@28 647 SnapType snap) const
Chris@13 648 {
Chris@13 649 if (!m_model) {
Chris@44 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@28 657
Chris@44 658 points = getLocalPoints(v, v->getXForFrame(frame));
Chris@28 659 if (points.empty()) return false;
Chris@28 660 frame = points.begin()->frame;
Chris@28 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@13 669 i != points.end(); ++i) {
Chris@13 670
Chris@28 671 if (snap == SnapRight) {
Chris@28 672
Chris@13 673 if (i->frame > frame) {
Chris@28 674 snapped = i->frame;
Chris@28 675 found = true;
Chris@13 676 break;
Chris@13 677 }
Chris@28 678
Chris@28 679 } else if (snap == SnapLeft) {
Chris@28 680
Chris@13 681 if (i->frame <= frame) {
Chris@28 682 snapped = i->frame;
Chris@28 683 found = true; // don't break, as the next may be better
Chris@28 684 } else {
Chris@28 685 break;
Chris@28 686 }
Chris@28 687
Chris@28 688 } else { // nearest
Chris@28 689
Chris@28 690 SparseTimeValueModel::PointList::const_iterator j = i;
Chris@28 691 ++j;
Chris@28 692
Chris@28 693 if (j == points.end()) {
Chris@28 694
Chris@28 695 snapped = i->frame;
Chris@28 696 found = true;
Chris@28 697 break;
Chris@28 698
Chris@28 699 } else if (j->frame >= frame) {
Chris@28 700
Chris@28 701 if (j->frame - frame < frame - i->frame) {
Chris@28 702 snapped = j->frame;
Chris@28 703 } else {
Chris@28 704 snapped = i->frame;
Chris@28 705 }
Chris@28 706 found = true;
Chris@28 707 break;
Chris@13 708 }
Chris@13 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@517 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@517 766 if (snap == SnapRight) {
Chris@517 767
Chris@517 768 if (i->frame > matchframe &&
Chris@908 769 fabs(i->value - matchvalue) < epsilon) {
Chris@517 770 snapped = i->frame;
Chris@517 771 found = true;
Chris@517 772 break;
Chris@517 773 }
Chris@517 774
Chris@517 775 } else if (snap == SnapLeft) {
Chris@517 776
Chris@517 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@517 782 } else if (found || distant) {
Chris@517 783 break;
Chris@517 784 }
Chris@517 785
Chris@517 786 } else {
Chris@517 787 // no other snap types supported
Chris@517 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@0 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@682 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@918 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@0 954 SparseTimeValueModel::PointList localPoints =
Chris@44 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@0 959 if (!localPoints.empty()) illuminateFrame = localPoints.begin()->frame;
Chris@0 960 }
Chris@6 961
Chris@20 962 int w =
Chris@44 963 v->getXForFrame(frame0 + m_model->getResolution()) -
Chris@44 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@0 993 i != points.end(); ++i) {
Chris@0 994
Chris@553 995 if (m_derivative && i == points.begin()) continue;
Chris@721 996
Chris@0 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@44 1006 int x = v->getXForFrame(p.frame);
Chris@553 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@34 1027 bool haveNext = false;
Chris@908 1028 double nvalue = 0.f;
Chris@908 1029 sv_frame_t nf = v->getModelsEndFrame();
Chris@615 1030 int nx = v->getXForFrame(nf);
Chris@34 1031 int ny = y;
Chris@34 1032
Chris@34 1033 SparseTimeValueModel::PointList::const_iterator j = i;
Chris@34 1034 ++j;
Chris@34 1035
Chris@34 1036 if (j != points.end()) {
Chris@34 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@615 1041 nx = v->getXForFrame(nf);
Chris@553 1042 ny = getYForValue(v, nvalue);
Chris@34 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@615 1049 if (m_plotStyle == PlotDiscreteCurves) {
Chris@615 1050 paint.setPen(QPen(getBaseQColor(), 3));
Chris@615 1051 paint.setBrush(Qt::NoBrush);
Chris@615 1052 } else if (m_plotStyle == PlotSegmentation) {
Chris@287 1053 paint.setPen(getForegroundQColor(v));
Chris@553 1054 paint.setBrush(getColourForValue(v, value));
Chris@26 1055 } else if (m_plotStyle == PlotLines ||
Chris@26 1056 m_plotStyle == PlotCurve) {
Chris@615 1057 paint.setPen(getBaseQColor());
Chris@6 1058 paint.setBrush(Qt::NoBrush);
Chris@3 1059 } else {
Chris@615 1060 paint.setPen(getBaseQColor());
Chris@6 1061 paint.setBrush(brushColour);
Chris@3 1062 }
Chris@0 1063
Chris@0 1064 if (m_plotStyle == PlotStems) {
Chris@430 1065 /*
Chris@0 1066 paint.setPen(brushColour);
Chris@0 1067 if (y < origin - 1) {
Chris@0 1068 paint.drawRect(x + w/2, y + 1, 1, origin - y);
Chris@0 1069 } else if (y > origin + 1) {
Chris@0 1070 paint.drawRect(x + w/2, origin, 1, y - origin - 1);
Chris@0 1071 }
Chris@430 1072 */
Chris@287 1073 paint.setPen(getBaseQColor());
Chris@430 1074 if (y < origin - 1) {
Chris@430 1075 paint.drawLine(x + w/2, y + 1, x + w/2, origin);
Chris@430 1076 } else if (y > origin + 1) {
Chris@430 1077 paint.drawLine(x + w/2, origin, x + w/2, y - 1);
Chris@430 1078 }
Chris@0 1079 }
Chris@0 1080
Chris@631 1081 bool illuminate = false;
Chris@631 1082
Chris@0 1083 if (illuminateFrame == p.frame) {
Chris@6 1084
Chris@631 1085 // not equipped to illuminate the right section in line
Chris@631 1086 // or curve mode
Chris@6 1087
Chris@6 1088 if (m_plotStyle != PlotCurve &&
Chris@615 1089 m_plotStyle != PlotDiscreteCurves &&
Chris@6 1090 m_plotStyle != PlotLines) {
Chris@631 1091 illuminate = true;
Chris@631 1092 }
Chris@631 1093 }
Chris@0 1094
Chris@6 1095 if (m_plotStyle != PlotLines &&
Chris@26 1096 m_plotStyle != PlotCurve &&
Chris@615 1097 m_plotStyle != PlotDiscreteCurves &&
Chris@26 1098 m_plotStyle != PlotSegmentation) {
Chris@631 1099 if (illuminate) {
Chris@631 1100 paint.save();
Chris@631 1101 paint.setPen(getForegroundQColor(v));
Chris@631 1102 paint.setBrush(getForegroundQColor(v));
Chris@631 1103 }
Chris@326 1104 if (m_plotStyle != PlotStems ||
Chris@326 1105 w > 1) {
Chris@326 1106 paint.drawRect(x, y - 1, w, 2);
Chris@326 1107 }
Chris@631 1108 if (illuminate) {
Chris@631 1109 paint.restore();
Chris@631 1110 }
Chris@3 1111 }
Chris@0 1112
Chris@6 1113 if (m_plotStyle == PlotConnectedPoints ||
Chris@6 1114 m_plotStyle == PlotLines ||
Chris@615 1115 m_plotStyle == PlotDiscreteCurves ||
Chris@6 1116 m_plotStyle == PlotCurve) {
Chris@0 1117
Chris@34 1118 if (haveNext) {
Chris@3 1119
Chris@6 1120 if (m_plotStyle == PlotConnectedPoints) {
Chris@34 1121
Chris@79 1122 paint.save();
Chris@3 1123 paint.setPen(brushColour);
Chris@3 1124 paint.drawLine(x + w, y, nx, ny);
Chris@79 1125 paint.restore();
Chris@6 1126
Chris@6 1127 } else if (m_plotStyle == PlotLines) {
Chris@430 1128
Chris@430 1129 if (pointCount == 0) {
Chris@430 1130 path.moveTo(x + w/2, y);
Chris@430 1131 }
Chris@6 1132
Chris@430 1133 // paint.drawLine(x + w/2, y, nx + w/2, ny);
Chris@430 1134 path.lineTo(nx + w/2, ny);
Chris@6 1135
Chris@3 1136 } else {
Chris@6 1137
Chris@908 1138 double x0 = x + double(w)/2;
Chris@908 1139 double x1 = nx + double(w)/2;
Chris@55 1140
Chris@908 1141 double y0 = y;
Chris@908 1142 double y1 = ny;
Chris@55 1143
Chris@615 1144 if (m_plotStyle == PlotDiscreteCurves) {
Chris@721 1145 bool nextGap =
Chris@721 1146 (nvalue == 0.0) ||
Chris@721 1147 (nf - p.frame >= m_model->getResolution() * 2);
Chris@615 1148 if (nextGap) {
Chris@615 1149 x1 = x0;
Chris@615 1150 y1 = y0;
Chris@615 1151 }
Chris@615 1152 }
Chris@615 1153
Chris@615 1154 if (pointCount == 0 || gap) {
Chris@55 1155 path.moveTo((x0 + x1) / 2, (y0 + y1) / 2);
Chris@6 1156 }
Chris@6 1157
Chris@6 1158 if (nx - x > 5) {
Chris@55 1159 path.cubicTo(x0, y0,
Chris@55 1160 x0, y0,
Chris@55 1161 (x0 + x1) / 2, (y0 + y1) / 2);
Chris@55 1162
Chris@55 1163 // // or
Chris@55 1164 // path.quadTo(x0, y0, (x0 + x1) / 2, (y0 + y1) / 2);
Chris@55 1165
Chris@6 1166 } else {
Chris@883 1167 path.lineTo(x0, y0);
Chris@55 1168 path.lineTo((x0 + x1) / 2, (y0 + y1) / 2);
Chris@6 1169 }
Chris@3 1170 }
Chris@0 1171 }
Chris@0 1172 }
Chris@0 1173
Chris@26 1174 if (m_plotStyle == PlotSegmentation) {
Chris@76 1175
Chris@526 1176 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@682 1177 cerr << "drawing rect" << endl;
Chris@526 1178 #endif
Chris@26 1179
Chris@27 1180 if (nx <= x) continue;
Chris@26 1181
Chris@559 1182 paint.setPen(QPen(getForegroundQColor(v), 2));
Chris@559 1183
Chris@631 1184 if (!illuminate) {
Chris@513 1185 if (!m_drawSegmentDivisions ||
Chris@513 1186 nx < x + 5 ||
Chris@918 1187 x >= v->getPaintWidth() - 1) {
Chris@513 1188 paint.setPen(Qt::NoPen);
Chris@513 1189 }
Chris@27 1190 }
Chris@26 1191
Chris@918 1192 paint.drawRect(x, -1, nx - x, v->getPaintHeight() + 1);
Chris@26 1193 }
Chris@26 1194
Chris@741 1195 if (v->shouldShowFeatureLabels()) {
Chris@629 1196
Chris@741 1197 QString label = p.label;
Chris@741 1198 bool italic = false;
Chris@741 1199
Chris@741 1200 if (label == "" &&
Chris@741 1201 (m_plotStyle == PlotPoints ||
Chris@741 1202 m_plotStyle == PlotSegmentation ||
Chris@741 1203 m_plotStyle == PlotConnectedPoints)) {
Chris@741 1204 char lc[20];
Chris@741 1205 snprintf(lc, 20, "%.3g", p.value);
Chris@741 1206 label = lc;
Chris@741 1207 italic = true;
Chris@741 1208 }
Chris@741 1209
Chris@741 1210 if (label != "") {
Chris@741 1211 // Quick test for 20px before we do the slower test using metrics
Chris@741 1212 bool haveRoom = (nx > x + 20);
Chris@741 1213 haveRoom = (haveRoom &&
Chris@741 1214 (nx > x + 6 + paint.fontMetrics().width(label)));
Chris@741 1215 if (haveRoom ||
Chris@741 1216 (!haveNext &&
Chris@741 1217 (pointCount == 0 || !italic))) {
Chris@1078 1218 PaintAssistant::drawVisibleText(v, paint, x + 5, textY, label,
Chris@741 1219 italic ?
Chris@1078 1220 PaintAssistant::OutlinedItalicText :
Chris@1078 1221 PaintAssistant::OutlinedText);
Chris@741 1222 }
Chris@741 1223 }
Chris@629 1224 }
Chris@629 1225
Chris@615 1226 prevFrame = p.frame;
Chris@632 1227 ++pointCount;
Chris@0 1228 }
Chris@6 1229
Chris@691 1230 if (m_plotStyle == PlotDiscreteCurves) {
Chris@691 1231 paint.setRenderHint(QPainter::Antialiasing, true);
Chris@691 1232 paint.drawPath(path);
Chris@691 1233 } else if ((m_plotStyle == PlotCurve || m_plotStyle == PlotLines)
Chris@691 1234 && !path.isEmpty()) {
Chris@918 1235 paint.setRenderHint(QPainter::Antialiasing, pointCount <= v->getPaintWidth());
Chris@6 1236 paint.drawPath(path);
Chris@6 1237 }
Chris@6 1238
Chris@6 1239 paint.restore();
Chris@6 1240
Chris@6 1241 // looks like save/restore doesn't deal with this:
Chris@6 1242 paint.setRenderHint(QPainter::Antialiasing, false);
Chris@6 1243 }
Chris@6 1244
Chris@42 1245 int
Chris@918 1246 TimeValueLayer::getVerticalScaleWidth(LayerGeometryProvider *v, bool, QPainter &paint) const
Chris@42 1247 {
Chris@701 1248 if (!m_model || shouldAutoAlign()) {
Chris@701 1249 return 0;
Chris@701 1250 } else if (m_plotStyle == PlotSegmentation) {
Chris@699 1251 if (m_verticalScale == LogScale) {
Chris@699 1252 return LogColourScale().getWidth(v, paint);
Chris@699 1253 } else {
Chris@699 1254 return LinearColourScale().getWidth(v, paint);
Chris@699 1255 }
Chris@698 1256 } else {
Chris@699 1257 if (m_verticalScale == LogScale) {
Chris@699 1258 return LogNumericalScale().getWidth(v, paint) + 10; // for piano
Chris@699 1259 } else {
Chris@699 1260 return LinearNumericalScale().getWidth(v, paint);
Chris@699 1261 }
Chris@698 1262 }
Chris@42 1263 }
Chris@42 1264
Chris@42 1265 void
Chris@918 1266 TimeValueLayer::paintVerticalScale(LayerGeometryProvider *v, bool, QPainter &paint, QRect) const
Chris@42 1267 {
Chris@717 1268 if (!m_model || m_model->getPoints().empty()) return;
Chris@42 1269
Chris@699 1270 QString unit;
Chris@908 1271 double min, max;
Chris@698 1272 bool logarithmic;
Chris@698 1273
Chris@698 1274 int w = getVerticalScaleWidth(v, false, paint);
Chris@918 1275 int h = v->getPaintHeight();
Chris@698 1276
Chris@698 1277 if (m_plotStyle == PlotSegmentation) {
Chris@698 1278
Chris@699 1279 getValueExtents(min, max, logarithmic, unit);
Chris@699 1280
Chris@699 1281 if (logarithmic) {
Chris@699 1282 LogRange::mapRange(min, max);
Chris@699 1283 LogColourScale().paintVertical(v, this, paint, 0, min, max);
Chris@699 1284 } else {
Chris@699 1285 LinearColourScale().paintVertical(v, this, paint, 0, min, max);
Chris@699 1286 }
Chris@698 1287
Chris@698 1288 } else {
Chris@698 1289
Chris@699 1290 getScaleExtents(v, min, max, logarithmic);
Chris@699 1291
Chris@698 1292 if (logarithmic) {
Chris@698 1293 LogNumericalScale().paintVertical(v, this, paint, 0, min, max);
Chris@698 1294 } else {
Chris@698 1295 LinearNumericalScale().paintVertical(v, this, paint, 0, min, max);
Chris@698 1296 }
Chris@698 1297
Chris@698 1298 if (logarithmic && (getScaleUnits() == "Hz")) {
Chris@698 1299 PianoScale().paintPianoVertical
Chris@698 1300 (v, paint, QRect(w - 10, 0, 10, h),
Chris@698 1301 LogRange::unmap(min),
Chris@698 1302 LogRange::unmap(max));
Chris@698 1303 paint.drawLine(w, 0, w, h);
Chris@698 1304 }
Chris@698 1305 }
Chris@698 1306
Chris@698 1307 if (getScaleUnits() != "") {
Chris@701 1308 int mw = w - 5;
Chris@701 1309 paint.drawText(5,
Chris@701 1310 5 + paint.fontMetrics().ascent(),
Chris@701 1311 TextAbbrev::abbreviate(getScaleUnits(),
Chris@701 1312 paint.fontMetrics(),
Chris@701 1313 mw));
Chris@691 1314 }
Chris@42 1315 }
Chris@42 1316
Chris@21 1317 void
Chris@918 1318 TimeValueLayer::drawStart(LayerGeometryProvider *v, QMouseEvent *e)
Chris@21 1319 {
Chris@526 1320 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@682 1321 cerr << "TimeValueLayer::drawStart(" << e->x() << "," << e->y() << ")" << endl;
Chris@526 1322 #endif
Chris@21 1323
Chris@21 1324 if (!m_model) return;
Chris@21 1325
Chris@908 1326 sv_frame_t frame = v->getFrameForX(e->x());
Chris@908 1327 int resolution = m_model->getResolution();
Chris@21 1328 if (frame < 0) frame = 0;
Chris@76 1329 frame = (frame / resolution) * resolution;
Chris@21 1330
Chris@908 1331 double value = getValueForY(v, e->y());
Chris@21 1332
Chris@76 1333 bool havePoint = false;
Chris@76 1334
Chris@76 1335 SparseTimeValueModel::PointList points = getLocalPoints(v, e->x());
Chris@76 1336 if (!points.empty()) {
Chris@76 1337 for (SparseTimeValueModel::PointList::iterator i = points.begin();
Chris@76 1338 i != points.end(); ++i) {
Chris@76 1339 if (((i->frame / resolution) * resolution) != frame) {
Chris@526 1340 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@682 1341 cerr << "ignoring out-of-range frame at " << i->frame << endl;
Chris@526 1342 #endif
Chris@76 1343 continue;
Chris@76 1344 }
Chris@76 1345 m_editingPoint = *i;
Chris@76 1346 havePoint = true;
Chris@76 1347 }
Chris@76 1348 }
Chris@76 1349
Chris@76 1350 if (!havePoint) {
Chris@76 1351 m_editingPoint = SparseTimeValueModel::Point
Chris@908 1352 (frame, float(value), tr("New Point"));
Chris@76 1353 }
Chris@76 1354
Chris@23 1355 m_originalPoint = m_editingPoint;
Chris@22 1356
Chris@376 1357 if (m_editingCommand) finish(m_editingCommand);
Chris@22 1358 m_editingCommand = new SparseTimeValueModel::EditCommand(m_model,
Chris@22 1359 tr("Draw Point"));
Chris@76 1360 if (!havePoint) {
Chris@76 1361 m_editingCommand->addPoint(m_editingPoint);
Chris@76 1362 }
Chris@22 1363
Chris@21 1364 m_editing = true;
Chris@21 1365 }
Chris@21 1366
Chris@21 1367 void
Chris@918 1368 TimeValueLayer::drawDrag(LayerGeometryProvider *v, QMouseEvent *e)
Chris@21 1369 {
Chris@526 1370 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@682 1371 cerr << "TimeValueLayer::drawDrag(" << e->x() << "," << e->y() << ")" << endl;
Chris@526 1372 #endif
Chris@21 1373
Chris@21 1374 if (!m_model || !m_editing) return;
Chris@21 1375
Chris@908 1376 sv_frame_t frame = v->getFrameForX(e->x());
Chris@908 1377 int resolution = m_model->getResolution();
Chris@21 1378 if (frame < 0) frame = 0;
Chris@76 1379 frame = (frame / resolution) * resolution;
Chris@21 1380
Chris@908 1381 double value = getValueForY(v, e->y());
Chris@21 1382
Chris@76 1383 SparseTimeValueModel::PointList points = getLocalPoints(v, e->x());
Chris@76 1384
Chris@526 1385 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@682 1386 cerr << points.size() << " points" << endl;
Chris@526 1387 #endif
Chris@76 1388
Chris@76 1389 bool havePoint = false;
Chris@76 1390
Chris@76 1391 if (!points.empty()) {
Chris@76 1392 for (SparseTimeValueModel::PointList::iterator i = points.begin();
Chris@76 1393 i != points.end(); ++i) {
Chris@76 1394 if (i->frame == m_editingPoint.frame &&
Chris@76 1395 i->value == m_editingPoint.value) {
Chris@526 1396 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@682 1397 cerr << "ignoring current editing point at " << i->frame << ", " << i->value << endl;
Chris@526 1398 #endif
Chris@76 1399 continue;
Chris@76 1400 }
Chris@76 1401 if (((i->frame / resolution) * resolution) != frame) {
Chris@526 1402 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@682 1403 cerr << "ignoring out-of-range frame at " << i->frame << endl;
Chris@526 1404 #endif
Chris@76 1405 continue;
Chris@76 1406 }
Chris@526 1407 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@682 1408 cerr << "adjusting to new point at " << i->frame << ", " << i->value << endl;
Chris@526 1409 #endif
Chris@76 1410 m_editingPoint = *i;
Chris@76 1411 m_originalPoint = m_editingPoint;
Chris@76 1412 m_editingCommand->deletePoint(m_editingPoint);
Chris@76 1413 havePoint = true;
Chris@76 1414 }
Chris@76 1415 }
Chris@76 1416
Chris@76 1417 if (!havePoint) {
Chris@76 1418 if (frame == m_editingPoint.frame) {
Chris@76 1419 m_editingCommand->deletePoint(m_editingPoint);
Chris@76 1420 }
Chris@76 1421 }
Chris@76 1422
Chris@76 1423 // m_editingCommand->deletePoint(m_editingPoint);
Chris@21 1424 m_editingPoint.frame = frame;
Chris@908 1425 m_editingPoint.value = float(value);
Chris@22 1426 m_editingCommand->addPoint(m_editingPoint);
Chris@21 1427 }
Chris@21 1428
Chris@21 1429 void
Chris@918 1430 TimeValueLayer::drawEnd(LayerGeometryProvider *, QMouseEvent *)
Chris@21 1431 {
Chris@526 1432 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@682 1433 cerr << "TimeValueLayer::drawEnd" << endl;
Chris@526 1434 #endif
Chris@21 1435 if (!m_model || !m_editing) return;
Chris@376 1436 finish(m_editingCommand);
Chris@22 1437 m_editingCommand = 0;
Chris@21 1438 m_editing = false;
Chris@21 1439 }
Chris@21 1440
Chris@21 1441 void
Chris@918 1442 TimeValueLayer::eraseStart(LayerGeometryProvider *v, QMouseEvent *e)
Chris@335 1443 {
Chris@335 1444 if (!m_model) return;
Chris@335 1445
Chris@335 1446 SparseTimeValueModel::PointList points = getLocalPoints(v, e->x());
Chris@335 1447 if (points.empty()) return;
Chris@335 1448
Chris@335 1449 m_editingPoint = *points.begin();
Chris@335 1450
Chris@335 1451 if (m_editingCommand) {
Chris@376 1452 finish(m_editingCommand);
Chris@335 1453 m_editingCommand = 0;
Chris@335 1454 }
Chris@335 1455
Chris@335 1456 m_editing = true;
Chris@335 1457 }
Chris@335 1458
Chris@335 1459 void
Chris@918 1460 TimeValueLayer::eraseDrag(LayerGeometryProvider *, QMouseEvent *)
Chris@335 1461 {
Chris@335 1462 }
Chris@335 1463
Chris@335 1464 void
Chris@918 1465 TimeValueLayer::eraseEnd(LayerGeometryProvider *v, QMouseEvent *e)
Chris@335 1466 {
Chris@335 1467 if (!m_model || !m_editing) return;
Chris@335 1468
Chris@335 1469 m_editing = false;
Chris@335 1470
Chris@335 1471 SparseTimeValueModel::PointList points = getLocalPoints(v, e->x());
Chris@335 1472 if (points.empty()) return;
Chris@335 1473 if (points.begin()->frame != m_editingPoint.frame ||
Chris@335 1474 points.begin()->value != m_editingPoint.value) return;
Chris@335 1475
Chris@335 1476 m_editingCommand = new SparseTimeValueModel::EditCommand
Chris@335 1477 (m_model, tr("Erase Point"));
Chris@335 1478
Chris@335 1479 m_editingCommand->deletePoint(m_editingPoint);
Chris@335 1480
Chris@376 1481 finish(m_editingCommand);
Chris@335 1482 m_editingCommand = 0;
Chris@335 1483 m_editing = false;
Chris@335 1484 }
Chris@335 1485
Chris@335 1486 void
Chris@918 1487 TimeValueLayer::editStart(LayerGeometryProvider *v, QMouseEvent *e)
Chris@21 1488 {
Chris@526 1489 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@682 1490 cerr << "TimeValueLayer::editStart(" << e->x() << "," << e->y() << ")" << endl;
Chris@526 1491 #endif
Chris@21 1492
Chris@21 1493 if (!m_model) return;
Chris@21 1494
Chris@44 1495 SparseTimeValueModel::PointList points = getLocalPoints(v, e->x());
Chris@21 1496 if (points.empty()) return;
Chris@21 1497
Chris@21 1498 m_editingPoint = *points.begin();
Chris@23 1499 m_originalPoint = m_editingPoint;
Chris@22 1500
Chris@22 1501 if (m_editingCommand) {
Chris@376 1502 finish(m_editingCommand);
Chris@22 1503 m_editingCommand = 0;
Chris@22 1504 }
Chris@22 1505
Chris@21 1506 m_editing = true;
Chris@21 1507 }
Chris@21 1508
Chris@21 1509 void
Chris@918 1510 TimeValueLayer::editDrag(LayerGeometryProvider *v, QMouseEvent *e)
Chris@21 1511 {
Chris@526 1512 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@682 1513 cerr << "TimeValueLayer::editDrag(" << e->x() << "," << e->y() << ")" << endl;
Chris@526 1514 #endif
Chris@21 1515
Chris@21 1516 if (!m_model || !m_editing) return;
Chris@21 1517
Chris@908 1518 sv_frame_t frame = v->getFrameForX(e->x());
Chris@21 1519 if (frame < 0) frame = 0;
Chris@21 1520 frame = frame / m_model->getResolution() * m_model->getResolution();
Chris@21 1521
Chris@908 1522 double value = getValueForY(v, e->y());
Chris@21 1523
Chris@22 1524 if (!m_editingCommand) {
Chris@22 1525 m_editingCommand = new SparseTimeValueModel::EditCommand(m_model,
Chris@22 1526 tr("Drag Point"));
Chris@22 1527 }
Chris@22 1528
Chris@22 1529 m_editingCommand->deletePoint(m_editingPoint);
Chris@21 1530 m_editingPoint.frame = frame;
Chris@908 1531 m_editingPoint.value = float(value);
Chris@22 1532 m_editingCommand->addPoint(m_editingPoint);
Chris@21 1533 }
Chris@21 1534
Chris@21 1535 void
Chris@918 1536 TimeValueLayer::editEnd(LayerGeometryProvider *, QMouseEvent *)
Chris@21 1537 {
Chris@526 1538 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@682 1539 cerr << "TimeValueLayer::editEnd" << endl;
Chris@526 1540 #endif
Chris@21 1541 if (!m_model || !m_editing) return;
Chris@23 1542
Chris@23 1543 if (m_editingCommand) {
Chris@23 1544
Chris@23 1545 QString newName = m_editingCommand->getName();
Chris@23 1546
Chris@23 1547 if (m_editingPoint.frame != m_originalPoint.frame) {
Chris@23 1548 if (m_editingPoint.value != m_originalPoint.value) {
Chris@23 1549 newName = tr("Edit Point");
Chris@23 1550 } else {
Chris@23 1551 newName = tr("Relocate Point");
Chris@23 1552 }
Chris@23 1553 } else {
Chris@23 1554 newName = tr("Change Point Value");
Chris@23 1555 }
Chris@23 1556
Chris@23 1557 m_editingCommand->setName(newName);
Chris@376 1558 finish(m_editingCommand);
Chris@23 1559 }
Chris@23 1560
Chris@22 1561 m_editingCommand = 0;
Chris@21 1562 m_editing = false;
Chris@21 1563 }
Chris@21 1564
Chris@255 1565 bool
Chris@918 1566 TimeValueLayer::editOpen(LayerGeometryProvider *v, QMouseEvent *e)
Chris@70 1567 {
Chris@255 1568 if (!m_model) return false;
Chris@70 1569
Chris@70 1570 SparseTimeValueModel::PointList points = getLocalPoints(v, e->x());
Chris@255 1571 if (points.empty()) return false;
Chris@70 1572
Chris@70 1573 SparseTimeValueModel::Point point = *points.begin();
Chris@70 1574
Chris@70 1575 ItemEditDialog *dialog = new ItemEditDialog
Chris@70 1576 (m_model->getSampleRate(),
Chris@70 1577 ItemEditDialog::ShowTime |
Chris@70 1578 ItemEditDialog::ShowValue |
Chris@73 1579 ItemEditDialog::ShowText,
Chris@698 1580 getScaleUnits());
Chris@70 1581
Chris@70 1582 dialog->setFrameTime(point.frame);
Chris@70 1583 dialog->setValue(point.value);
Chris@70 1584 dialog->setText(point.label);
Chris@70 1585
Chris@70 1586 if (dialog->exec() == QDialog::Accepted) {
Chris@70 1587
Chris@70 1588 SparseTimeValueModel::Point newPoint = point;
Chris@70 1589 newPoint.frame = dialog->getFrameTime();
Chris@70 1590 newPoint.value = dialog->getValue();
Chris@70 1591 newPoint.label = dialog->getText();
Chris@70 1592
Chris@70 1593 SparseTimeValueModel::EditCommand *command =
Chris@70 1594 new SparseTimeValueModel::EditCommand(m_model, tr("Edit Point"));
Chris@70 1595 command->deletePoint(point);
Chris@70 1596 command->addPoint(newPoint);
Chris@376 1597 finish(command);
Chris@70 1598 }
Chris@70 1599
Chris@70 1600 delete dialog;
Chris@255 1601 return true;
Chris@70 1602 }
Chris@70 1603
Chris@70 1604 void
Chris@908 1605 TimeValueLayer::moveSelection(Selection s, sv_frame_t newStartFrame)
Chris@43 1606 {
Chris@99 1607 if (!m_model) return;
Chris@99 1608
Chris@43 1609 SparseTimeValueModel::EditCommand *command =
Chris@43 1610 new SparseTimeValueModel::EditCommand(m_model,
Chris@43 1611 tr("Drag Selection"));
Chris@43 1612
Chris@43 1613 SparseTimeValueModel::PointList points =
Chris@43 1614 m_model->getPoints(s.getStartFrame(), s.getEndFrame());
Chris@43 1615
Chris@43 1616 for (SparseTimeValueModel::PointList::iterator i = points.begin();
Chris@43 1617 i != points.end(); ++i) {
Chris@43 1618
Chris@43 1619 if (s.contains(i->frame)) {
Chris@43 1620 SparseTimeValueModel::Point newPoint(*i);
Chris@43 1621 newPoint.frame = i->frame + newStartFrame - s.getStartFrame();
Chris@43 1622 command->deletePoint(*i);
Chris@43 1623 command->addPoint(newPoint);
Chris@43 1624 }
Chris@43 1625 }
Chris@43 1626
Chris@376 1627 finish(command);
Chris@43 1628 }
Chris@43 1629
Chris@43 1630 void
Chris@43 1631 TimeValueLayer::resizeSelection(Selection s, Selection newSize)
Chris@43 1632 {
Chris@99 1633 if (!m_model) return;
Chris@99 1634
Chris@43 1635 SparseTimeValueModel::EditCommand *command =
Chris@43 1636 new SparseTimeValueModel::EditCommand(m_model,
Chris@43 1637 tr("Resize Selection"));
Chris@43 1638
Chris@43 1639 SparseTimeValueModel::PointList points =
Chris@43 1640 m_model->getPoints(s.getStartFrame(), s.getEndFrame());
Chris@43 1641
Chris@43 1642 double ratio =
Chris@43 1643 double(newSize.getEndFrame() - newSize.getStartFrame()) /
Chris@43 1644 double(s.getEndFrame() - s.getStartFrame());
Chris@43 1645
Chris@43 1646 for (SparseTimeValueModel::PointList::iterator i = points.begin();
Chris@43 1647 i != points.end(); ++i) {
Chris@43 1648
Chris@43 1649 if (s.contains(i->frame)) {
Chris@43 1650
Chris@908 1651 double target = double(i->frame);
Chris@908 1652 target = double(newSize.getStartFrame()) +
Chris@908 1653 target - double(s.getStartFrame()) * ratio;
Chris@43 1654
Chris@43 1655 SparseTimeValueModel::Point newPoint(*i);
Chris@43 1656 newPoint.frame = lrint(target);
Chris@43 1657 command->deletePoint(*i);
Chris@43 1658 command->addPoint(newPoint);
Chris@43 1659 }
Chris@43 1660 }
Chris@43 1661
Chris@376 1662 finish(command);
Chris@43 1663 }
Chris@43 1664
Chris@76 1665 void
Chris@76 1666 TimeValueLayer::deleteSelection(Selection s)
Chris@76 1667 {
Chris@99 1668 if (!m_model) return;
Chris@99 1669
Chris@76 1670 SparseTimeValueModel::EditCommand *command =
Chris@76 1671 new SparseTimeValueModel::EditCommand(m_model,
Chris@76 1672 tr("Delete Selected Points"));
Chris@76 1673
Chris@76 1674 SparseTimeValueModel::PointList points =
Chris@76 1675 m_model->getPoints(s.getStartFrame(), s.getEndFrame());
Chris@76 1676
Chris@76 1677 for (SparseTimeValueModel::PointList::iterator i = points.begin();
Chris@76 1678 i != points.end(); ++i) {
Chris@76 1679
Chris@76 1680 if (s.contains(i->frame)) {
Chris@76 1681 command->deletePoint(*i);
Chris@76 1682 }
Chris@76 1683 }
Chris@76 1684
Chris@376 1685 finish(command);
Chris@76 1686 }
Chris@76 1687
Chris@76 1688 void
Chris@918 1689 TimeValueLayer::copy(LayerGeometryProvider *v, Selection s, Clipboard &to)
Chris@76 1690 {
Chris@99 1691 if (!m_model) return;
Chris@99 1692
Chris@76 1693 SparseTimeValueModel::PointList points =
Chris@76 1694 m_model->getPoints(s.getStartFrame(), s.getEndFrame());
Chris@76 1695
Chris@76 1696 for (SparseTimeValueModel::PointList::iterator i = points.begin();
Chris@76 1697 i != points.end(); ++i) {
Chris@76 1698 if (s.contains(i->frame)) {
Chris@83 1699 Clipboard::Point point(i->frame, i->value, i->label);
Chris@360 1700 point.setReferenceFrame(alignToReference(v, i->frame));
Chris@76 1701 to.addPoint(point);
Chris@76 1702 }
Chris@76 1703 }
Chris@76 1704 }
Chris@76 1705
Chris@125 1706 bool
Chris@918 1707 TimeValueLayer::paste(LayerGeometryProvider *v, const Clipboard &from, sv_frame_t /* frameOffset */,
Chris@125 1708 bool interactive)
Chris@76 1709 {
Chris@125 1710 if (!m_model) return false;
Chris@99 1711
Chris@76 1712 const Clipboard::PointList &points = from.getPoints();
Chris@76 1713
Chris@360 1714 bool realign = false;
Chris@360 1715
Chris@360 1716 if (clipboardHasDifferentAlignment(v, from)) {
Chris@360 1717
Chris@360 1718 QMessageBox::StandardButton button =
Chris@919 1719 QMessageBox::question(v->getView(), tr("Re-align pasted items?"),
Chris@360 1720 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 1721 QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel,
Chris@360 1722 QMessageBox::Yes);
Chris@360 1723
Chris@360 1724 if (button == QMessageBox::Cancel) {
Chris@360 1725 return false;
Chris@360 1726 }
Chris@360 1727
Chris@360 1728 if (button == QMessageBox::Yes) {
Chris@360 1729 realign = true;
Chris@360 1730 }
Chris@360 1731 }
Chris@360 1732
Chris@76 1733 SparseTimeValueModel::EditCommand *command =
Chris@76 1734 new SparseTimeValueModel::EditCommand(m_model, tr("Paste"));
Chris@76 1735
Chris@125 1736 enum ValueAvailability {
Chris@125 1737 UnknownAvailability,
Chris@125 1738 NoValues,
Chris@125 1739 SomeValues,
Chris@125 1740 AllValues
Chris@125 1741 };
Chris@125 1742
Chris@340 1743 Labeller::ValueType generation = Labeller::ValueNone;
Chris@125 1744
Chris@125 1745 bool haveUsableLabels = false;
Chris@340 1746 Labeller labeller;
Chris@340 1747 labeller.setSampleRate(m_model->getSampleRate());
Chris@125 1748
Chris@125 1749 if (interactive) {
Chris@125 1750
Chris@125 1751 ValueAvailability availability = UnknownAvailability;
Chris@125 1752
Chris@125 1753 for (Clipboard::PointList::const_iterator i = points.begin();
Chris@125 1754 i != points.end(); ++i) {
Chris@125 1755
Chris@125 1756 if (!i->haveFrame()) continue;
Chris@125 1757
Chris@125 1758 if (availability == UnknownAvailability) {
Chris@125 1759 if (i->haveValue()) availability = AllValues;
Chris@125 1760 else availability = NoValues;
Chris@125 1761 continue;
Chris@125 1762 }
Chris@125 1763
Chris@125 1764 if (i->haveValue()) {
Chris@125 1765 if (availability == NoValues) {
Chris@125 1766 availability = SomeValues;
Chris@125 1767 }
Chris@125 1768 } else {
Chris@125 1769 if (availability == AllValues) {
Chris@125 1770 availability = SomeValues;
Chris@125 1771 }
Chris@125 1772 }
Chris@125 1773
Chris@125 1774 if (!haveUsableLabels) {
Chris@125 1775 if (i->haveLabel()) {
Chris@125 1776 if (i->getLabel().contains(QRegExp("[0-9]"))) {
Chris@125 1777 haveUsableLabels = true;
Chris@125 1778 }
Chris@125 1779 }
Chris@125 1780 }
Chris@125 1781
Chris@125 1782 if (availability == SomeValues && haveUsableLabels) break;
Chris@125 1783 }
Chris@125 1784
Chris@125 1785 if (availability == NoValues || availability == SomeValues) {
Chris@125 1786
Chris@125 1787 QString text;
Chris@125 1788 if (availability == NoValues) {
Chris@125 1789 text = tr("The items you are pasting do not have values.\nWhat values do you want to use for these items?");
Chris@125 1790 } else {
Chris@125 1791 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 1792 }
Chris@125 1793
Chris@340 1794 Labeller::TypeNameMap names = labeller.getTypeNames();
Chris@340 1795
Chris@125 1796 QStringList options;
Chris@340 1797 std::vector<Labeller::ValueType> genopts;
Chris@125 1798
Chris@340 1799 for (Labeller::TypeNameMap::const_iterator i = names.begin();
Chris@340 1800 i != names.end(); ++i) {
Chris@340 1801 if (i->first == Labeller::ValueNone) options << tr("Zero for all items");
Chris@340 1802 else options << i->second;
Chris@340 1803 genopts.push_back(i->first);
Chris@125 1804 }
Chris@125 1805
Chris@125 1806 static int prevSelection = 0;
Chris@125 1807
Chris@125 1808 bool ok = false;
Chris@125 1809 QString selected = ListInputDialog::getItem
Chris@125 1810 (0, tr("Choose value calculation"),
Chris@125 1811 text, options, prevSelection, &ok);
Chris@125 1812
Chris@852 1813 if (!ok) {
Chris@852 1814 delete command;
Chris@852 1815 return false;
Chris@852 1816 }
Chris@125 1817 int selection = 0;
Chris@340 1818 generation = Labeller::ValueNone;
Chris@125 1819
Chris@125 1820 for (QStringList::const_iterator i = options.begin();
Chris@125 1821 i != options.end(); ++i) {
Chris@125 1822 if (selected == *i) {
Chris@340 1823 generation = genopts[selection];
Chris@125 1824 break;
Chris@125 1825 }
Chris@125 1826 ++selection;
Chris@125 1827 }
Chris@340 1828
Chris@340 1829 labeller.setType(generation);
Chris@340 1830
Chris@340 1831 if (generation == Labeller::ValueFromCyclicalCounter ||
Chris@340 1832 generation == Labeller::ValueFromTwoLevelCounter) {
Chris@616 1833 int cycleSize = QInputDialog::getInt
Chris@340 1834 (0, tr("Select cycle size"),
Chris@340 1835 tr("Cycle size:"), 4, 2, 16, 1);
Chris@340 1836 labeller.setCounterCycleSize(cycleSize);
Chris@340 1837 }
Chris@125 1838
Chris@125 1839 prevSelection = selection;
Chris@125 1840 }
Chris@125 1841 }
Chris@125 1842
Chris@372 1843 SparseTimeValueModel::Point prevPoint(0);
Chris@125 1844
Chris@76 1845 for (Clipboard::PointList::const_iterator i = points.begin();
Chris@76 1846 i != points.end(); ++i) {
Chris@76 1847
Chris@76 1848 if (!i->haveFrame()) continue;
Chris@360 1849
Chris@908 1850 sv_frame_t frame = 0;
Chris@360 1851
Chris@360 1852 if (!realign) {
Chris@360 1853
Chris@360 1854 frame = i->getFrame();
Chris@360 1855
Chris@360 1856 } else {
Chris@360 1857
Chris@360 1858 if (i->haveReferenceFrame()) {
Chris@360 1859 frame = i->getReferenceFrame();
Chris@360 1860 frame = alignFromReference(v, frame);
Chris@360 1861 } else {
Chris@360 1862 frame = i->getFrame();
Chris@360 1863 }
Chris@76 1864 }
Chris@360 1865
Chris@76 1866 SparseTimeValueModel::Point newPoint(frame);
Chris@76 1867
Chris@125 1868 if (i->haveLabel()) {
Chris@125 1869 newPoint.label = i->getLabel();
Chris@125 1870 } else if (i->haveValue()) {
Chris@125 1871 newPoint.label = QString("%1").arg(i->getValue());
Chris@125 1872 }
Chris@125 1873
Chris@372 1874 bool usePrev = false;
Chris@372 1875 SparseTimeValueModel::Point formerPrevPoint = prevPoint;
Chris@372 1876
Chris@125 1877 if (i->haveValue()) {
Chris@125 1878 newPoint.value = i->getValue();
Chris@125 1879 } else {
Chris@526 1880 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@682 1881 cerr << "Setting value on point at " << newPoint.frame << " from labeller";
Chris@526 1882 if (i == points.begin()) {
Chris@682 1883 cerr << ", no prev point" << endl;
Chris@526 1884 } else {
Chris@682 1885 cerr << ", prev point is at " << prevPoint.frame << endl;
Chris@526 1886 }
Chris@526 1887 #endif
Chris@340 1888 labeller.setValue<SparseTimeValueModel::Point>
Chris@371 1889 (newPoint, (i == points.begin()) ? 0 : &prevPoint);
Chris@526 1890 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@682 1891 cerr << "New point value = " << newPoint.value << endl;
Chris@526 1892 #endif
Chris@372 1893 if (labeller.actingOnPrevPoint() && i != points.begin()) {
Chris@372 1894 usePrev = true;
Chris@372 1895 }
Chris@372 1896 }
Chris@372 1897
Chris@372 1898 if (usePrev) {
Chris@372 1899 command->deletePoint(formerPrevPoint);
Chris@372 1900 command->addPoint(prevPoint);
Chris@340 1901 }
Chris@125 1902
Chris@340 1903 prevPoint = newPoint;
Chris@76 1904 command->addPoint(newPoint);
Chris@76 1905 }
Chris@76 1906
Chris@376 1907 finish(command);
Chris@125 1908 return true;
Chris@360 1909 }
Chris@76 1910
Chris@316 1911 void
Chris@316 1912 TimeValueLayer::toXml(QTextStream &stream,
Chris@316 1913 QString indent, QString extraAttributes) const
Chris@6 1914 {
Chris@316 1915 SingleColourLayer::toXml(stream, indent,
Chris@316 1916 extraAttributes +
Chris@553 1917 QString(" colourMap=\"%1\" plotStyle=\"%2\" verticalScale=\"%3\" scaleMinimum=\"%4\" scaleMaximum=\"%5\" drawDivisions=\"%6\" derivative=\"%7\" ")
Chris@316 1918 .arg(m_colourMap)
Chris@316 1919 .arg(m_plotStyle)
Chris@445 1920 .arg(m_verticalScale)
Chris@445 1921 .arg(m_scaleMinimum)
Chris@513 1922 .arg(m_scaleMaximum)
Chris@553 1923 .arg(m_drawSegmentDivisions ? "true" : "false")
Chris@553 1924 .arg(m_derivative ? "true" : "false"));
Chris@0 1925 }
Chris@0 1926
Chris@11 1927 void
Chris@11 1928 TimeValueLayer::setProperties(const QXmlAttributes &attributes)
Chris@11 1929 {
Chris@287 1930 SingleColourLayer::setProperties(attributes);
Chris@11 1931
Chris@445 1932 bool ok, alsoOk;
Chris@287 1933
Chris@287 1934 int cmap = attributes.value("colourMap").toInt(&ok);
Chris@287 1935 if (ok) setFillColourMap(cmap);
Chris@287 1936
Chris@11 1937 PlotStyle style = (PlotStyle)
Chris@11 1938 attributes.value("plotStyle").toInt(&ok);
Chris@11 1939 if (ok) setPlotStyle(style);
Chris@286 1940
Chris@286 1941 VerticalScale scale = (VerticalScale)
Chris@286 1942 attributes.value("verticalScale").toInt(&ok);
Chris@286 1943 if (ok) setVerticalScale(scale);
Chris@445 1944
Chris@513 1945 bool draw = (attributes.value("drawDivisions").trimmed() == "true");
Chris@513 1946 setDrawSegmentDivisions(draw);
Chris@513 1947
Chris@553 1948 bool derivative = (attributes.value("derivative").trimmed() == "true");
Chris@553 1949 setShowDerivative(derivative);
Chris@553 1950
Chris@445 1951 float min = attributes.value("scaleMinimum").toFloat(&ok);
Chris@445 1952 float max = attributes.value("scaleMaximum").toFloat(&alsoOk);
Chris@526 1953 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@682 1954 cerr << "from properties: min = " << min << ", max = " << max << endl;
Chris@526 1955 #endif
Chris@526 1956 if (ok && alsoOk && min != max) setDisplayExtents(min, max);
Chris@11 1957 }
Chris@11 1958