annotate layer/TimeValueLayer.cpp @ 473:4f4f943bfdfc

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