annotate layer/TimeValueLayer.cpp @ 1024:3bce4c45b681 spectrogram-minor-refactor

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