annotate layer/TimeValueLayer.cpp @ 789:9fd1bdf214dd tonioni

Play pointer: when user drags pane during playback such that the pointer is no longer visible, accept that and stop trying to track it until pointer naturally comes back within visible area
author Chris Cannam
date Thu, 12 Jun 2014 12:48:11 +0100
parents b6dc57688c72
children 1d526ba11a24
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@552 577 TimeValueLayer::getLabelPreceding(size_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@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@28 633 size_t &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@517 705 size_t &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@517 736 // if no suitable close one is found
Chris@517 737
Chris@517 738 while (i != points.end()) {
Chris@517 739
Chris@517 740 if (i == close.end()) {
Chris@517 741 i = points.begin();
Chris@517 742 distant = true;
Chris@517 743 }
Chris@517 744
Chris@517 745 if (snap == SnapRight) {
Chris@517 746
Chris@517 747 if (i->frame > matchframe &&
Chris@517 748 fabsf(i->value - matchvalue) < epsilon) {
Chris@517 749 snapped = i->frame;
Chris@517 750 found = true;
Chris@517 751 break;
Chris@517 752 }
Chris@517 753
Chris@517 754 } else if (snap == SnapLeft) {
Chris@517 755
Chris@517 756 if (i->frame < matchframe) {
Chris@517 757 if (fabsf(i->value - matchvalue) < epsilon) {
Chris@517 758 snapped = i->frame;
Chris@517 759 found = true; // don't break, as the next may be better
Chris@517 760 }
Chris@517 761 } else if (found || distant) {
Chris@517 762 break;
Chris@517 763 }
Chris@517 764
Chris@517 765 } else {
Chris@517 766 // no other snap types supported
Chris@517 767 }
Chris@517 768
Chris@517 769 ++i;
Chris@517 770 }
Chris@517 771
Chris@517 772 frame = snapped;
Chris@517 773 return found;
Chris@517 774 }
Chris@517 775
Chris@101 776 void
Chris@101 777 TimeValueLayer::getScaleExtents(View *v, float &min, float &max, bool &log) const
Chris@101 778 {
Chris@101 779 min = 0.0;
Chris@101 780 max = 0.0;
Chris@101 781 log = false;
Chris@101 782
Chris@296 783 if (shouldAutoAlign()) {
Chris@101 784
Chris@698 785 if (!v->getValueExtents(getScaleUnits(), min, max, log)) {
Chris@101 786 min = m_model->getValueMinimum();
Chris@101 787 max = m_model->getValueMaximum();
Chris@668 788 } else if (log) {
Chris@668 789 LogRange::mapRange(min, max);
Chris@101 790 }
Chris@101 791
Chris@101 792 } else if (m_verticalScale == PlusMinusOneScale) {
Chris@101 793
Chris@101 794 min = -1.0;
Chris@101 795 max = 1.0;
Chris@101 796
Chris@101 797 } else {
Chris@101 798
Chris@437 799 getDisplayExtents(min, max);
Chris@632 800
Chris@101 801 if (m_verticalScale == LogScale) {
Chris@197 802 LogRange::mapRange(min, max);
Chris@101 803 log = true;
Chris@101 804 }
Chris@101 805 }
Chris@101 806
Chris@629 807 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@682 808 cerr << "TimeValueLayer::getScaleExtents: min = " << min << ", max = " << max << endl;
Chris@629 809 #endif
Chris@101 810 }
Chris@101 811
Chris@21 812 int
Chris@66 813 TimeValueLayer::getYForValue(View *v, float val) const
Chris@21 814 {
Chris@79 815 float min = 0.0, max = 0.0;
Chris@101 816 bool logarithmic = false;
Chris@79 817 int h = v->height();
Chris@79 818
Chris@101 819 getScaleExtents(v, min, max, logarithmic);
Chris@101 820
Chris@526 821 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@682 822 cerr << "getYForValue(" << val << "): min " << min << ", max "
Chris@682 823 << max << ", log " << logarithmic << endl;
Chris@526 824 #endif
Chris@101 825
Chris@101 826 if (logarithmic) {
Chris@197 827 val = LogRange::map(val);
Chris@79 828 }
Chris@79 829
Chris@66 830 return int(h - ((val - min) * h) / (max - min));
Chris@21 831 }
Chris@21 832
Chris@21 833 float
Chris@44 834 TimeValueLayer::getValueForY(View *v, int y) const
Chris@21 835 {
Chris@101 836 float min = 0.0, max = 0.0;
Chris@101 837 bool logarithmic = false;
Chris@44 838 int h = v->height();
Chris@21 839
Chris@101 840 getScaleExtents(v, min, max, logarithmic);
Chris@101 841
Chris@101 842 float val = min + (float(h - y) * float(max - min)) / h;
Chris@101 843
Chris@101 844 if (logarithmic) {
Chris@437 845 val = LogRange::map(val);
Chris@101 846 }
Chris@101 847
Chris@101 848 return val;
Chris@21 849 }
Chris@21 850
Chris@296 851 bool
Chris@296 852 TimeValueLayer::shouldAutoAlign() const
Chris@296 853 {
Chris@296 854 if (!m_model) return false;
Chris@698 855 QString unit = getScaleUnits();
Chris@296 856 return (m_verticalScale == AutoAlignScale && unit != "");
Chris@296 857 }
Chris@296 858
Chris@68 859 QColor
Chris@101 860 TimeValueLayer::getColourForValue(View *v, float val) const
Chris@68 861 {
Chris@101 862 float min, max;
Chris@101 863 bool log;
Chris@101 864 getScaleExtents(v, min, max, log);
Chris@68 865
Chris@197 866 if (min > max) std::swap(min, max);
Chris@197 867 if (max == min) max = min + 1;
Chris@197 868
Chris@101 869 if (log) {
Chris@197 870 val = LogRange::map(val);
Chris@68 871 }
Chris@68 872
Chris@526 873 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@682 874 cerr << "TimeValueLayer::getColourForValue: min " << min << ", max "
Chris@682 875 << max << ", log " << log << ", value " << val << endl;
Chris@526 876 #endif
Chris@68 877
Chris@197 878 QColor solid = ColourMapper(m_colourMap, min, max).map(val);
Chris@197 879 return QColor(solid.red(), solid.green(), solid.blue(), 120);
Chris@68 880 }
Chris@68 881
Chris@287 882 int
Chris@287 883 TimeValueLayer::getDefaultColourHint(bool darkbg, bool &impose)
Chris@287 884 {
Chris@287 885 impose = false;
Chris@287 886 return ColourDatabase::getInstance()->getColourIndex
Chris@287 887 (QString(darkbg ? "Bright Green" : "Green"));
Chris@287 888 }
Chris@287 889
Chris@0 890 void
Chris@44 891 TimeValueLayer::paint(View *v, QPainter &paint, QRect rect) const
Chris@0 892 {
Chris@0 893 if (!m_model || !m_model->isOK()) return;
Chris@0 894
Chris@0 895 int sampleRate = m_model->getSampleRate();
Chris@0 896 if (!sampleRate) return;
Chris@0 897
Chris@353 898 paint.setRenderHint(QPainter::Antialiasing, false);
Chris@353 899
Chris@0 900 // Profiler profiler("TimeValueLayer::paint", true);
Chris@0 901
Chris@0 902 int x0 = rect.left(), x1 = rect.right();
Chris@44 903 long frame0 = v->getFrameForX(x0);
Chris@44 904 long frame1 = v->getFrameForX(x1);
Chris@553 905 if (m_derivative) --frame0;
Chris@0 906
Chris@0 907 SparseTimeValueModel::PointList points(m_model->getPoints
Chris@0 908 (frame0, frame1));
Chris@11 909 if (points.empty()) return;
Chris@0 910
Chris@287 911 paint.setPen(getBaseQColor());
Chris@0 912
Chris@287 913 QColor brushColour(getBaseQColor());
Chris@0 914 brushColour.setAlpha(80);
Chris@0 915 paint.setBrush(brushColour);
Chris@0 916
Chris@526 917 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@682 918 cerr << "TimeValueLayer::paint: resolution is "
Chris@682 919 << m_model->getResolution() << " frames" << endl;
Chris@526 920 #endif
Chris@0 921
Chris@0 922 float min = m_model->getValueMinimum();
Chris@0 923 float max = m_model->getValueMaximum();
Chris@0 924 if (max == min) max = min + 1.0;
Chris@0 925
Chris@44 926 int origin = int(nearbyint(v->height() -
Chris@44 927 (-min * v->height()) / (max - min)));
Chris@0 928
Chris@0 929 QPoint localPos;
Chris@0 930 long illuminateFrame = -1;
Chris@0 931
Chris@44 932 if (v->shouldIlluminateLocalFeatures(this, localPos)) {
Chris@0 933 SparseTimeValueModel::PointList localPoints =
Chris@44 934 getLocalPoints(v, localPos.x());
Chris@526 935 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@682 936 cerr << "TimeValueLayer: " << localPoints.size() << " local points" << endl;
Chris@526 937 #endif
Chris@0 938 if (!localPoints.empty()) illuminateFrame = localPoints.begin()->frame;
Chris@0 939 }
Chris@6 940
Chris@20 941 int w =
Chris@44 942 v->getXForFrame(frame0 + m_model->getResolution()) -
Chris@44 943 v->getXForFrame(frame0);
Chris@7 944
Chris@629 945 if (m_plotStyle == PlotStems) {
Chris@629 946 if (w < 2) w = 2;
Chris@629 947 } else {
Chris@629 948 if (w < 1) w = 1;
Chris@629 949 }
Chris@629 950
Chris@6 951 paint.save();
Chris@6 952
Chris@6 953 QPainterPath path;
Chris@55 954 int pointCount = 0;
Chris@79 955
Chris@79 956 int textY = 0;
Chris@79 957 if (m_plotStyle == PlotSegmentation) {
Chris@79 958 textY = v->getTextLabelHeight(this, paint);
Chris@554 959 } else {
Chris@554 960 int originY = getYForValue(v, 0.f);
Chris@554 961 if (originY > 0 && originY < v->height()) {
Chris@554 962 paint.save();
Chris@554 963 paint.setPen(getPartialShades(v)[1]);
Chris@554 964 paint.drawLine(x0, originY, x1, originY);
Chris@554 965 paint.restore();
Chris@554 966 }
Chris@79 967 }
Chris@6 968
Chris@615 969 int prevFrame = 0;
Chris@615 970
Chris@0 971 for (SparseTimeValueModel::PointList::const_iterator i = points.begin();
Chris@0 972 i != points.end(); ++i) {
Chris@0 973
Chris@553 974 if (m_derivative && i == points.begin()) continue;
Chris@721 975
Chris@0 976 const SparseTimeValueModel::Point &p(*i);
Chris@0 977
Chris@553 978 float value = p.value;
Chris@553 979 if (m_derivative) {
Chris@553 980 SparseTimeValueModel::PointList::const_iterator j = i;
Chris@553 981 --j;
Chris@553 982 value -= j->value;
Chris@553 983 }
Chris@553 984
Chris@44 985 int x = v->getXForFrame(p.frame);
Chris@553 986 int y = getYForValue(v, value);
Chris@0 987
Chris@615 988 bool gap = false;
Chris@615 989 if (m_plotStyle == PlotDiscreteCurves) {
Chris@721 990 if (value == 0.0) {
Chris@721 991 // Treat zeros as gaps
Chris@721 992 continue;
Chris@721 993 }
Chris@615 994 gap = (p.frame > prevFrame &&
Chris@615 995 (p.frame - prevFrame >= m_model->getResolution() * 2));
Chris@615 996 }
Chris@615 997
Chris@79 998 if (m_plotStyle != PlotSegmentation) {
Chris@79 999 textY = y - paint.fontMetrics().height()
Chris@631 1000 + paint.fontMetrics().ascent() - 1;
Chris@372 1001 if (textY < paint.fontMetrics().ascent() + 1) {
Chris@372 1002 textY = paint.fontMetrics().ascent() + 1;
Chris@372 1003 }
Chris@79 1004 }
Chris@79 1005
Chris@34 1006 bool haveNext = false;
Chris@721 1007 float nvalue = 0.f;
Chris@615 1008 int nf = v->getModelsEndFrame();
Chris@615 1009 int nx = v->getXForFrame(nf);
Chris@34 1010 int ny = y;
Chris@34 1011
Chris@34 1012 SparseTimeValueModel::PointList::const_iterator j = i;
Chris@34 1013 ++j;
Chris@34 1014
Chris@34 1015 if (j != points.end()) {
Chris@34 1016 const SparseTimeValueModel::Point &q(*j);
Chris@721 1017 nvalue = q.value;
Chris@553 1018 if (m_derivative) nvalue -= p.value;
Chris@615 1019 nf = q.frame;
Chris@615 1020 nx = v->getXForFrame(nf);
Chris@553 1021 ny = getYForValue(v, nvalue);
Chris@34 1022 haveNext = true;
Chris@76 1023 }
Chris@76 1024
Chris@682 1025 // cout << "frame = " << p.frame << ", x = " << x << ", haveNext = " << haveNext
Chris@682 1026 // << ", nx = " << nx << endl;
Chris@34 1027
Chris@615 1028 if (m_plotStyle == PlotDiscreteCurves) {
Chris@615 1029 paint.setPen(QPen(getBaseQColor(), 3));
Chris@615 1030 paint.setBrush(Qt::NoBrush);
Chris@615 1031 } else if (m_plotStyle == PlotSegmentation) {
Chris@287 1032 paint.setPen(getForegroundQColor(v));
Chris@553 1033 paint.setBrush(getColourForValue(v, value));
Chris@26 1034 } else if (m_plotStyle == PlotLines ||
Chris@26 1035 m_plotStyle == PlotCurve) {
Chris@615 1036 paint.setPen(getBaseQColor());
Chris@6 1037 paint.setBrush(Qt::NoBrush);
Chris@3 1038 } else {
Chris@615 1039 paint.setPen(getBaseQColor());
Chris@6 1040 paint.setBrush(brushColour);
Chris@3 1041 }
Chris@0 1042
Chris@0 1043 if (m_plotStyle == PlotStems) {
Chris@430 1044 /*
Chris@0 1045 paint.setPen(brushColour);
Chris@0 1046 if (y < origin - 1) {
Chris@0 1047 paint.drawRect(x + w/2, y + 1, 1, origin - y);
Chris@0 1048 } else if (y > origin + 1) {
Chris@0 1049 paint.drawRect(x + w/2, origin, 1, y - origin - 1);
Chris@0 1050 }
Chris@430 1051 */
Chris@287 1052 paint.setPen(getBaseQColor());
Chris@430 1053 if (y < origin - 1) {
Chris@430 1054 paint.drawLine(x + w/2, y + 1, x + w/2, origin);
Chris@430 1055 } else if (y > origin + 1) {
Chris@430 1056 paint.drawLine(x + w/2, origin, x + w/2, y - 1);
Chris@430 1057 }
Chris@0 1058 }
Chris@0 1059
Chris@631 1060 bool illuminate = false;
Chris@631 1061
Chris@0 1062 if (illuminateFrame == p.frame) {
Chris@6 1063
Chris@631 1064 // not equipped to illuminate the right section in line
Chris@631 1065 // or curve mode
Chris@6 1066
Chris@6 1067 if (m_plotStyle != PlotCurve &&
Chris@615 1068 m_plotStyle != PlotDiscreteCurves &&
Chris@6 1069 m_plotStyle != PlotLines) {
Chris@631 1070 illuminate = true;
Chris@631 1071 }
Chris@631 1072 }
Chris@0 1073
Chris@6 1074 if (m_plotStyle != PlotLines &&
Chris@26 1075 m_plotStyle != PlotCurve &&
Chris@615 1076 m_plotStyle != PlotDiscreteCurves &&
Chris@26 1077 m_plotStyle != PlotSegmentation) {
Chris@631 1078 if (illuminate) {
Chris@631 1079 paint.save();
Chris@631 1080 paint.setPen(getForegroundQColor(v));
Chris@631 1081 paint.setBrush(getForegroundQColor(v));
Chris@631 1082 }
Chris@326 1083 if (m_plotStyle != PlotStems ||
Chris@326 1084 w > 1) {
Chris@326 1085 paint.drawRect(x, y - 1, w, 2);
Chris@326 1086 }
Chris@631 1087 if (illuminate) {
Chris@631 1088 paint.restore();
Chris@631 1089 }
Chris@3 1090 }
Chris@0 1091
Chris@6 1092 if (m_plotStyle == PlotConnectedPoints ||
Chris@6 1093 m_plotStyle == PlotLines ||
Chris@615 1094 m_plotStyle == PlotDiscreteCurves ||
Chris@6 1095 m_plotStyle == PlotCurve) {
Chris@0 1096
Chris@34 1097 if (haveNext) {
Chris@3 1098
Chris@6 1099 if (m_plotStyle == PlotConnectedPoints) {
Chris@34 1100
Chris@79 1101 paint.save();
Chris@3 1102 paint.setPen(brushColour);
Chris@3 1103 paint.drawLine(x + w, y, nx, ny);
Chris@79 1104 paint.restore();
Chris@6 1105
Chris@6 1106 } else if (m_plotStyle == PlotLines) {
Chris@430 1107
Chris@430 1108 if (pointCount == 0) {
Chris@430 1109 path.moveTo(x + w/2, y);
Chris@430 1110 }
Chris@6 1111
Chris@430 1112 // paint.drawLine(x + w/2, y, nx + w/2, ny);
Chris@430 1113 path.lineTo(nx + w/2, ny);
Chris@6 1114
Chris@3 1115 } else {
Chris@6 1116
Chris@55 1117 float x0 = x + float(w)/2;
Chris@55 1118 float x1 = nx + float(w)/2;
Chris@55 1119
Chris@55 1120 float y0 = y;
Chris@55 1121 float y1 = ny;
Chris@55 1122
Chris@615 1123 if (m_plotStyle == PlotDiscreteCurves) {
Chris@721 1124 bool nextGap =
Chris@721 1125 (nvalue == 0.0) ||
Chris@721 1126 (nf - p.frame >= m_model->getResolution() * 2);
Chris@615 1127 if (nextGap) {
Chris@615 1128 x1 = x0;
Chris@615 1129 y1 = y0;
Chris@615 1130 }
Chris@615 1131 }
Chris@615 1132
Chris@615 1133 if (pointCount == 0 || gap) {
Chris@55 1134 path.moveTo((x0 + x1) / 2, (y0 + y1) / 2);
Chris@6 1135 }
Chris@6 1136
Chris@6 1137 if (nx - x > 5) {
Chris@55 1138 path.cubicTo(x0, y0,
Chris@55 1139 x0, y0,
Chris@55 1140 (x0 + x1) / 2, (y0 + y1) / 2);
Chris@55 1141
Chris@55 1142 // // or
Chris@55 1143 // path.quadTo(x0, y0, (x0 + x1) / 2, (y0 + y1) / 2);
Chris@55 1144
Chris@6 1145 } else {
Chris@55 1146 path.lineTo((x0 + x1) / 2, (y0 + y1) / 2);
Chris@6 1147 }
Chris@3 1148 }
Chris@0 1149 }
Chris@0 1150 }
Chris@0 1151
Chris@26 1152 if (m_plotStyle == PlotSegmentation) {
Chris@76 1153
Chris@526 1154 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@682 1155 cerr << "drawing rect" << endl;
Chris@526 1156 #endif
Chris@26 1157
Chris@27 1158 if (nx <= x) continue;
Chris@26 1159
Chris@559 1160 paint.setPen(QPen(getForegroundQColor(v), 2));
Chris@559 1161
Chris@631 1162 if (!illuminate) {
Chris@513 1163 if (!m_drawSegmentDivisions ||
Chris@513 1164 nx < x + 5 ||
Chris@513 1165 x >= v->width() - 1) {
Chris@513 1166 paint.setPen(Qt::NoPen);
Chris@513 1167 }
Chris@27 1168 }
Chris@26 1169
Chris@44 1170 paint.drawRect(x, -1, nx - x, v->height() + 1);
Chris@26 1171 }
Chris@26 1172
Chris@741 1173 if (v->shouldShowFeatureLabels()) {
Chris@629 1174
Chris@741 1175 QString label = p.label;
Chris@741 1176 bool italic = false;
Chris@741 1177
Chris@741 1178 if (label == "" &&
Chris@741 1179 (m_plotStyle == PlotPoints ||
Chris@741 1180 m_plotStyle == PlotSegmentation ||
Chris@741 1181 m_plotStyle == PlotConnectedPoints)) {
Chris@741 1182 char lc[20];
Chris@741 1183 snprintf(lc, 20, "%.3g", p.value);
Chris@741 1184 label = lc;
Chris@741 1185 italic = true;
Chris@741 1186 }
Chris@741 1187
Chris@741 1188 if (label != "") {
Chris@741 1189 // Quick test for 20px before we do the slower test using metrics
Chris@741 1190 bool haveRoom = (nx > x + 20);
Chris@741 1191 haveRoom = (haveRoom &&
Chris@741 1192 (nx > x + 6 + paint.fontMetrics().width(label)));
Chris@741 1193 if (haveRoom ||
Chris@741 1194 (!haveNext &&
Chris@741 1195 (pointCount == 0 || !italic))) {
Chris@741 1196 v->drawVisibleText(paint, x + 5, textY, label,
Chris@741 1197 italic ?
Chris@741 1198 View::OutlinedItalicText :
Chris@741 1199 View::OutlinedText);
Chris@741 1200 }
Chris@741 1201 }
Chris@629 1202 }
Chris@629 1203
Chris@615 1204 prevFrame = p.frame;
Chris@632 1205 ++pointCount;
Chris@0 1206 }
Chris@6 1207
Chris@691 1208 if (m_plotStyle == PlotDiscreteCurves) {
Chris@691 1209 paint.setRenderHint(QPainter::Antialiasing, true);
Chris@691 1210 paint.drawPath(path);
Chris@691 1211 } else if ((m_plotStyle == PlotCurve || m_plotStyle == PlotLines)
Chris@691 1212 && !path.isEmpty()) {
Chris@55 1213 paint.setRenderHint(QPainter::Antialiasing, pointCount <= v->width());
Chris@6 1214 paint.drawPath(path);
Chris@6 1215 }
Chris@6 1216
Chris@6 1217 paint.restore();
Chris@6 1218
Chris@6 1219 // looks like save/restore doesn't deal with this:
Chris@6 1220 paint.setRenderHint(QPainter::Antialiasing, false);
Chris@6 1221 }
Chris@6 1222
Chris@42 1223 int
Chris@698 1224 TimeValueLayer::getVerticalScaleWidth(View *v, bool, QPainter &paint) const
Chris@42 1225 {
Chris@701 1226 if (!m_model || shouldAutoAlign()) {
Chris@701 1227 return 0;
Chris@701 1228 } else if (m_plotStyle == PlotSegmentation) {
Chris@699 1229 if (m_verticalScale == LogScale) {
Chris@699 1230 return LogColourScale().getWidth(v, paint);
Chris@699 1231 } else {
Chris@699 1232 return LinearColourScale().getWidth(v, paint);
Chris@699 1233 }
Chris@698 1234 } else {
Chris@699 1235 if (m_verticalScale == LogScale) {
Chris@699 1236 return LogNumericalScale().getWidth(v, paint) + 10; // for piano
Chris@699 1237 } else {
Chris@699 1238 return LinearNumericalScale().getWidth(v, paint);
Chris@699 1239 }
Chris@698 1240 }
Chris@42 1241 }
Chris@42 1242
Chris@42 1243 void
Chris@607 1244 TimeValueLayer::paintVerticalScale(View *v, bool, QPainter &paint, QRect) const
Chris@42 1245 {
Chris@717 1246 if (!m_model || m_model->getPoints().empty()) return;
Chris@42 1247
Chris@699 1248 QString unit;
Chris@698 1249 float min, max;
Chris@698 1250 bool logarithmic;
Chris@698 1251
Chris@698 1252 int w = getVerticalScaleWidth(v, false, paint);
Chris@698 1253 int h = v->height();
Chris@698 1254
Chris@698 1255 if (m_plotStyle == PlotSegmentation) {
Chris@698 1256
Chris@699 1257 getValueExtents(min, max, logarithmic, unit);
Chris@699 1258
Chris@699 1259 if (logarithmic) {
Chris@699 1260 LogRange::mapRange(min, max);
Chris@699 1261 LogColourScale().paintVertical(v, this, paint, 0, min, max);
Chris@699 1262 } else {
Chris@699 1263 LinearColourScale().paintVertical(v, this, paint, 0, min, max);
Chris@699 1264 }
Chris@698 1265
Chris@698 1266 } else {
Chris@698 1267
Chris@699 1268 getScaleExtents(v, min, max, logarithmic);
Chris@699 1269
Chris@698 1270 if (logarithmic) {
Chris@698 1271 LogNumericalScale().paintVertical(v, this, paint, 0, min, max);
Chris@698 1272 } else {
Chris@698 1273 LinearNumericalScale().paintVertical(v, this, paint, 0, min, max);
Chris@698 1274 }
Chris@698 1275
Chris@698 1276 if (logarithmic && (getScaleUnits() == "Hz")) {
Chris@698 1277 PianoScale().paintPianoVertical
Chris@698 1278 (v, paint, QRect(w - 10, 0, 10, h),
Chris@698 1279 LogRange::unmap(min),
Chris@698 1280 LogRange::unmap(max));
Chris@698 1281 paint.drawLine(w, 0, w, h);
Chris@698 1282 }
Chris@698 1283 }
Chris@698 1284
Chris@698 1285 if (getScaleUnits() != "") {
Chris@701 1286 int mw = w - 5;
Chris@701 1287 paint.drawText(5,
Chris@701 1288 5 + paint.fontMetrics().ascent(),
Chris@701 1289 TextAbbrev::abbreviate(getScaleUnits(),
Chris@701 1290 paint.fontMetrics(),
Chris@701 1291 mw));
Chris@691 1292 }
Chris@42 1293 }
Chris@42 1294
Chris@21 1295 void
Chris@44 1296 TimeValueLayer::drawStart(View *v, QMouseEvent *e)
Chris@21 1297 {
Chris@526 1298 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@682 1299 cerr << "TimeValueLayer::drawStart(" << e->x() << "," << e->y() << ")" << endl;
Chris@526 1300 #endif
Chris@21 1301
Chris@21 1302 if (!m_model) return;
Chris@21 1303
Chris@44 1304 long frame = v->getFrameForX(e->x());
Chris@76 1305 long resolution = m_model->getResolution();
Chris@21 1306 if (frame < 0) frame = 0;
Chris@76 1307 frame = (frame / resolution) * resolution;
Chris@21 1308
Chris@44 1309 float value = getValueForY(v, e->y());
Chris@21 1310
Chris@76 1311 bool havePoint = false;
Chris@76 1312
Chris@76 1313 SparseTimeValueModel::PointList points = getLocalPoints(v, e->x());
Chris@76 1314 if (!points.empty()) {
Chris@76 1315 for (SparseTimeValueModel::PointList::iterator i = points.begin();
Chris@76 1316 i != points.end(); ++i) {
Chris@76 1317 if (((i->frame / resolution) * resolution) != frame) {
Chris@526 1318 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@682 1319 cerr << "ignoring out-of-range frame at " << i->frame << endl;
Chris@526 1320 #endif
Chris@76 1321 continue;
Chris@76 1322 }
Chris@76 1323 m_editingPoint = *i;
Chris@76 1324 havePoint = true;
Chris@76 1325 }
Chris@76 1326 }
Chris@76 1327
Chris@76 1328 if (!havePoint) {
Chris@76 1329 m_editingPoint = SparseTimeValueModel::Point
Chris@76 1330 (frame, value, tr("New Point"));
Chris@76 1331 }
Chris@76 1332
Chris@23 1333 m_originalPoint = m_editingPoint;
Chris@22 1334
Chris@376 1335 if (m_editingCommand) finish(m_editingCommand);
Chris@22 1336 m_editingCommand = new SparseTimeValueModel::EditCommand(m_model,
Chris@22 1337 tr("Draw Point"));
Chris@76 1338 if (!havePoint) {
Chris@76 1339 m_editingCommand->addPoint(m_editingPoint);
Chris@76 1340 }
Chris@22 1341
Chris@21 1342 m_editing = true;
Chris@21 1343 }
Chris@21 1344
Chris@21 1345 void
Chris@44 1346 TimeValueLayer::drawDrag(View *v, QMouseEvent *e)
Chris@21 1347 {
Chris@526 1348 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@682 1349 cerr << "TimeValueLayer::drawDrag(" << e->x() << "," << e->y() << ")" << endl;
Chris@526 1350 #endif
Chris@21 1351
Chris@21 1352 if (!m_model || !m_editing) return;
Chris@21 1353
Chris@44 1354 long frame = v->getFrameForX(e->x());
Chris@76 1355 long resolution = m_model->getResolution();
Chris@21 1356 if (frame < 0) frame = 0;
Chris@76 1357 frame = (frame / resolution) * resolution;
Chris@21 1358
Chris@44 1359 float value = getValueForY(v, e->y());
Chris@21 1360
Chris@76 1361 SparseTimeValueModel::PointList points = getLocalPoints(v, e->x());
Chris@76 1362
Chris@526 1363 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@682 1364 cerr << points.size() << " points" << endl;
Chris@526 1365 #endif
Chris@76 1366
Chris@76 1367 bool havePoint = false;
Chris@76 1368
Chris@76 1369 if (!points.empty()) {
Chris@76 1370 for (SparseTimeValueModel::PointList::iterator i = points.begin();
Chris@76 1371 i != points.end(); ++i) {
Chris@76 1372 if (i->frame == m_editingPoint.frame &&
Chris@76 1373 i->value == m_editingPoint.value) {
Chris@526 1374 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@682 1375 cerr << "ignoring current editing point at " << i->frame << ", " << i->value << endl;
Chris@526 1376 #endif
Chris@76 1377 continue;
Chris@76 1378 }
Chris@76 1379 if (((i->frame / resolution) * resolution) != frame) {
Chris@526 1380 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@682 1381 cerr << "ignoring out-of-range frame at " << i->frame << endl;
Chris@526 1382 #endif
Chris@76 1383 continue;
Chris@76 1384 }
Chris@526 1385 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@682 1386 cerr << "adjusting to new point at " << i->frame << ", " << i->value << endl;
Chris@526 1387 #endif
Chris@76 1388 m_editingPoint = *i;
Chris@76 1389 m_originalPoint = m_editingPoint;
Chris@76 1390 m_editingCommand->deletePoint(m_editingPoint);
Chris@76 1391 havePoint = true;
Chris@76 1392 }
Chris@76 1393 }
Chris@76 1394
Chris@76 1395 if (!havePoint) {
Chris@76 1396 if (frame == m_editingPoint.frame) {
Chris@76 1397 m_editingCommand->deletePoint(m_editingPoint);
Chris@76 1398 }
Chris@76 1399 }
Chris@76 1400
Chris@76 1401 // m_editingCommand->deletePoint(m_editingPoint);
Chris@21 1402 m_editingPoint.frame = frame;
Chris@21 1403 m_editingPoint.value = value;
Chris@22 1404 m_editingCommand->addPoint(m_editingPoint);
Chris@21 1405 }
Chris@21 1406
Chris@21 1407 void
Chris@248 1408 TimeValueLayer::drawEnd(View *, QMouseEvent *)
Chris@21 1409 {
Chris@526 1410 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@682 1411 cerr << "TimeValueLayer::drawEnd" << endl;
Chris@526 1412 #endif
Chris@21 1413 if (!m_model || !m_editing) return;
Chris@376 1414 finish(m_editingCommand);
Chris@22 1415 m_editingCommand = 0;
Chris@21 1416 m_editing = false;
Chris@21 1417 }
Chris@21 1418
Chris@21 1419 void
Chris@335 1420 TimeValueLayer::eraseStart(View *v, QMouseEvent *e)
Chris@335 1421 {
Chris@335 1422 if (!m_model) return;
Chris@335 1423
Chris@335 1424 SparseTimeValueModel::PointList points = getLocalPoints(v, e->x());
Chris@335 1425 if (points.empty()) return;
Chris@335 1426
Chris@335 1427 m_editingPoint = *points.begin();
Chris@335 1428
Chris@335 1429 if (m_editingCommand) {
Chris@376 1430 finish(m_editingCommand);
Chris@335 1431 m_editingCommand = 0;
Chris@335 1432 }
Chris@335 1433
Chris@335 1434 m_editing = true;
Chris@335 1435 }
Chris@335 1436
Chris@335 1437 void
Chris@335 1438 TimeValueLayer::eraseDrag(View *v, QMouseEvent *e)
Chris@335 1439 {
Chris@335 1440 }
Chris@335 1441
Chris@335 1442 void
Chris@335 1443 TimeValueLayer::eraseEnd(View *v, QMouseEvent *e)
Chris@335 1444 {
Chris@335 1445 if (!m_model || !m_editing) return;
Chris@335 1446
Chris@335 1447 m_editing = false;
Chris@335 1448
Chris@335 1449 SparseTimeValueModel::PointList points = getLocalPoints(v, e->x());
Chris@335 1450 if (points.empty()) return;
Chris@335 1451 if (points.begin()->frame != m_editingPoint.frame ||
Chris@335 1452 points.begin()->value != m_editingPoint.value) return;
Chris@335 1453
Chris@335 1454 m_editingCommand = new SparseTimeValueModel::EditCommand
Chris@335 1455 (m_model, tr("Erase Point"));
Chris@335 1456
Chris@335 1457 m_editingCommand->deletePoint(m_editingPoint);
Chris@335 1458
Chris@376 1459 finish(m_editingCommand);
Chris@335 1460 m_editingCommand = 0;
Chris@335 1461 m_editing = false;
Chris@335 1462 }
Chris@335 1463
Chris@335 1464 void
Chris@44 1465 TimeValueLayer::editStart(View *v, QMouseEvent *e)
Chris@21 1466 {
Chris@526 1467 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@682 1468 cerr << "TimeValueLayer::editStart(" << e->x() << "," << e->y() << ")" << endl;
Chris@526 1469 #endif
Chris@21 1470
Chris@21 1471 if (!m_model) return;
Chris@21 1472
Chris@44 1473 SparseTimeValueModel::PointList points = getLocalPoints(v, e->x());
Chris@21 1474 if (points.empty()) return;
Chris@21 1475
Chris@21 1476 m_editingPoint = *points.begin();
Chris@23 1477 m_originalPoint = m_editingPoint;
Chris@22 1478
Chris@22 1479 if (m_editingCommand) {
Chris@376 1480 finish(m_editingCommand);
Chris@22 1481 m_editingCommand = 0;
Chris@22 1482 }
Chris@22 1483
Chris@21 1484 m_editing = true;
Chris@21 1485 }
Chris@21 1486
Chris@21 1487 void
Chris@44 1488 TimeValueLayer::editDrag(View *v, QMouseEvent *e)
Chris@21 1489 {
Chris@526 1490 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@682 1491 cerr << "TimeValueLayer::editDrag(" << e->x() << "," << e->y() << ")" << endl;
Chris@526 1492 #endif
Chris@21 1493
Chris@21 1494 if (!m_model || !m_editing) return;
Chris@21 1495
Chris@44 1496 long frame = v->getFrameForX(e->x());
Chris@21 1497 if (frame < 0) frame = 0;
Chris@21 1498 frame = frame / m_model->getResolution() * m_model->getResolution();
Chris@21 1499
Chris@44 1500 float value = getValueForY(v, e->y());
Chris@21 1501
Chris@22 1502 if (!m_editingCommand) {
Chris@22 1503 m_editingCommand = new SparseTimeValueModel::EditCommand(m_model,
Chris@22 1504 tr("Drag Point"));
Chris@22 1505 }
Chris@22 1506
Chris@22 1507 m_editingCommand->deletePoint(m_editingPoint);
Chris@21 1508 m_editingPoint.frame = frame;
Chris@21 1509 m_editingPoint.value = value;
Chris@22 1510 m_editingCommand->addPoint(m_editingPoint);
Chris@21 1511 }
Chris@21 1512
Chris@21 1513 void
Chris@248 1514 TimeValueLayer::editEnd(View *, QMouseEvent *)
Chris@21 1515 {
Chris@526 1516 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@682 1517 cerr << "TimeValueLayer::editEnd" << endl;
Chris@526 1518 #endif
Chris@21 1519 if (!m_model || !m_editing) return;
Chris@23 1520
Chris@23 1521 if (m_editingCommand) {
Chris@23 1522
Chris@23 1523 QString newName = m_editingCommand->getName();
Chris@23 1524
Chris@23 1525 if (m_editingPoint.frame != m_originalPoint.frame) {
Chris@23 1526 if (m_editingPoint.value != m_originalPoint.value) {
Chris@23 1527 newName = tr("Edit Point");
Chris@23 1528 } else {
Chris@23 1529 newName = tr("Relocate Point");
Chris@23 1530 }
Chris@23 1531 } else {
Chris@23 1532 newName = tr("Change Point Value");
Chris@23 1533 }
Chris@23 1534
Chris@23 1535 m_editingCommand->setName(newName);
Chris@376 1536 finish(m_editingCommand);
Chris@23 1537 }
Chris@23 1538
Chris@22 1539 m_editingCommand = 0;
Chris@21 1540 m_editing = false;
Chris@21 1541 }
Chris@21 1542
Chris@255 1543 bool
Chris@70 1544 TimeValueLayer::editOpen(View *v, QMouseEvent *e)
Chris@70 1545 {
Chris@255 1546 if (!m_model) return false;
Chris@70 1547
Chris@70 1548 SparseTimeValueModel::PointList points = getLocalPoints(v, e->x());
Chris@255 1549 if (points.empty()) return false;
Chris@70 1550
Chris@70 1551 SparseTimeValueModel::Point point = *points.begin();
Chris@70 1552
Chris@70 1553 ItemEditDialog *dialog = new ItemEditDialog
Chris@70 1554 (m_model->getSampleRate(),
Chris@70 1555 ItemEditDialog::ShowTime |
Chris@70 1556 ItemEditDialog::ShowValue |
Chris@73 1557 ItemEditDialog::ShowText,
Chris@698 1558 getScaleUnits());
Chris@70 1559
Chris@70 1560 dialog->setFrameTime(point.frame);
Chris@70 1561 dialog->setValue(point.value);
Chris@70 1562 dialog->setText(point.label);
Chris@70 1563
Chris@70 1564 if (dialog->exec() == QDialog::Accepted) {
Chris@70 1565
Chris@70 1566 SparseTimeValueModel::Point newPoint = point;
Chris@70 1567 newPoint.frame = dialog->getFrameTime();
Chris@70 1568 newPoint.value = dialog->getValue();
Chris@70 1569 newPoint.label = dialog->getText();
Chris@70 1570
Chris@70 1571 SparseTimeValueModel::EditCommand *command =
Chris@70 1572 new SparseTimeValueModel::EditCommand(m_model, tr("Edit Point"));
Chris@70 1573 command->deletePoint(point);
Chris@70 1574 command->addPoint(newPoint);
Chris@376 1575 finish(command);
Chris@70 1576 }
Chris@70 1577
Chris@70 1578 delete dialog;
Chris@255 1579 return true;
Chris@70 1580 }
Chris@70 1581
Chris@70 1582 void
Chris@43 1583 TimeValueLayer::moveSelection(Selection s, size_t newStartFrame)
Chris@43 1584 {
Chris@99 1585 if (!m_model) return;
Chris@99 1586
Chris@43 1587 SparseTimeValueModel::EditCommand *command =
Chris@43 1588 new SparseTimeValueModel::EditCommand(m_model,
Chris@43 1589 tr("Drag Selection"));
Chris@43 1590
Chris@43 1591 SparseTimeValueModel::PointList points =
Chris@43 1592 m_model->getPoints(s.getStartFrame(), s.getEndFrame());
Chris@43 1593
Chris@43 1594 for (SparseTimeValueModel::PointList::iterator i = points.begin();
Chris@43 1595 i != points.end(); ++i) {
Chris@43 1596
Chris@43 1597 if (s.contains(i->frame)) {
Chris@43 1598 SparseTimeValueModel::Point newPoint(*i);
Chris@43 1599 newPoint.frame = i->frame + newStartFrame - s.getStartFrame();
Chris@43 1600 command->deletePoint(*i);
Chris@43 1601 command->addPoint(newPoint);
Chris@43 1602 }
Chris@43 1603 }
Chris@43 1604
Chris@376 1605 finish(command);
Chris@43 1606 }
Chris@43 1607
Chris@43 1608 void
Chris@43 1609 TimeValueLayer::resizeSelection(Selection s, Selection newSize)
Chris@43 1610 {
Chris@99 1611 if (!m_model) return;
Chris@99 1612
Chris@43 1613 SparseTimeValueModel::EditCommand *command =
Chris@43 1614 new SparseTimeValueModel::EditCommand(m_model,
Chris@43 1615 tr("Resize Selection"));
Chris@43 1616
Chris@43 1617 SparseTimeValueModel::PointList points =
Chris@43 1618 m_model->getPoints(s.getStartFrame(), s.getEndFrame());
Chris@43 1619
Chris@43 1620 double ratio =
Chris@43 1621 double(newSize.getEndFrame() - newSize.getStartFrame()) /
Chris@43 1622 double(s.getEndFrame() - s.getStartFrame());
Chris@43 1623
Chris@43 1624 for (SparseTimeValueModel::PointList::iterator i = points.begin();
Chris@43 1625 i != points.end(); ++i) {
Chris@43 1626
Chris@43 1627 if (s.contains(i->frame)) {
Chris@43 1628
Chris@43 1629 double target = i->frame;
Chris@43 1630 target = newSize.getStartFrame() +
Chris@43 1631 double(target - s.getStartFrame()) * ratio;
Chris@43 1632
Chris@43 1633 SparseTimeValueModel::Point newPoint(*i);
Chris@43 1634 newPoint.frame = lrint(target);
Chris@43 1635 command->deletePoint(*i);
Chris@43 1636 command->addPoint(newPoint);
Chris@43 1637 }
Chris@43 1638 }
Chris@43 1639
Chris@376 1640 finish(command);
Chris@43 1641 }
Chris@43 1642
Chris@76 1643 void
Chris@76 1644 TimeValueLayer::deleteSelection(Selection s)
Chris@76 1645 {
Chris@99 1646 if (!m_model) return;
Chris@99 1647
Chris@76 1648 SparseTimeValueModel::EditCommand *command =
Chris@76 1649 new SparseTimeValueModel::EditCommand(m_model,
Chris@76 1650 tr("Delete Selected Points"));
Chris@76 1651
Chris@76 1652 SparseTimeValueModel::PointList points =
Chris@76 1653 m_model->getPoints(s.getStartFrame(), s.getEndFrame());
Chris@76 1654
Chris@76 1655 for (SparseTimeValueModel::PointList::iterator i = points.begin();
Chris@76 1656 i != points.end(); ++i) {
Chris@76 1657
Chris@76 1658 if (s.contains(i->frame)) {
Chris@76 1659 command->deletePoint(*i);
Chris@76 1660 }
Chris@76 1661 }
Chris@76 1662
Chris@376 1663 finish(command);
Chris@76 1664 }
Chris@76 1665
Chris@76 1666 void
Chris@359 1667 TimeValueLayer::copy(View *v, Selection s, Clipboard &to)
Chris@76 1668 {
Chris@99 1669 if (!m_model) return;
Chris@99 1670
Chris@76 1671 SparseTimeValueModel::PointList points =
Chris@76 1672 m_model->getPoints(s.getStartFrame(), s.getEndFrame());
Chris@76 1673
Chris@76 1674 for (SparseTimeValueModel::PointList::iterator i = points.begin();
Chris@76 1675 i != points.end(); ++i) {
Chris@76 1676 if (s.contains(i->frame)) {
Chris@83 1677 Clipboard::Point point(i->frame, i->value, i->label);
Chris@360 1678 point.setReferenceFrame(alignToReference(v, i->frame));
Chris@76 1679 to.addPoint(point);
Chris@76 1680 }
Chris@76 1681 }
Chris@76 1682 }
Chris@76 1683
Chris@125 1684 bool
Chris@359 1685 TimeValueLayer::paste(View *v, const Clipboard &from, int frameOffset,
Chris@125 1686 bool interactive)
Chris@76 1687 {
Chris@125 1688 if (!m_model) return false;
Chris@99 1689
Chris@76 1690 const Clipboard::PointList &points = from.getPoints();
Chris@76 1691
Chris@360 1692 bool realign = false;
Chris@360 1693
Chris@360 1694 if (clipboardHasDifferentAlignment(v, from)) {
Chris@360 1695
Chris@360 1696 QMessageBox::StandardButton button =
Chris@360 1697 QMessageBox::question(v, tr("Re-align pasted items?"),
Chris@360 1698 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 1699 QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel,
Chris@360 1700 QMessageBox::Yes);
Chris@360 1701
Chris@360 1702 if (button == QMessageBox::Cancel) {
Chris@360 1703 return false;
Chris@360 1704 }
Chris@360 1705
Chris@360 1706 if (button == QMessageBox::Yes) {
Chris@360 1707 realign = true;
Chris@360 1708 }
Chris@360 1709 }
Chris@360 1710
Chris@76 1711 SparseTimeValueModel::EditCommand *command =
Chris@76 1712 new SparseTimeValueModel::EditCommand(m_model, tr("Paste"));
Chris@76 1713
Chris@125 1714 enum ValueAvailability {
Chris@125 1715 UnknownAvailability,
Chris@125 1716 NoValues,
Chris@125 1717 SomeValues,
Chris@125 1718 AllValues
Chris@125 1719 };
Chris@125 1720
Chris@340 1721 Labeller::ValueType generation = Labeller::ValueNone;
Chris@125 1722
Chris@125 1723 bool haveUsableLabels = false;
Chris@125 1724 bool haveExistingItems = !(m_model->isEmpty());
Chris@340 1725 Labeller labeller;
Chris@340 1726 labeller.setSampleRate(m_model->getSampleRate());
Chris@125 1727
Chris@125 1728 if (interactive) {
Chris@125 1729
Chris@125 1730 ValueAvailability availability = UnknownAvailability;
Chris@125 1731
Chris@125 1732 for (Clipboard::PointList::const_iterator i = points.begin();
Chris@125 1733 i != points.end(); ++i) {
Chris@125 1734
Chris@125 1735 if (!i->haveFrame()) continue;
Chris@125 1736
Chris@125 1737 if (availability == UnknownAvailability) {
Chris@125 1738 if (i->haveValue()) availability = AllValues;
Chris@125 1739 else availability = NoValues;
Chris@125 1740 continue;
Chris@125 1741 }
Chris@125 1742
Chris@125 1743 if (i->haveValue()) {
Chris@125 1744 if (availability == NoValues) {
Chris@125 1745 availability = SomeValues;
Chris@125 1746 }
Chris@125 1747 } else {
Chris@125 1748 if (availability == AllValues) {
Chris@125 1749 availability = SomeValues;
Chris@125 1750 }
Chris@125 1751 }
Chris@125 1752
Chris@125 1753 if (!haveUsableLabels) {
Chris@125 1754 if (i->haveLabel()) {
Chris@125 1755 if (i->getLabel().contains(QRegExp("[0-9]"))) {
Chris@125 1756 haveUsableLabels = true;
Chris@125 1757 }
Chris@125 1758 }
Chris@125 1759 }
Chris@125 1760
Chris@125 1761 if (availability == SomeValues && haveUsableLabels) break;
Chris@125 1762 }
Chris@125 1763
Chris@125 1764 if (availability == NoValues || availability == SomeValues) {
Chris@125 1765
Chris@125 1766 QString text;
Chris@125 1767 if (availability == NoValues) {
Chris@125 1768 text = tr("The items you are pasting do not have values.\nWhat values do you want to use for these items?");
Chris@125 1769 } else {
Chris@125 1770 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 1771 }
Chris@125 1772
Chris@340 1773 Labeller::TypeNameMap names = labeller.getTypeNames();
Chris@340 1774
Chris@125 1775 QStringList options;
Chris@340 1776 std::vector<Labeller::ValueType> genopts;
Chris@125 1777
Chris@340 1778 for (Labeller::TypeNameMap::const_iterator i = names.begin();
Chris@340 1779 i != names.end(); ++i) {
Chris@340 1780 if (i->first == Labeller::ValueNone) options << tr("Zero for all items");
Chris@340 1781 else options << i->second;
Chris@340 1782 genopts.push_back(i->first);
Chris@125 1783 }
Chris@125 1784
Chris@125 1785 static int prevSelection = 0;
Chris@125 1786
Chris@125 1787 bool ok = false;
Chris@125 1788 QString selected = ListInputDialog::getItem
Chris@125 1789 (0, tr("Choose value calculation"),
Chris@125 1790 text, options, prevSelection, &ok);
Chris@125 1791
Chris@125 1792 if (!ok) return false;
Chris@125 1793 int selection = 0;
Chris@340 1794 generation = Labeller::ValueNone;
Chris@125 1795
Chris@125 1796 for (QStringList::const_iterator i = options.begin();
Chris@125 1797 i != options.end(); ++i) {
Chris@125 1798 if (selected == *i) {
Chris@340 1799 generation = genopts[selection];
Chris@125 1800 break;
Chris@125 1801 }
Chris@125 1802 ++selection;
Chris@125 1803 }
Chris@340 1804
Chris@340 1805 labeller.setType(generation);
Chris@340 1806
Chris@340 1807 if (generation == Labeller::ValueFromCyclicalCounter ||
Chris@340 1808 generation == Labeller::ValueFromTwoLevelCounter) {
Chris@616 1809 int cycleSize = QInputDialog::getInt
Chris@340 1810 (0, tr("Select cycle size"),
Chris@340 1811 tr("Cycle size:"), 4, 2, 16, 1);
Chris@340 1812 labeller.setCounterCycleSize(cycleSize);
Chris@340 1813 }
Chris@125 1814
Chris@125 1815 prevSelection = selection;
Chris@125 1816 }
Chris@125 1817 }
Chris@125 1818
Chris@372 1819 SparseTimeValueModel::Point prevPoint(0);
Chris@125 1820
Chris@76 1821 for (Clipboard::PointList::const_iterator i = points.begin();
Chris@76 1822 i != points.end(); ++i) {
Chris@76 1823
Chris@76 1824 if (!i->haveFrame()) continue;
Chris@360 1825
Chris@76 1826 size_t frame = 0;
Chris@360 1827
Chris@360 1828 if (!realign) {
Chris@360 1829
Chris@360 1830 frame = i->getFrame();
Chris@360 1831
Chris@360 1832 } else {
Chris@360 1833
Chris@360 1834 if (i->haveReferenceFrame()) {
Chris@360 1835 frame = i->getReferenceFrame();
Chris@360 1836 frame = alignFromReference(v, frame);
Chris@360 1837 } else {
Chris@360 1838 frame = i->getFrame();
Chris@360 1839 }
Chris@76 1840 }
Chris@360 1841
Chris@76 1842 SparseTimeValueModel::Point newPoint(frame);
Chris@76 1843
Chris@125 1844 if (i->haveLabel()) {
Chris@125 1845 newPoint.label = i->getLabel();
Chris@125 1846 } else if (i->haveValue()) {
Chris@125 1847 newPoint.label = QString("%1").arg(i->getValue());
Chris@125 1848 }
Chris@125 1849
Chris@372 1850 bool usePrev = false;
Chris@372 1851 SparseTimeValueModel::Point formerPrevPoint = prevPoint;
Chris@372 1852
Chris@125 1853 if (i->haveValue()) {
Chris@125 1854 newPoint.value = i->getValue();
Chris@125 1855 } else {
Chris@526 1856 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@682 1857 cerr << "Setting value on point at " << newPoint.frame << " from labeller";
Chris@526 1858 if (i == points.begin()) {
Chris@682 1859 cerr << ", no prev point" << endl;
Chris@526 1860 } else {
Chris@682 1861 cerr << ", prev point is at " << prevPoint.frame << endl;
Chris@526 1862 }
Chris@526 1863 #endif
Chris@340 1864 labeller.setValue<SparseTimeValueModel::Point>
Chris@371 1865 (newPoint, (i == points.begin()) ? 0 : &prevPoint);
Chris@526 1866 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@682 1867 cerr << "New point value = " << newPoint.value << endl;
Chris@526 1868 #endif
Chris@372 1869 if (labeller.actingOnPrevPoint() && i != points.begin()) {
Chris@372 1870 usePrev = true;
Chris@372 1871 }
Chris@372 1872 }
Chris@372 1873
Chris@372 1874 if (usePrev) {
Chris@372 1875 command->deletePoint(formerPrevPoint);
Chris@372 1876 command->addPoint(prevPoint);
Chris@340 1877 }
Chris@125 1878
Chris@340 1879 prevPoint = newPoint;
Chris@76 1880 command->addPoint(newPoint);
Chris@76 1881 }
Chris@76 1882
Chris@376 1883 finish(command);
Chris@125 1884 return true;
Chris@360 1885 }
Chris@76 1886
Chris@316 1887 void
Chris@316 1888 TimeValueLayer::toXml(QTextStream &stream,
Chris@316 1889 QString indent, QString extraAttributes) const
Chris@6 1890 {
Chris@316 1891 SingleColourLayer::toXml(stream, indent,
Chris@316 1892 extraAttributes +
Chris@553 1893 QString(" colourMap=\"%1\" plotStyle=\"%2\" verticalScale=\"%3\" scaleMinimum=\"%4\" scaleMaximum=\"%5\" drawDivisions=\"%6\" derivative=\"%7\" ")
Chris@316 1894 .arg(m_colourMap)
Chris@316 1895 .arg(m_plotStyle)
Chris@445 1896 .arg(m_verticalScale)
Chris@445 1897 .arg(m_scaleMinimum)
Chris@513 1898 .arg(m_scaleMaximum)
Chris@553 1899 .arg(m_drawSegmentDivisions ? "true" : "false")
Chris@553 1900 .arg(m_derivative ? "true" : "false"));
Chris@0 1901 }
Chris@0 1902
Chris@11 1903 void
Chris@11 1904 TimeValueLayer::setProperties(const QXmlAttributes &attributes)
Chris@11 1905 {
Chris@287 1906 SingleColourLayer::setProperties(attributes);
Chris@11 1907
Chris@445 1908 bool ok, alsoOk;
Chris@287 1909
Chris@287 1910 int cmap = attributes.value("colourMap").toInt(&ok);
Chris@287 1911 if (ok) setFillColourMap(cmap);
Chris@287 1912
Chris@11 1913 PlotStyle style = (PlotStyle)
Chris@11 1914 attributes.value("plotStyle").toInt(&ok);
Chris@11 1915 if (ok) setPlotStyle(style);
Chris@286 1916
Chris@286 1917 VerticalScale scale = (VerticalScale)
Chris@286 1918 attributes.value("verticalScale").toInt(&ok);
Chris@286 1919 if (ok) setVerticalScale(scale);
Chris@445 1920
Chris@513 1921 bool draw = (attributes.value("drawDivisions").trimmed() == "true");
Chris@513 1922 setDrawSegmentDivisions(draw);
Chris@513 1923
Chris@553 1924 bool derivative = (attributes.value("derivative").trimmed() == "true");
Chris@553 1925 setShowDerivative(derivative);
Chris@553 1926
Chris@445 1927 float min = attributes.value("scaleMinimum").toFloat(&ok);
Chris@445 1928 float max = attributes.value("scaleMaximum").toFloat(&alsoOk);
Chris@526 1929 #ifdef DEBUG_TIME_VALUE_LAYER
Chris@682 1930 cerr << "from properties: min = " << min << ", max = " << max << endl;
Chris@526 1931 #endif
Chris@526 1932 if (ok && alsoOk && min != max) setDisplayExtents(min, max);
Chris@11 1933 }
Chris@11 1934