annotate layer/TimeValueLayer.cpp @ 432:8b2b497d302c

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