annotate layer/TimeValueLayer.cpp @ 947:e53a87a5efb2

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