annotate layer/TimeValueLayer.cpp @ 333:e74b56f07c73

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