annotate layer/TimeValueLayer.cpp @ 561:aced8ec09bc8

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