annotate layer/TimeValueLayer.cpp @ 738:d26545a2a02d tonioni

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