annotate layer/TimeValueLayer.cpp @ 855:57efeb75880d

Simplify some logic where loop was used with an unconditional "break" that meant it could only happen once (from coverity scan)
author Chris Cannam
date Wed, 03 Sep 2014 12:05:45 +0100
parents 6c6e34b09688
children 33157c3f0e80
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@101 332 TimeValueLayer::getValueExtents(float &min, float &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@553 345 max = std::max(fabsf(min), fabsf(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@668 359 float 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@101 373 TimeValueLayer::getDisplayExtents(float &min, float &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@553 387 max = std::max(fabsf(min), fabsf(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@437 399 TimeValueLayer::setDisplayExtents(float min, float 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@437 441 float 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@437 464 float min, max;
Chris@437 465 bool logarithmic;
Chris@437 466 QString unit;
Chris@437 467 getValueExtents(min, max, logarithmic, unit);
Chris@437 468
Chris@437 469 float dmin, dmax;
Chris@437 470 getDisplayExtents(dmin, dmax);
Chris@437 471
Chris@437 472 float newdist = mapper->getValueForPosition(100 - step);
Chris@437 473
Chris@437 474 float newmin, newmax;
Chris@437 475
Chris@437 476 if (logarithmic) {
Chris@437 477
Chris@437 478 // see SpectrogramLayer::setVerticalZoomStep
Chris@437 479
Chris@437 480 newmax = (newdist + sqrtf(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@437 488 float 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@437 515 float 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@44 536 long 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@248 556 } else if (long(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@805 577 TimeValueLayer::getLabelPreceding(int 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@0 605 long 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@44 632 TimeValueLayer::snapToFeatureFrame(View *v, int &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@28 652 int 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@517 704 TimeValueLayer::snapToSimilarFeature(View *v, int &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@517 719 int matchframe = frame;
Chris@517 720 float matchvalue = 0.f;
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@517 728 int snapped = frame;
Chris@517 729 bool found = false;
Chris@517 730 bool distant = false;
Chris@517 731 float 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@517 756 fabsf(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@517 765 if (fabsf(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@101 785 TimeValueLayer::getScaleExtents(View *v, float &min, float &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@66 821 TimeValueLayer::getYForValue(View *v, float val) const
Chris@21 822 {
Chris@79 823 float 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@21 841 float
Chris@44 842 TimeValueLayer::getValueForY(View *v, int y) const
Chris@21 843 {
Chris@101 844 float 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@101 850 float val = min + (float(h - y) * float(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@101 868 TimeValueLayer::getColourForValue(View *v, float val) const
Chris@68 869 {
Chris@101 870 float 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@0 903 int 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@44 911 long frame0 = v->getFrameForX(x0);
Chris@44 912 long 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@0 930 float min = m_model->getValueMinimum();
Chris@0 931 float 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@0 938 long 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@615 977 int 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@553 986 float 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@721 1015 float nvalue = 0.f;
Chris@615 1016 int 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@55 1125 float x0 = x + float(w)/2;
Chris@55 1126 float x1 = nx + float(w)/2;
Chris@55 1127
Chris@55 1128 float y0 = y;
Chris@55 1129 float 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@55 1154 path.lineTo((x0 + x1) / 2, (y0 + y1) / 2);
Chris@6 1155 }
Chris@3 1156 }
Chris@0 1157 }
Chris@0 1158 }
Chris@0 1159
Chris@26 1160 if (m_plotStyle == PlotSegmentation) {
Chris@76 1161
Chris@526 1162 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@682 1163 cerr << "drawing rect" << endl;
Chris@526 1164 #endif
Chris@26 1165
Chris@27 1166 if (nx <= x) continue;
Chris@26 1167
Chris@559 1168 paint.setPen(QPen(getForegroundQColor(v), 2));
Chris@559 1169
Chris@631 1170 if (!illuminate) {
Chris@513 1171 if (!m_drawSegmentDivisions ||
Chris@513 1172 nx < x + 5 ||
Chris@513 1173 x >= v->width() - 1) {
Chris@513 1174 paint.setPen(Qt::NoPen);
Chris@513 1175 }
Chris@27 1176 }
Chris@26 1177
Chris@44 1178 paint.drawRect(x, -1, nx - x, v->height() + 1);
Chris@26 1179 }
Chris@26 1180
Chris@741 1181 if (v->shouldShowFeatureLabels()) {
Chris@629 1182
Chris@741 1183 QString label = p.label;
Chris@741 1184 bool italic = false;
Chris@741 1185
Chris@741 1186 if (label == "" &&
Chris@741 1187 (m_plotStyle == PlotPoints ||
Chris@741 1188 m_plotStyle == PlotSegmentation ||
Chris@741 1189 m_plotStyle == PlotConnectedPoints)) {
Chris@741 1190 char lc[20];
Chris@741 1191 snprintf(lc, 20, "%.3g", p.value);
Chris@741 1192 label = lc;
Chris@741 1193 italic = true;
Chris@741 1194 }
Chris@741 1195
Chris@741 1196 if (label != "") {
Chris@741 1197 // Quick test for 20px before we do the slower test using metrics
Chris@741 1198 bool haveRoom = (nx > x + 20);
Chris@741 1199 haveRoom = (haveRoom &&
Chris@741 1200 (nx > x + 6 + paint.fontMetrics().width(label)));
Chris@741 1201 if (haveRoom ||
Chris@741 1202 (!haveNext &&
Chris@741 1203 (pointCount == 0 || !italic))) {
Chris@741 1204 v->drawVisibleText(paint, x + 5, textY, label,
Chris@741 1205 italic ?
Chris@741 1206 View::OutlinedItalicText :
Chris@741 1207 View::OutlinedText);
Chris@741 1208 }
Chris@741 1209 }
Chris@629 1210 }
Chris@629 1211
Chris@615 1212 prevFrame = p.frame;
Chris@632 1213 ++pointCount;
Chris@0 1214 }
Chris@6 1215
Chris@691 1216 if (m_plotStyle == PlotDiscreteCurves) {
Chris@691 1217 paint.setRenderHint(QPainter::Antialiasing, true);
Chris@691 1218 paint.drawPath(path);
Chris@691 1219 } else if ((m_plotStyle == PlotCurve || m_plotStyle == PlotLines)
Chris@691 1220 && !path.isEmpty()) {
Chris@55 1221 paint.setRenderHint(QPainter::Antialiasing, pointCount <= v->width());
Chris@6 1222 paint.drawPath(path);
Chris@6 1223 }
Chris@6 1224
Chris@6 1225 paint.restore();
Chris@6 1226
Chris@6 1227 // looks like save/restore doesn't deal with this:
Chris@6 1228 paint.setRenderHint(QPainter::Antialiasing, false);
Chris@6 1229 }
Chris@6 1230
Chris@42 1231 int
Chris@698 1232 TimeValueLayer::getVerticalScaleWidth(View *v, bool, QPainter &paint) const
Chris@42 1233 {
Chris@701 1234 if (!m_model || shouldAutoAlign()) {
Chris@701 1235 return 0;
Chris@701 1236 } else if (m_plotStyle == PlotSegmentation) {
Chris@699 1237 if (m_verticalScale == LogScale) {
Chris@699 1238 return LogColourScale().getWidth(v, paint);
Chris@699 1239 } else {
Chris@699 1240 return LinearColourScale().getWidth(v, paint);
Chris@699 1241 }
Chris@698 1242 } else {
Chris@699 1243 if (m_verticalScale == LogScale) {
Chris@699 1244 return LogNumericalScale().getWidth(v, paint) + 10; // for piano
Chris@699 1245 } else {
Chris@699 1246 return LinearNumericalScale().getWidth(v, paint);
Chris@699 1247 }
Chris@698 1248 }
Chris@42 1249 }
Chris@42 1250
Chris@42 1251 void
Chris@607 1252 TimeValueLayer::paintVerticalScale(View *v, bool, QPainter &paint, QRect) const
Chris@42 1253 {
Chris@717 1254 if (!m_model || m_model->getPoints().empty()) return;
Chris@42 1255
Chris@699 1256 QString unit;
Chris@698 1257 float min, max;
Chris@698 1258 bool logarithmic;
Chris@698 1259
Chris@698 1260 int w = getVerticalScaleWidth(v, false, paint);
Chris@698 1261 int h = v->height();
Chris@698 1262
Chris@698 1263 if (m_plotStyle == PlotSegmentation) {
Chris@698 1264
Chris@699 1265 getValueExtents(min, max, logarithmic, unit);
Chris@699 1266
Chris@699 1267 if (logarithmic) {
Chris@699 1268 LogRange::mapRange(min, max);
Chris@699 1269 LogColourScale().paintVertical(v, this, paint, 0, min, max);
Chris@699 1270 } else {
Chris@699 1271 LinearColourScale().paintVertical(v, this, paint, 0, min, max);
Chris@699 1272 }
Chris@698 1273
Chris@698 1274 } else {
Chris@698 1275
Chris@699 1276 getScaleExtents(v, min, max, logarithmic);
Chris@699 1277
Chris@698 1278 if (logarithmic) {
Chris@698 1279 LogNumericalScale().paintVertical(v, this, paint, 0, min, max);
Chris@698 1280 } else {
Chris@698 1281 LinearNumericalScale().paintVertical(v, this, paint, 0, min, max);
Chris@698 1282 }
Chris@698 1283
Chris@698 1284 if (logarithmic && (getScaleUnits() == "Hz")) {
Chris@698 1285 PianoScale().paintPianoVertical
Chris@698 1286 (v, paint, QRect(w - 10, 0, 10, h),
Chris@698 1287 LogRange::unmap(min),
Chris@698 1288 LogRange::unmap(max));
Chris@698 1289 paint.drawLine(w, 0, w, h);
Chris@698 1290 }
Chris@698 1291 }
Chris@698 1292
Chris@698 1293 if (getScaleUnits() != "") {
Chris@701 1294 int mw = w - 5;
Chris@701 1295 paint.drawText(5,
Chris@701 1296 5 + paint.fontMetrics().ascent(),
Chris@701 1297 TextAbbrev::abbreviate(getScaleUnits(),
Chris@701 1298 paint.fontMetrics(),
Chris@701 1299 mw));
Chris@691 1300 }
Chris@42 1301 }
Chris@42 1302
Chris@21 1303 void
Chris@44 1304 TimeValueLayer::drawStart(View *v, QMouseEvent *e)
Chris@21 1305 {
Chris@526 1306 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@682 1307 cerr << "TimeValueLayer::drawStart(" << e->x() << "," << e->y() << ")" << endl;
Chris@526 1308 #endif
Chris@21 1309
Chris@21 1310 if (!m_model) return;
Chris@21 1311
Chris@44 1312 long frame = v->getFrameForX(e->x());
Chris@76 1313 long resolution = m_model->getResolution();
Chris@21 1314 if (frame < 0) frame = 0;
Chris@76 1315 frame = (frame / resolution) * resolution;
Chris@21 1316
Chris@44 1317 float value = getValueForY(v, e->y());
Chris@21 1318
Chris@76 1319 bool havePoint = false;
Chris@76 1320
Chris@76 1321 SparseTimeValueModel::PointList points = getLocalPoints(v, e->x());
Chris@76 1322 if (!points.empty()) {
Chris@76 1323 for (SparseTimeValueModel::PointList::iterator i = points.begin();
Chris@76 1324 i != points.end(); ++i) {
Chris@76 1325 if (((i->frame / resolution) * resolution) != frame) {
Chris@526 1326 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@682 1327 cerr << "ignoring out-of-range frame at " << i->frame << endl;
Chris@526 1328 #endif
Chris@76 1329 continue;
Chris@76 1330 }
Chris@76 1331 m_editingPoint = *i;
Chris@76 1332 havePoint = true;
Chris@76 1333 }
Chris@76 1334 }
Chris@76 1335
Chris@76 1336 if (!havePoint) {
Chris@76 1337 m_editingPoint = SparseTimeValueModel::Point
Chris@76 1338 (frame, value, tr("New Point"));
Chris@76 1339 }
Chris@76 1340
Chris@23 1341 m_originalPoint = m_editingPoint;
Chris@22 1342
Chris@376 1343 if (m_editingCommand) finish(m_editingCommand);
Chris@22 1344 m_editingCommand = new SparseTimeValueModel::EditCommand(m_model,
Chris@22 1345 tr("Draw Point"));
Chris@76 1346 if (!havePoint) {
Chris@76 1347 m_editingCommand->addPoint(m_editingPoint);
Chris@76 1348 }
Chris@22 1349
Chris@21 1350 m_editing = true;
Chris@21 1351 }
Chris@21 1352
Chris@21 1353 void
Chris@44 1354 TimeValueLayer::drawDrag(View *v, QMouseEvent *e)
Chris@21 1355 {
Chris@526 1356 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@682 1357 cerr << "TimeValueLayer::drawDrag(" << e->x() << "," << e->y() << ")" << endl;
Chris@526 1358 #endif
Chris@21 1359
Chris@21 1360 if (!m_model || !m_editing) return;
Chris@21 1361
Chris@44 1362 long frame = v->getFrameForX(e->x());
Chris@76 1363 long resolution = m_model->getResolution();
Chris@21 1364 if (frame < 0) frame = 0;
Chris@76 1365 frame = (frame / resolution) * resolution;
Chris@21 1366
Chris@44 1367 float value = getValueForY(v, e->y());
Chris@21 1368
Chris@76 1369 SparseTimeValueModel::PointList points = getLocalPoints(v, e->x());
Chris@76 1370
Chris@526 1371 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@682 1372 cerr << points.size() << " points" << endl;
Chris@526 1373 #endif
Chris@76 1374
Chris@76 1375 bool havePoint = false;
Chris@76 1376
Chris@76 1377 if (!points.empty()) {
Chris@76 1378 for (SparseTimeValueModel::PointList::iterator i = points.begin();
Chris@76 1379 i != points.end(); ++i) {
Chris@76 1380 if (i->frame == m_editingPoint.frame &&
Chris@76 1381 i->value == m_editingPoint.value) {
Chris@526 1382 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@682 1383 cerr << "ignoring current editing point at " << i->frame << ", " << i->value << endl;
Chris@526 1384 #endif
Chris@76 1385 continue;
Chris@76 1386 }
Chris@76 1387 if (((i->frame / resolution) * resolution) != frame) {
Chris@526 1388 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@682 1389 cerr << "ignoring out-of-range frame at " << i->frame << endl;
Chris@526 1390 #endif
Chris@76 1391 continue;
Chris@76 1392 }
Chris@526 1393 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@682 1394 cerr << "adjusting to new point at " << i->frame << ", " << i->value << endl;
Chris@526 1395 #endif
Chris@76 1396 m_editingPoint = *i;
Chris@76 1397 m_originalPoint = m_editingPoint;
Chris@76 1398 m_editingCommand->deletePoint(m_editingPoint);
Chris@76 1399 havePoint = true;
Chris@76 1400 }
Chris@76 1401 }
Chris@76 1402
Chris@76 1403 if (!havePoint) {
Chris@76 1404 if (frame == m_editingPoint.frame) {
Chris@76 1405 m_editingCommand->deletePoint(m_editingPoint);
Chris@76 1406 }
Chris@76 1407 }
Chris@76 1408
Chris@76 1409 // m_editingCommand->deletePoint(m_editingPoint);
Chris@21 1410 m_editingPoint.frame = frame;
Chris@21 1411 m_editingPoint.value = value;
Chris@22 1412 m_editingCommand->addPoint(m_editingPoint);
Chris@21 1413 }
Chris@21 1414
Chris@21 1415 void
Chris@248 1416 TimeValueLayer::drawEnd(View *, QMouseEvent *)
Chris@21 1417 {
Chris@526 1418 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@682 1419 cerr << "TimeValueLayer::drawEnd" << endl;
Chris@526 1420 #endif
Chris@21 1421 if (!m_model || !m_editing) return;
Chris@376 1422 finish(m_editingCommand);
Chris@22 1423 m_editingCommand = 0;
Chris@21 1424 m_editing = false;
Chris@21 1425 }
Chris@21 1426
Chris@21 1427 void
Chris@335 1428 TimeValueLayer::eraseStart(View *v, QMouseEvent *e)
Chris@335 1429 {
Chris@335 1430 if (!m_model) return;
Chris@335 1431
Chris@335 1432 SparseTimeValueModel::PointList points = getLocalPoints(v, e->x());
Chris@335 1433 if (points.empty()) return;
Chris@335 1434
Chris@335 1435 m_editingPoint = *points.begin();
Chris@335 1436
Chris@335 1437 if (m_editingCommand) {
Chris@376 1438 finish(m_editingCommand);
Chris@335 1439 m_editingCommand = 0;
Chris@335 1440 }
Chris@335 1441
Chris@335 1442 m_editing = true;
Chris@335 1443 }
Chris@335 1444
Chris@335 1445 void
Chris@805 1446 TimeValueLayer::eraseDrag(View *, QMouseEvent *)
Chris@335 1447 {
Chris@335 1448 }
Chris@335 1449
Chris@335 1450 void
Chris@335 1451 TimeValueLayer::eraseEnd(View *v, QMouseEvent *e)
Chris@335 1452 {
Chris@335 1453 if (!m_model || !m_editing) return;
Chris@335 1454
Chris@335 1455 m_editing = false;
Chris@335 1456
Chris@335 1457 SparseTimeValueModel::PointList points = getLocalPoints(v, e->x());
Chris@335 1458 if (points.empty()) return;
Chris@335 1459 if (points.begin()->frame != m_editingPoint.frame ||
Chris@335 1460 points.begin()->value != m_editingPoint.value) return;
Chris@335 1461
Chris@335 1462 m_editingCommand = new SparseTimeValueModel::EditCommand
Chris@335 1463 (m_model, tr("Erase Point"));
Chris@335 1464
Chris@335 1465 m_editingCommand->deletePoint(m_editingPoint);
Chris@335 1466
Chris@376 1467 finish(m_editingCommand);
Chris@335 1468 m_editingCommand = 0;
Chris@335 1469 m_editing = false;
Chris@335 1470 }
Chris@335 1471
Chris@335 1472 void
Chris@44 1473 TimeValueLayer::editStart(View *v, QMouseEvent *e)
Chris@21 1474 {
Chris@526 1475 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@682 1476 cerr << "TimeValueLayer::editStart(" << e->x() << "," << e->y() << ")" << endl;
Chris@526 1477 #endif
Chris@21 1478
Chris@21 1479 if (!m_model) return;
Chris@21 1480
Chris@44 1481 SparseTimeValueModel::PointList points = getLocalPoints(v, e->x());
Chris@21 1482 if (points.empty()) return;
Chris@21 1483
Chris@21 1484 m_editingPoint = *points.begin();
Chris@23 1485 m_originalPoint = m_editingPoint;
Chris@22 1486
Chris@22 1487 if (m_editingCommand) {
Chris@376 1488 finish(m_editingCommand);
Chris@22 1489 m_editingCommand = 0;
Chris@22 1490 }
Chris@22 1491
Chris@21 1492 m_editing = true;
Chris@21 1493 }
Chris@21 1494
Chris@21 1495 void
Chris@44 1496 TimeValueLayer::editDrag(View *v, QMouseEvent *e)
Chris@21 1497 {
Chris@526 1498 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@682 1499 cerr << "TimeValueLayer::editDrag(" << e->x() << "," << e->y() << ")" << endl;
Chris@526 1500 #endif
Chris@21 1501
Chris@21 1502 if (!m_model || !m_editing) return;
Chris@21 1503
Chris@44 1504 long frame = v->getFrameForX(e->x());
Chris@21 1505 if (frame < 0) frame = 0;
Chris@21 1506 frame = frame / m_model->getResolution() * m_model->getResolution();
Chris@21 1507
Chris@44 1508 float value = getValueForY(v, e->y());
Chris@21 1509
Chris@22 1510 if (!m_editingCommand) {
Chris@22 1511 m_editingCommand = new SparseTimeValueModel::EditCommand(m_model,
Chris@22 1512 tr("Drag Point"));
Chris@22 1513 }
Chris@22 1514
Chris@22 1515 m_editingCommand->deletePoint(m_editingPoint);
Chris@21 1516 m_editingPoint.frame = frame;
Chris@21 1517 m_editingPoint.value = value;
Chris@22 1518 m_editingCommand->addPoint(m_editingPoint);
Chris@21 1519 }
Chris@21 1520
Chris@21 1521 void
Chris@248 1522 TimeValueLayer::editEnd(View *, QMouseEvent *)
Chris@21 1523 {
Chris@526 1524 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@682 1525 cerr << "TimeValueLayer::editEnd" << endl;
Chris@526 1526 #endif
Chris@21 1527 if (!m_model || !m_editing) return;
Chris@23 1528
Chris@23 1529 if (m_editingCommand) {
Chris@23 1530
Chris@23 1531 QString newName = m_editingCommand->getName();
Chris@23 1532
Chris@23 1533 if (m_editingPoint.frame != m_originalPoint.frame) {
Chris@23 1534 if (m_editingPoint.value != m_originalPoint.value) {
Chris@23 1535 newName = tr("Edit Point");
Chris@23 1536 } else {
Chris@23 1537 newName = tr("Relocate Point");
Chris@23 1538 }
Chris@23 1539 } else {
Chris@23 1540 newName = tr("Change Point Value");
Chris@23 1541 }
Chris@23 1542
Chris@23 1543 m_editingCommand->setName(newName);
Chris@376 1544 finish(m_editingCommand);
Chris@23 1545 }
Chris@23 1546
Chris@22 1547 m_editingCommand = 0;
Chris@21 1548 m_editing = false;
Chris@21 1549 }
Chris@21 1550
Chris@255 1551 bool
Chris@70 1552 TimeValueLayer::editOpen(View *v, QMouseEvent *e)
Chris@70 1553 {
Chris@255 1554 if (!m_model) return false;
Chris@70 1555
Chris@70 1556 SparseTimeValueModel::PointList points = getLocalPoints(v, e->x());
Chris@255 1557 if (points.empty()) return false;
Chris@70 1558
Chris@70 1559 SparseTimeValueModel::Point point = *points.begin();
Chris@70 1560
Chris@70 1561 ItemEditDialog *dialog = new ItemEditDialog
Chris@70 1562 (m_model->getSampleRate(),
Chris@70 1563 ItemEditDialog::ShowTime |
Chris@70 1564 ItemEditDialog::ShowValue |
Chris@73 1565 ItemEditDialog::ShowText,
Chris@698 1566 getScaleUnits());
Chris@70 1567
Chris@70 1568 dialog->setFrameTime(point.frame);
Chris@70 1569 dialog->setValue(point.value);
Chris@70 1570 dialog->setText(point.label);
Chris@70 1571
Chris@70 1572 if (dialog->exec() == QDialog::Accepted) {
Chris@70 1573
Chris@70 1574 SparseTimeValueModel::Point newPoint = point;
Chris@70 1575 newPoint.frame = dialog->getFrameTime();
Chris@70 1576 newPoint.value = dialog->getValue();
Chris@70 1577 newPoint.label = dialog->getText();
Chris@70 1578
Chris@70 1579 SparseTimeValueModel::EditCommand *command =
Chris@70 1580 new SparseTimeValueModel::EditCommand(m_model, tr("Edit Point"));
Chris@70 1581 command->deletePoint(point);
Chris@70 1582 command->addPoint(newPoint);
Chris@376 1583 finish(command);
Chris@70 1584 }
Chris@70 1585
Chris@70 1586 delete dialog;
Chris@255 1587 return true;
Chris@70 1588 }
Chris@70 1589
Chris@70 1590 void
Chris@805 1591 TimeValueLayer::moveSelection(Selection s, int newStartFrame)
Chris@43 1592 {
Chris@99 1593 if (!m_model) return;
Chris@99 1594
Chris@43 1595 SparseTimeValueModel::EditCommand *command =
Chris@43 1596 new SparseTimeValueModel::EditCommand(m_model,
Chris@43 1597 tr("Drag Selection"));
Chris@43 1598
Chris@43 1599 SparseTimeValueModel::PointList points =
Chris@43 1600 m_model->getPoints(s.getStartFrame(), s.getEndFrame());
Chris@43 1601
Chris@43 1602 for (SparseTimeValueModel::PointList::iterator i = points.begin();
Chris@43 1603 i != points.end(); ++i) {
Chris@43 1604
Chris@43 1605 if (s.contains(i->frame)) {
Chris@43 1606 SparseTimeValueModel::Point newPoint(*i);
Chris@43 1607 newPoint.frame = i->frame + newStartFrame - s.getStartFrame();
Chris@43 1608 command->deletePoint(*i);
Chris@43 1609 command->addPoint(newPoint);
Chris@43 1610 }
Chris@43 1611 }
Chris@43 1612
Chris@376 1613 finish(command);
Chris@43 1614 }
Chris@43 1615
Chris@43 1616 void
Chris@43 1617 TimeValueLayer::resizeSelection(Selection s, Selection newSize)
Chris@43 1618 {
Chris@99 1619 if (!m_model) return;
Chris@99 1620
Chris@43 1621 SparseTimeValueModel::EditCommand *command =
Chris@43 1622 new SparseTimeValueModel::EditCommand(m_model,
Chris@43 1623 tr("Resize Selection"));
Chris@43 1624
Chris@43 1625 SparseTimeValueModel::PointList points =
Chris@43 1626 m_model->getPoints(s.getStartFrame(), s.getEndFrame());
Chris@43 1627
Chris@43 1628 double ratio =
Chris@43 1629 double(newSize.getEndFrame() - newSize.getStartFrame()) /
Chris@43 1630 double(s.getEndFrame() - s.getStartFrame());
Chris@43 1631
Chris@43 1632 for (SparseTimeValueModel::PointList::iterator i = points.begin();
Chris@43 1633 i != points.end(); ++i) {
Chris@43 1634
Chris@43 1635 if (s.contains(i->frame)) {
Chris@43 1636
Chris@43 1637 double target = i->frame;
Chris@43 1638 target = newSize.getStartFrame() +
Chris@43 1639 double(target - s.getStartFrame()) * ratio;
Chris@43 1640
Chris@43 1641 SparseTimeValueModel::Point newPoint(*i);
Chris@43 1642 newPoint.frame = lrint(target);
Chris@43 1643 command->deletePoint(*i);
Chris@43 1644 command->addPoint(newPoint);
Chris@43 1645 }
Chris@43 1646 }
Chris@43 1647
Chris@376 1648 finish(command);
Chris@43 1649 }
Chris@43 1650
Chris@76 1651 void
Chris@76 1652 TimeValueLayer::deleteSelection(Selection s)
Chris@76 1653 {
Chris@99 1654 if (!m_model) return;
Chris@99 1655
Chris@76 1656 SparseTimeValueModel::EditCommand *command =
Chris@76 1657 new SparseTimeValueModel::EditCommand(m_model,
Chris@76 1658 tr("Delete Selected Points"));
Chris@76 1659
Chris@76 1660 SparseTimeValueModel::PointList points =
Chris@76 1661 m_model->getPoints(s.getStartFrame(), s.getEndFrame());
Chris@76 1662
Chris@76 1663 for (SparseTimeValueModel::PointList::iterator i = points.begin();
Chris@76 1664 i != points.end(); ++i) {
Chris@76 1665
Chris@76 1666 if (s.contains(i->frame)) {
Chris@76 1667 command->deletePoint(*i);
Chris@76 1668 }
Chris@76 1669 }
Chris@76 1670
Chris@376 1671 finish(command);
Chris@76 1672 }
Chris@76 1673
Chris@76 1674 void
Chris@359 1675 TimeValueLayer::copy(View *v, Selection s, Clipboard &to)
Chris@76 1676 {
Chris@99 1677 if (!m_model) return;
Chris@99 1678
Chris@76 1679 SparseTimeValueModel::PointList points =
Chris@76 1680 m_model->getPoints(s.getStartFrame(), s.getEndFrame());
Chris@76 1681
Chris@76 1682 for (SparseTimeValueModel::PointList::iterator i = points.begin();
Chris@76 1683 i != points.end(); ++i) {
Chris@76 1684 if (s.contains(i->frame)) {
Chris@83 1685 Clipboard::Point point(i->frame, i->value, i->label);
Chris@360 1686 point.setReferenceFrame(alignToReference(v, i->frame));
Chris@76 1687 to.addPoint(point);
Chris@76 1688 }
Chris@76 1689 }
Chris@76 1690 }
Chris@76 1691
Chris@125 1692 bool
Chris@805 1693 TimeValueLayer::paste(View *v, const Clipboard &from, int /* frameOffset */,
Chris@125 1694 bool interactive)
Chris@76 1695 {
Chris@125 1696 if (!m_model) return false;
Chris@99 1697
Chris@76 1698 const Clipboard::PointList &points = from.getPoints();
Chris@76 1699
Chris@360 1700 bool realign = false;
Chris@360 1701
Chris@360 1702 if (clipboardHasDifferentAlignment(v, from)) {
Chris@360 1703
Chris@360 1704 QMessageBox::StandardButton button =
Chris@360 1705 QMessageBox::question(v, tr("Re-align pasted items?"),
Chris@360 1706 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 1707 QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel,
Chris@360 1708 QMessageBox::Yes);
Chris@360 1709
Chris@360 1710 if (button == QMessageBox::Cancel) {
Chris@360 1711 return false;
Chris@360 1712 }
Chris@360 1713
Chris@360 1714 if (button == QMessageBox::Yes) {
Chris@360 1715 realign = true;
Chris@360 1716 }
Chris@360 1717 }
Chris@360 1718
Chris@76 1719 SparseTimeValueModel::EditCommand *command =
Chris@76 1720 new SparseTimeValueModel::EditCommand(m_model, tr("Paste"));
Chris@76 1721
Chris@125 1722 enum ValueAvailability {
Chris@125 1723 UnknownAvailability,
Chris@125 1724 NoValues,
Chris@125 1725 SomeValues,
Chris@125 1726 AllValues
Chris@125 1727 };
Chris@125 1728
Chris@340 1729 Labeller::ValueType generation = Labeller::ValueNone;
Chris@125 1730
Chris@125 1731 bool haveUsableLabels = false;
Chris@340 1732 Labeller labeller;
Chris@340 1733 labeller.setSampleRate(m_model->getSampleRate());
Chris@125 1734
Chris@125 1735 if (interactive) {
Chris@125 1736
Chris@125 1737 ValueAvailability availability = UnknownAvailability;
Chris@125 1738
Chris@125 1739 for (Clipboard::PointList::const_iterator i = points.begin();
Chris@125 1740 i != points.end(); ++i) {
Chris@125 1741
Chris@125 1742 if (!i->haveFrame()) continue;
Chris@125 1743
Chris@125 1744 if (availability == UnknownAvailability) {
Chris@125 1745 if (i->haveValue()) availability = AllValues;
Chris@125 1746 else availability = NoValues;
Chris@125 1747 continue;
Chris@125 1748 }
Chris@125 1749
Chris@125 1750 if (i->haveValue()) {
Chris@125 1751 if (availability == NoValues) {
Chris@125 1752 availability = SomeValues;
Chris@125 1753 }
Chris@125 1754 } else {
Chris@125 1755 if (availability == AllValues) {
Chris@125 1756 availability = SomeValues;
Chris@125 1757 }
Chris@125 1758 }
Chris@125 1759
Chris@125 1760 if (!haveUsableLabels) {
Chris@125 1761 if (i->haveLabel()) {
Chris@125 1762 if (i->getLabel().contains(QRegExp("[0-9]"))) {
Chris@125 1763 haveUsableLabels = true;
Chris@125 1764 }
Chris@125 1765 }
Chris@125 1766 }
Chris@125 1767
Chris@125 1768 if (availability == SomeValues && haveUsableLabels) break;
Chris@125 1769 }
Chris@125 1770
Chris@125 1771 if (availability == NoValues || availability == SomeValues) {
Chris@125 1772
Chris@125 1773 QString text;
Chris@125 1774 if (availability == NoValues) {
Chris@125 1775 text = tr("The items you are pasting do not have values.\nWhat values do you want to use for these items?");
Chris@125 1776 } else {
Chris@125 1777 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 1778 }
Chris@125 1779
Chris@340 1780 Labeller::TypeNameMap names = labeller.getTypeNames();
Chris@340 1781
Chris@125 1782 QStringList options;
Chris@340 1783 std::vector<Labeller::ValueType> genopts;
Chris@125 1784
Chris@340 1785 for (Labeller::TypeNameMap::const_iterator i = names.begin();
Chris@340 1786 i != names.end(); ++i) {
Chris@340 1787 if (i->first == Labeller::ValueNone) options << tr("Zero for all items");
Chris@340 1788 else options << i->second;
Chris@340 1789 genopts.push_back(i->first);
Chris@125 1790 }
Chris@125 1791
Chris@125 1792 static int prevSelection = 0;
Chris@125 1793
Chris@125 1794 bool ok = false;
Chris@125 1795 QString selected = ListInputDialog::getItem
Chris@125 1796 (0, tr("Choose value calculation"),
Chris@125 1797 text, options, prevSelection, &ok);
Chris@125 1798
Chris@852 1799 if (!ok) {
Chris@852 1800 delete command;
Chris@852 1801 return false;
Chris@852 1802 }
Chris@125 1803 int selection = 0;
Chris@340 1804 generation = Labeller::ValueNone;
Chris@125 1805
Chris@125 1806 for (QStringList::const_iterator i = options.begin();
Chris@125 1807 i != options.end(); ++i) {
Chris@125 1808 if (selected == *i) {
Chris@340 1809 generation = genopts[selection];
Chris@125 1810 break;
Chris@125 1811 }
Chris@125 1812 ++selection;
Chris@125 1813 }
Chris@340 1814
Chris@340 1815 labeller.setType(generation);
Chris@340 1816
Chris@340 1817 if (generation == Labeller::ValueFromCyclicalCounter ||
Chris@340 1818 generation == Labeller::ValueFromTwoLevelCounter) {
Chris@616 1819 int cycleSize = QInputDialog::getInt
Chris@340 1820 (0, tr("Select cycle size"),
Chris@340 1821 tr("Cycle size:"), 4, 2, 16, 1);
Chris@340 1822 labeller.setCounterCycleSize(cycleSize);
Chris@340 1823 }
Chris@125 1824
Chris@125 1825 prevSelection = selection;
Chris@125 1826 }
Chris@125 1827 }
Chris@125 1828
Chris@372 1829 SparseTimeValueModel::Point prevPoint(0);
Chris@125 1830
Chris@76 1831 for (Clipboard::PointList::const_iterator i = points.begin();
Chris@76 1832 i != points.end(); ++i) {
Chris@76 1833
Chris@76 1834 if (!i->haveFrame()) continue;
Chris@360 1835
Chris@805 1836 int frame = 0;
Chris@360 1837
Chris@360 1838 if (!realign) {
Chris@360 1839
Chris@360 1840 frame = i->getFrame();
Chris@360 1841
Chris@360 1842 } else {
Chris@360 1843
Chris@360 1844 if (i->haveReferenceFrame()) {
Chris@360 1845 frame = i->getReferenceFrame();
Chris@360 1846 frame = alignFromReference(v, frame);
Chris@360 1847 } else {
Chris@360 1848 frame = i->getFrame();
Chris@360 1849 }
Chris@76 1850 }
Chris@360 1851
Chris@76 1852 SparseTimeValueModel::Point newPoint(frame);
Chris@76 1853
Chris@125 1854 if (i->haveLabel()) {
Chris@125 1855 newPoint.label = i->getLabel();
Chris@125 1856 } else if (i->haveValue()) {
Chris@125 1857 newPoint.label = QString("%1").arg(i->getValue());
Chris@125 1858 }
Chris@125 1859
Chris@372 1860 bool usePrev = false;
Chris@372 1861 SparseTimeValueModel::Point formerPrevPoint = prevPoint;
Chris@372 1862
Chris@125 1863 if (i->haveValue()) {
Chris@125 1864 newPoint.value = i->getValue();
Chris@125 1865 } else {
Chris@526 1866 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@682 1867 cerr << "Setting value on point at " << newPoint.frame << " from labeller";
Chris@526 1868 if (i == points.begin()) {
Chris@682 1869 cerr << ", no prev point" << endl;
Chris@526 1870 } else {
Chris@682 1871 cerr << ", prev point is at " << prevPoint.frame << endl;
Chris@526 1872 }
Chris@526 1873 #endif
Chris@340 1874 labeller.setValue<SparseTimeValueModel::Point>
Chris@371 1875 (newPoint, (i == points.begin()) ? 0 : &prevPoint);
Chris@526 1876 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@682 1877 cerr << "New point value = " << newPoint.value << endl;
Chris@526 1878 #endif
Chris@372 1879 if (labeller.actingOnPrevPoint() && i != points.begin()) {
Chris@372 1880 usePrev = true;
Chris@372 1881 }
Chris@372 1882 }
Chris@372 1883
Chris@372 1884 if (usePrev) {
Chris@372 1885 command->deletePoint(formerPrevPoint);
Chris@372 1886 command->addPoint(prevPoint);
Chris@340 1887 }
Chris@125 1888
Chris@340 1889 prevPoint = newPoint;
Chris@76 1890 command->addPoint(newPoint);
Chris@76 1891 }
Chris@76 1892
Chris@376 1893 finish(command);
Chris@125 1894 return true;
Chris@360 1895 }
Chris@76 1896
Chris@316 1897 void
Chris@316 1898 TimeValueLayer::toXml(QTextStream &stream,
Chris@316 1899 QString indent, QString extraAttributes) const
Chris@6 1900 {
Chris@316 1901 SingleColourLayer::toXml(stream, indent,
Chris@316 1902 extraAttributes +
Chris@553 1903 QString(" colourMap=\"%1\" plotStyle=\"%2\" verticalScale=\"%3\" scaleMinimum=\"%4\" scaleMaximum=\"%5\" drawDivisions=\"%6\" derivative=\"%7\" ")
Chris@316 1904 .arg(m_colourMap)
Chris@316 1905 .arg(m_plotStyle)
Chris@445 1906 .arg(m_verticalScale)
Chris@445 1907 .arg(m_scaleMinimum)
Chris@513 1908 .arg(m_scaleMaximum)
Chris@553 1909 .arg(m_drawSegmentDivisions ? "true" : "false")
Chris@553 1910 .arg(m_derivative ? "true" : "false"));
Chris@0 1911 }
Chris@0 1912
Chris@11 1913 void
Chris@11 1914 TimeValueLayer::setProperties(const QXmlAttributes &attributes)
Chris@11 1915 {
Chris@287 1916 SingleColourLayer::setProperties(attributes);
Chris@11 1917
Chris@445 1918 bool ok, alsoOk;
Chris@287 1919
Chris@287 1920 int cmap = attributes.value("colourMap").toInt(&ok);
Chris@287 1921 if (ok) setFillColourMap(cmap);
Chris@287 1922
Chris@11 1923 PlotStyle style = (PlotStyle)
Chris@11 1924 attributes.value("plotStyle").toInt(&ok);
Chris@11 1925 if (ok) setPlotStyle(style);
Chris@286 1926
Chris@286 1927 VerticalScale scale = (VerticalScale)
Chris@286 1928 attributes.value("verticalScale").toInt(&ok);
Chris@286 1929 if (ok) setVerticalScale(scale);
Chris@445 1930
Chris@513 1931 bool draw = (attributes.value("drawDivisions").trimmed() == "true");
Chris@513 1932 setDrawSegmentDivisions(draw);
Chris@513 1933
Chris@553 1934 bool derivative = (attributes.value("derivative").trimmed() == "true");
Chris@553 1935 setShowDerivative(derivative);
Chris@553 1936
Chris@445 1937 float min = attributes.value("scaleMinimum").toFloat(&ok);
Chris@445 1938 float max = attributes.value("scaleMaximum").toFloat(&alsoOk);
Chris@526 1939 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@682 1940 cerr << "from properties: min = " << min << ", max = " << max << endl;
Chris@526 1941 #endif
Chris@526 1942 if (ok && alsoOk && min != max) setDisplayExtents(min, max);
Chris@11 1943 }
Chris@11 1944