annotate layer/TimeValueLayer.cpp @ 162:f32212631b9c

* Handle generator transforms (plugins whose channel count isn't dependent on number of audio inputs, as they have none) * Be less keen to suspend writing FFT data in spectrogram repaint -- only do it if we find we actually need to query the FFT data (i.e. we aren't repainting an area that hasn't been generated at all yet)
author Chris Cannam
date Tue, 10 Oct 2006 19:04:57 +0000
parents 33929e0c3c6b
children 6b023411087b
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@128 21 #include "view/View.h"
Chris@0 22
Chris@128 23 #include "data/model/SparseTimeValueModel.h"
Chris@0 24
Chris@70 25 #include "widgets/ItemEditDialog.h"
Chris@125 26 #include "widgets/ListInputDialog.h"
Chris@70 27
Chris@66 28 #include "SpectrogramLayer.h" // for optional frequency alignment
Chris@66 29
Chris@0 30 #include <QPainter>
Chris@6 31 #include <QPainterPath>
Chris@21 32 #include <QMouseEvent>
Chris@125 33 #include <QRegExp>
Chris@0 34
Chris@0 35 #include <iostream>
Chris@0 36 #include <cmath>
Chris@0 37
Chris@44 38 TimeValueLayer::TimeValueLayer() :
Chris@44 39 Layer(),
Chris@0 40 m_model(0),
Chris@21 41 m_editing(false),
Chris@23 42 m_originalPoint(0, 0.0, tr("New Point")),
Chris@21 43 m_editingPoint(0, 0.0, tr("New Point")),
Chris@22 44 m_editingCommand(0),
Chris@67 45 m_colour(Qt::darkGreen),
Chris@66 46 m_plotStyle(PlotConnectedPoints),
Chris@101 47 m_verticalScale(AutoAlignScale)
Chris@0 48 {
Chris@44 49
Chris@0 50 }
Chris@0 51
Chris@0 52 void
Chris@0 53 TimeValueLayer::setModel(SparseTimeValueModel *model)
Chris@0 54 {
Chris@0 55 if (m_model == model) return;
Chris@0 56 m_model = model;
Chris@0 57
Chris@0 58 connect(m_model, SIGNAL(modelChanged()), this, SIGNAL(modelChanged()));
Chris@0 59 connect(m_model, SIGNAL(modelChanged(size_t, size_t)),
Chris@0 60 this, SIGNAL(modelChanged(size_t, size_t)));
Chris@0 61
Chris@0 62 connect(m_model, SIGNAL(completionChanged()),
Chris@0 63 this, SIGNAL(modelCompletionChanged()));
Chris@0 64
Chris@101 65 // std::cerr << "TimeValueLayer::setModel(" << model << ")" << std::endl;
Chris@0 66
Chris@0 67 emit modelReplaced();
Chris@0 68 }
Chris@0 69
Chris@0 70 Layer::PropertyList
Chris@0 71 TimeValueLayer::getProperties() const
Chris@0 72 {
Chris@0 73 PropertyList list;
Chris@87 74 list.push_back("Colour");
Chris@87 75 list.push_back("Plot Type");
Chris@87 76 list.push_back("Vertical Scale");
Chris@100 77 list.push_back("Scale Units");
Chris@0 78 return list;
Chris@0 79 }
Chris@0 80
Chris@87 81 QString
Chris@87 82 TimeValueLayer::getPropertyLabel(const PropertyName &name) const
Chris@87 83 {
Chris@87 84 if (name == "Colour") return tr("Colour");
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@87 88 return "";
Chris@87 89 }
Chris@87 90
Chris@0 91 Layer::PropertyType
Chris@0 92 TimeValueLayer::getPropertyType(const PropertyName &name) const
Chris@0 93 {
Chris@100 94 if (name == "Scale Units") return UnitsProperty;
Chris@100 95 else return ValueProperty;
Chris@0 96 }
Chris@0 97
Chris@0 98 int
Chris@0 99 TimeValueLayer::getPropertyRangeAndValue(const PropertyName &name,
Chris@0 100 int *min, int *max) const
Chris@0 101 {
Chris@0 102 //!!! factor this colour handling stuff out into a colour manager class
Chris@0 103
Chris@0 104 int deft = 0;
Chris@0 105
Chris@87 106 if (name == "Colour") {
Chris@0 107
Chris@10 108 if (min) *min = 0;
Chris@10 109 if (max) *max = 5;
Chris@0 110
Chris@0 111 if (m_colour == Qt::black) deft = 0;
Chris@0 112 else if (m_colour == Qt::darkRed) deft = 1;
Chris@0 113 else if (m_colour == Qt::darkBlue) deft = 2;
Chris@0 114 else if (m_colour == Qt::darkGreen) deft = 3;
Chris@0 115 else if (m_colour == QColor(200, 50, 255)) deft = 4;
Chris@0 116 else if (m_colour == QColor(255, 150, 50)) deft = 5;
Chris@0 117
Chris@87 118 } else if (name == "Plot Type") {
Chris@0 119
Chris@10 120 if (min) *min = 0;
Chris@26 121 if (max) *max = 5;
Chris@0 122
Chris@0 123 deft = int(m_plotStyle);
Chris@0 124
Chris@87 125 } else if (name == "Vertical Scale") {
Chris@66 126
Chris@66 127 if (min) *min = 0;
Chris@66 128 if (max) *max = 3;
Chris@66 129
Chris@66 130 deft = int(m_verticalScale);
Chris@66 131
Chris@100 132 } else if (name == "Scale Units") {
Chris@100 133
Chris@100 134 if (m_model) {
Chris@100 135 deft = UnitDatabase::getInstance()->getUnitId
Chris@100 136 (m_model->getScaleUnits());
Chris@100 137 }
Chris@100 138
Chris@0 139 } else {
Chris@0 140
Chris@0 141 deft = Layer::getPropertyRangeAndValue(name, min, max);
Chris@0 142 }
Chris@0 143
Chris@0 144 return deft;
Chris@0 145 }
Chris@0 146
Chris@0 147 QString
Chris@0 148 TimeValueLayer::getPropertyValueLabel(const PropertyName &name,
Chris@0 149 int value) const
Chris@0 150 {
Chris@87 151 if (name == "Colour") {
Chris@0 152 switch (value) {
Chris@0 153 default:
Chris@0 154 case 0: return tr("Black");
Chris@0 155 case 1: return tr("Red");
Chris@0 156 case 2: return tr("Blue");
Chris@0 157 case 3: return tr("Green");
Chris@0 158 case 4: return tr("Purple");
Chris@0 159 case 5: return tr("Orange");
Chris@0 160 }
Chris@87 161 } else if (name == "Plot Type") {
Chris@0 162 switch (value) {
Chris@0 163 default:
Chris@0 164 case 0: return tr("Points");
Chris@0 165 case 1: return tr("Stems");
Chris@6 166 case 2: return tr("Connected Points");
Chris@6 167 case 3: return tr("Lines");
Chris@6 168 case 4: return tr("Curve");
Chris@26 169 case 5: return tr("Segmentation");
Chris@0 170 }
Chris@87 171 } else if (name == "Vertical Scale") {
Chris@66 172 switch (value) {
Chris@66 173 default:
Chris@101 174 case 0: return tr("Auto-Align");
Chris@101 175 case 1: return tr("Linear Scale");
Chris@101 176 case 2: return tr("Log Scale");
Chris@101 177 case 3: return tr("+/-1 Scale");
Chris@66 178 }
Chris@0 179 }
Chris@0 180 return tr("<unknown>");
Chris@0 181 }
Chris@0 182
Chris@0 183 void
Chris@0 184 TimeValueLayer::setProperty(const PropertyName &name, int value)
Chris@0 185 {
Chris@87 186 if (name == "Colour") {
Chris@0 187 switch (value) {
Chris@0 188 default:
Chris@0 189 case 0: setBaseColour(Qt::black); break;
Chris@0 190 case 1: setBaseColour(Qt::darkRed); break;
Chris@0 191 case 2: setBaseColour(Qt::darkBlue); break;
Chris@0 192 case 3: setBaseColour(Qt::darkGreen); break;
Chris@0 193 case 4: setBaseColour(QColor(200, 50, 255)); break;
Chris@0 194 case 5: setBaseColour(QColor(255, 150, 50)); break;
Chris@0 195 }
Chris@87 196 } else if (name == "Plot Type") {
Chris@0 197 setPlotStyle(PlotStyle(value));
Chris@87 198 } else if (name == "Vertical Scale") {
Chris@66 199 setVerticalScale(VerticalScale(value));
Chris@100 200 } else if (name == "Scale Units") {
Chris@100 201 if (m_model) {
Chris@100 202 m_model->setScaleUnits
Chris@100 203 (UnitDatabase::getInstance()->getUnitById(value));
Chris@100 204 emit modelChanged();
Chris@100 205 }
Chris@0 206 }
Chris@0 207 }
Chris@0 208
Chris@0 209 void
Chris@0 210 TimeValueLayer::setBaseColour(QColor colour)
Chris@0 211 {
Chris@0 212 if (m_colour == colour) return;
Chris@0 213 m_colour = colour;
Chris@0 214 emit layerParametersChanged();
Chris@0 215 }
Chris@0 216
Chris@0 217 void
Chris@0 218 TimeValueLayer::setPlotStyle(PlotStyle style)
Chris@0 219 {
Chris@0 220 if (m_plotStyle == style) return;
Chris@0 221 m_plotStyle = style;
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@101 261 if (!m_model || m_verticalScale == AutoAlignScale) 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@44 291 } else if (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@101 433 if (m_verticalScale == AutoAlignScale) {
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@101 439 min = (min < 0.0) ? -log10(-min) : (min == 0.0) ? 0.0 : log10(min);
Chris@101 440 max = (max < 0.0) ? -log10(-max) : (max == 0.0) ? 0.0 : log10(max);
Chris@101 441 }
Chris@101 442
Chris@101 443 } else if (m_verticalScale == PlusMinusOneScale) {
Chris@101 444
Chris@101 445 min = -1.0;
Chris@101 446 max = 1.0;
Chris@101 447
Chris@101 448 } else {
Chris@101 449
Chris@101 450 min = m_model->getValueMinimum();
Chris@101 451 max = m_model->getValueMaximum();
Chris@101 452
Chris@101 453 if (m_verticalScale == LogScale) {
Chris@101 454 min = (min < 0.0) ? -log10(-min) : (min == 0.0) ? 0.0 : log10(min);
Chris@101 455 max = (max < 0.0) ? -log10(-max) : (max == 0.0) ? 0.0 : log10(max);
Chris@101 456 log = true;
Chris@101 457 }
Chris@101 458 }
Chris@101 459
Chris@101 460 if (max == min) max = min + 1.0;
Chris@101 461 }
Chris@101 462
Chris@21 463 int
Chris@66 464 TimeValueLayer::getYForValue(View *v, float val) const
Chris@21 465 {
Chris@79 466 float min = 0.0, max = 0.0;
Chris@101 467 bool logarithmic = false;
Chris@79 468 int h = v->height();
Chris@79 469
Chris@101 470 getScaleExtents(v, min, max, logarithmic);
Chris@101 471
Chris@101 472 // std::cerr << "getYForValue(" << val << "): min " << min << ", max "
Chris@101 473 // << max << ", log " << logarithmic << std::endl;
Chris@101 474
Chris@101 475 if (logarithmic) {
Chris@101 476 val = (val < 0.0) ? -log10(-val) : (val == 0.0) ? 0.0 : log10(val);
Chris@79 477 }
Chris@79 478
Chris@66 479 return int(h - ((val - min) * h) / (max - min));
Chris@21 480 }
Chris@21 481
Chris@21 482 float
Chris@44 483 TimeValueLayer::getValueForY(View *v, int y) const
Chris@21 484 {
Chris@101 485 float min = 0.0, max = 0.0;
Chris@101 486 bool logarithmic = false;
Chris@44 487 int h = v->height();
Chris@21 488
Chris@101 489 getScaleExtents(v, min, max, logarithmic);
Chris@101 490
Chris@101 491 float val = min + (float(h - y) * float(max - min)) / h;
Chris@101 492
Chris@101 493 if (logarithmic) {
Chris@101 494 val = pow(10, val);
Chris@101 495 }
Chris@101 496
Chris@101 497 return val;
Chris@21 498 }
Chris@21 499
Chris@68 500 QColor
Chris@101 501 TimeValueLayer::getColourForValue(View *v, float val) const
Chris@68 502 {
Chris@101 503 float min, max;
Chris@101 504 bool log;
Chris@101 505 getScaleExtents(v, min, max, log);
Chris@68 506
Chris@101 507 if (log) {
Chris@68 508 val = (val < 0.0) ? -log10(-val) : (val == 0.0) ? 0.0 : log10(val);
Chris@68 509 }
Chris@68 510
Chris@68 511 int iv = ((val - min) / (max - min)) * 255.999;
Chris@68 512
Chris@68 513 QColor colour = QColor::fromHsv(256 - iv, iv / 2 + 128, iv);
Chris@68 514 return QColor(colour.red(), colour.green(), colour.blue(), 120);
Chris@68 515 }
Chris@68 516
Chris@0 517 void
Chris@44 518 TimeValueLayer::paint(View *v, QPainter &paint, QRect rect) const
Chris@0 519 {
Chris@0 520 if (!m_model || !m_model->isOK()) return;
Chris@0 521
Chris@0 522 int sampleRate = m_model->getSampleRate();
Chris@0 523 if (!sampleRate) return;
Chris@0 524
Chris@0 525 // Profiler profiler("TimeValueLayer::paint", true);
Chris@0 526
Chris@0 527 int x0 = rect.left(), x1 = rect.right();
Chris@44 528 long frame0 = v->getFrameForX(x0);
Chris@44 529 long frame1 = v->getFrameForX(x1);
Chris@0 530
Chris@0 531 SparseTimeValueModel::PointList points(m_model->getPoints
Chris@0 532 (frame0, frame1));
Chris@11 533 if (points.empty()) return;
Chris@0 534
Chris@0 535 paint.setPen(m_colour);
Chris@0 536
Chris@0 537 QColor brushColour(m_colour);
Chris@0 538 brushColour.setAlpha(80);
Chris@0 539 paint.setBrush(brushColour);
Chris@0 540
Chris@0 541 // std::cerr << "TimeValueLayer::paint: resolution is "
Chris@0 542 // << m_model->getResolution() << " frames" << std::endl;
Chris@0 543
Chris@0 544 float min = m_model->getValueMinimum();
Chris@0 545 float max = m_model->getValueMaximum();
Chris@0 546 if (max == min) max = min + 1.0;
Chris@0 547
Chris@44 548 int origin = int(nearbyint(v->height() -
Chris@44 549 (-min * v->height()) / (max - min)));
Chris@0 550
Chris@0 551 QPoint localPos;
Chris@0 552 long illuminateFrame = -1;
Chris@0 553
Chris@44 554 if (v->shouldIlluminateLocalFeatures(this, localPos)) {
Chris@0 555 SparseTimeValueModel::PointList localPoints =
Chris@44 556 getLocalPoints(v, localPos.x());
Chris@0 557 if (!localPoints.empty()) illuminateFrame = localPoints.begin()->frame;
Chris@0 558 }
Chris@6 559
Chris@20 560 int w =
Chris@44 561 v->getXForFrame(frame0 + m_model->getResolution()) -
Chris@44 562 v->getXForFrame(frame0);
Chris@7 563
Chris@6 564 paint.save();
Chris@6 565
Chris@6 566 QPainterPath path;
Chris@55 567 int pointCount = 0;
Chris@79 568
Chris@79 569 int textY = 0;
Chris@79 570 if (m_plotStyle == PlotSegmentation) {
Chris@79 571 textY = v->getTextLabelHeight(this, paint);
Chris@79 572 }
Chris@6 573
Chris@0 574 for (SparseTimeValueModel::PointList::const_iterator i = points.begin();
Chris@0 575 i != points.end(); ++i) {
Chris@0 576
Chris@0 577 const SparseTimeValueModel::Point &p(*i);
Chris@0 578
Chris@44 579 int x = v->getXForFrame(p.frame);
Chris@44 580 int y = getYForValue(v, p.value);
Chris@0 581
Chris@79 582 if (m_plotStyle != PlotSegmentation) {
Chris@79 583 textY = y - paint.fontMetrics().height()
Chris@79 584 + paint.fontMetrics().ascent();
Chris@79 585 }
Chris@79 586
Chris@34 587 bool haveNext = false;
Chris@76 588 int nx = v->getXForFrame(v->getModelsEndFrame());
Chris@76 589 // m_model->getEndFrame());
Chris@34 590 int ny = y;
Chris@34 591
Chris@34 592 SparseTimeValueModel::PointList::const_iterator j = i;
Chris@34 593 ++j;
Chris@34 594
Chris@34 595 if (j != points.end()) {
Chris@34 596 const SparseTimeValueModel::Point &q(*j);
Chris@44 597 nx = v->getXForFrame(q.frame);
Chris@44 598 ny = getYForValue(v, q.value);
Chris@34 599 haveNext = true;
Chris@76 600 }
Chris@76 601
Chris@76 602 // std::cout << "frame = " << p.frame << ", x = " << x << ", haveNext = " << haveNext
Chris@76 603 // << ", nx = " << nx << std::endl;
Chris@34 604
Chris@34 605 int labelY = y;
Chris@34 606
Chris@0 607 if (w < 1) w = 1;
Chris@26 608 paint.setPen(m_colour);
Chris@0 609
Chris@26 610 if (m_plotStyle == PlotSegmentation) {
Chris@101 611 paint.setBrush(getColourForValue(v, p.value));
Chris@44 612 labelY = v->height();
Chris@26 613 } else if (m_plotStyle == PlotLines ||
Chris@26 614 m_plotStyle == PlotCurve) {
Chris@6 615 paint.setBrush(Qt::NoBrush);
Chris@3 616 } else {
Chris@6 617 paint.setBrush(brushColour);
Chris@3 618 }
Chris@0 619
Chris@0 620 if (m_plotStyle == PlotStems) {
Chris@0 621 paint.setPen(brushColour);
Chris@0 622 if (y < origin - 1) {
Chris@0 623 paint.drawRect(x + w/2, y + 1, 1, origin - y);
Chris@0 624 } else if (y > origin + 1) {
Chris@0 625 paint.drawRect(x + w/2, origin, 1, y - origin - 1);
Chris@0 626 }
Chris@0 627 paint.setPen(m_colour);
Chris@0 628 }
Chris@0 629
Chris@0 630 if (illuminateFrame == p.frame) {
Chris@6 631
Chris@0 632 //!!! aside from the problem of choosing a colour, it'd be
Chris@0 633 //better to save the highlighted rects and draw them at
Chris@0 634 //the end perhaps
Chris@6 635
Chris@6 636 //!!! not equipped to illuminate the right section in line
Chris@6 637 //or curve mode
Chris@6 638
Chris@6 639 if (m_plotStyle != PlotCurve &&
Chris@6 640 m_plotStyle != PlotLines) {
Chris@6 641 paint.setPen(Qt::black);//!!!
Chris@26 642 if (m_plotStyle != PlotSegmentation) {
Chris@26 643 paint.setBrush(Qt::black);//!!!
Chris@26 644 }
Chris@6 645 }
Chris@0 646 }
Chris@0 647
Chris@6 648 if (m_plotStyle != PlotLines &&
Chris@26 649 m_plotStyle != PlotCurve &&
Chris@26 650 m_plotStyle != PlotSegmentation) {
Chris@3 651 paint.drawRect(x, y - 1, w, 2);
Chris@3 652 }
Chris@0 653
Chris@6 654 if (m_plotStyle == PlotConnectedPoints ||
Chris@6 655 m_plotStyle == PlotLines ||
Chris@6 656 m_plotStyle == PlotCurve) {
Chris@0 657
Chris@34 658 if (haveNext) {
Chris@3 659
Chris@6 660 if (m_plotStyle == PlotConnectedPoints) {
Chris@34 661
Chris@79 662 paint.save();
Chris@3 663 paint.setPen(brushColour);
Chris@3 664 paint.drawLine(x + w, y, nx, ny);
Chris@79 665 paint.restore();
Chris@6 666
Chris@6 667 } else if (m_plotStyle == PlotLines) {
Chris@6 668
Chris@6 669 paint.drawLine(x + w/2, y, nx + w/2, ny);
Chris@6 670
Chris@3 671 } else {
Chris@6 672
Chris@55 673 float x0 = x + float(w)/2;
Chris@55 674 float x1 = nx + float(w)/2;
Chris@55 675
Chris@55 676 float y0 = y;
Chris@55 677 float y1 = ny;
Chris@55 678
Chris@55 679 if (pointCount == 0) {
Chris@55 680 path.moveTo((x0 + x1) / 2, (y0 + y1) / 2);
Chris@6 681 }
Chris@55 682 ++pointCount;
Chris@6 683
Chris@6 684 if (nx - x > 5) {
Chris@55 685 path.cubicTo(x0, y0,
Chris@55 686 x0, y0,
Chris@55 687 (x0 + x1) / 2, (y0 + y1) / 2);
Chris@55 688
Chris@55 689 // // or
Chris@55 690 // path.quadTo(x0, y0, (x0 + x1) / 2, (y0 + y1) / 2);
Chris@55 691
Chris@6 692 } else {
Chris@55 693 path.lineTo((x0 + x1) / 2, (y0 + y1) / 2);
Chris@6 694 }
Chris@3 695 }
Chris@0 696 }
Chris@0 697 }
Chris@0 698
Chris@26 699 if (m_plotStyle == PlotSegmentation) {
Chris@76 700
Chris@76 701 // std::cerr << "drawing rect" << std::endl;
Chris@26 702
Chris@27 703 if (nx <= x) continue;
Chris@26 704
Chris@28 705 if (illuminateFrame != p.frame &&
Chris@44 706 (nx < x + 5 || x >= v->width() - 1)) {
Chris@27 707 paint.setPen(Qt::NoPen);
Chris@27 708 }
Chris@26 709
Chris@44 710 paint.drawRect(x, -1, nx - x, v->height() + 1);
Chris@26 711 }
Chris@26 712
Chris@55 713 if (p.label != "") {
Chris@79 714 if (!haveNext || nx > x + 6 + paint.fontMetrics().width(p.label)) {
Chris@79 715 paint.drawText(x + 5, textY, p.label);
Chris@79 716 }
Chris@55 717 }
Chris@0 718 }
Chris@6 719
Chris@6 720 if (m_plotStyle == PlotCurve && !path.isEmpty()) {
Chris@55 721 paint.setRenderHint(QPainter::Antialiasing, pointCount <= v->width());
Chris@6 722 paint.drawPath(path);
Chris@6 723 }
Chris@6 724
Chris@6 725 paint.restore();
Chris@6 726
Chris@6 727 // looks like save/restore doesn't deal with this:
Chris@6 728 paint.setRenderHint(QPainter::Antialiasing, false);
Chris@6 729 }
Chris@6 730
Chris@42 731 int
Chris@44 732 TimeValueLayer::getVerticalScaleWidth(View *v, QPainter &paint) const
Chris@42 733 {
Chris@68 734 int w = paint.fontMetrics().width("-000.000");
Chris@68 735 if (m_plotStyle == PlotSegmentation) return w + 20;
Chris@68 736 else return w + 10;
Chris@42 737 }
Chris@42 738
Chris@42 739 void
Chris@44 740 TimeValueLayer::paintVerticalScale(View *v, QPainter &paint, QRect rect) const
Chris@42 741 {
Chris@42 742 if (!m_model) return;
Chris@42 743
Chris@68 744 int h = v->height();
Chris@68 745
Chris@68 746 int n = 10;
Chris@68 747
Chris@68 748 float max = m_model->getValueMaximum();
Chris@68 749 float min = m_model->getValueMinimum();
Chris@68 750 float val = min;
Chris@68 751 float inc = (max - val) / n;
Chris@42 752
Chris@55 753 char buffer[40];
Chris@55 754
Chris@55 755 int w = getVerticalScaleWidth(v, paint);
Chris@55 756
Chris@68 757 int tx = 5;
Chris@68 758
Chris@68 759 int boxx = 5, boxy = 5;
Chris@68 760 if (m_model->getScaleUnits() != "") {
Chris@68 761 boxy += paint.fontMetrics().height();
Chris@68 762 }
Chris@68 763 int boxw = 10, boxh = h - boxy - 5;
Chris@68 764
Chris@68 765 if (m_plotStyle == PlotSegmentation) {
Chris@68 766 tx += boxx + boxw;
Chris@68 767 paint.drawRect(boxx, boxy, boxw, boxh);
Chris@68 768 }
Chris@68 769
Chris@68 770 if (m_plotStyle == PlotSegmentation) {
Chris@68 771 paint.save();
Chris@68 772 for (int y = 0; y < boxh; ++y) {
Chris@68 773 float val = ((boxh - y) * (max - min)) / boxh + min;
Chris@101 774 paint.setPen(getColourForValue(v, val));
Chris@68 775 paint.drawLine(boxx + 1, y + boxy + 1, boxx + boxw, y + boxy + 1);
Chris@68 776 }
Chris@68 777 paint.restore();
Chris@68 778 }
Chris@68 779
Chris@68 780 for (int i = 0; i < n; ++i) {
Chris@68 781
Chris@68 782 int y, ty;
Chris@68 783 bool drawText = true;
Chris@68 784
Chris@68 785 if (m_plotStyle == PlotSegmentation) {
Chris@68 786 y = boxy + int(boxh - ((val - min) * boxh) / (max - min));
Chris@68 787 ty = y;
Chris@68 788 } else {
Chris@68 789 if (i == n-1) {
Chris@68 790 if (m_model->getScaleUnits() != "") drawText = false;
Chris@68 791 }
Chris@68 792 y = getYForValue(v, val);
Chris@68 793 ty = y - paint.fontMetrics().height() +
Chris@68 794 paint.fontMetrics().ascent();
Chris@68 795 }
Chris@68 796
Chris@68 797 sprintf(buffer, "%.3f", val);
Chris@55 798 QString label = QString(buffer);
Chris@68 799
Chris@68 800 if (m_plotStyle != PlotSegmentation) {
Chris@68 801 paint.drawLine(w - 5, y, w, y);
Chris@68 802 } else {
Chris@68 803 paint.drawLine(boxx + boxw - boxw/3, y, boxx + boxw, y);
Chris@68 804 }
Chris@68 805
Chris@68 806 if (drawText) paint.drawText(tx, ty, label);
Chris@44 807 val += inc;
Chris@42 808 }
Chris@68 809
Chris@67 810 if (m_model->getScaleUnits() != "") {
Chris@67 811 paint.drawText(5, 5 + paint.fontMetrics().ascent(),
Chris@67 812 m_model->getScaleUnits());
Chris@67 813 }
Chris@42 814 }
Chris@42 815
Chris@21 816 void
Chris@44 817 TimeValueLayer::drawStart(View *v, QMouseEvent *e)
Chris@21 818 {
Chris@101 819 // std::cerr << "TimeValueLayer::drawStart(" << e->x() << "," << e->y() << ")" << std::endl;
Chris@21 820
Chris@21 821 if (!m_model) return;
Chris@21 822
Chris@44 823 long frame = v->getFrameForX(e->x());
Chris@76 824 long resolution = m_model->getResolution();
Chris@21 825 if (frame < 0) frame = 0;
Chris@76 826 frame = (frame / resolution) * resolution;
Chris@21 827
Chris@44 828 float value = getValueForY(v, e->y());
Chris@21 829
Chris@76 830 bool havePoint = false;
Chris@76 831
Chris@76 832 SparseTimeValueModel::PointList points = getLocalPoints(v, e->x());
Chris@76 833 if (!points.empty()) {
Chris@76 834 for (SparseTimeValueModel::PointList::iterator i = points.begin();
Chris@76 835 i != points.end(); ++i) {
Chris@76 836 if (((i->frame / resolution) * resolution) != frame) {
Chris@101 837 // std::cerr << "ignoring out-of-range frame at " << i->frame << std::endl;
Chris@76 838 continue;
Chris@76 839 }
Chris@76 840 m_editingPoint = *i;
Chris@76 841 havePoint = true;
Chris@76 842 }
Chris@76 843 }
Chris@76 844
Chris@76 845 if (!havePoint) {
Chris@76 846 m_editingPoint = SparseTimeValueModel::Point
Chris@76 847 (frame, value, tr("New Point"));
Chris@76 848 }
Chris@76 849
Chris@23 850 m_originalPoint = m_editingPoint;
Chris@22 851
Chris@22 852 if (m_editingCommand) m_editingCommand->finish();
Chris@22 853 m_editingCommand = new SparseTimeValueModel::EditCommand(m_model,
Chris@22 854 tr("Draw Point"));
Chris@76 855 if (!havePoint) {
Chris@76 856 m_editingCommand->addPoint(m_editingPoint);
Chris@76 857 }
Chris@22 858
Chris@21 859 m_editing = true;
Chris@21 860 }
Chris@21 861
Chris@21 862 void
Chris@44 863 TimeValueLayer::drawDrag(View *v, QMouseEvent *e)
Chris@21 864 {
Chris@101 865 // std::cerr << "TimeValueLayer::drawDrag(" << e->x() << "," << e->y() << ")" << std::endl;
Chris@21 866
Chris@21 867 if (!m_model || !m_editing) return;
Chris@21 868
Chris@44 869 long frame = v->getFrameForX(e->x());
Chris@76 870 long resolution = m_model->getResolution();
Chris@21 871 if (frame < 0) frame = 0;
Chris@76 872 frame = (frame / resolution) * resolution;
Chris@21 873
Chris@44 874 float value = getValueForY(v, e->y());
Chris@21 875
Chris@76 876 SparseTimeValueModel::PointList points = getLocalPoints(v, e->x());
Chris@76 877
Chris@101 878 // std::cerr << points.size() << " points" << std::endl;
Chris@76 879
Chris@76 880 bool havePoint = false;
Chris@76 881
Chris@76 882 if (!points.empty()) {
Chris@76 883 for (SparseTimeValueModel::PointList::iterator i = points.begin();
Chris@76 884 i != points.end(); ++i) {
Chris@76 885 if (i->frame == m_editingPoint.frame &&
Chris@76 886 i->value == m_editingPoint.value) {
Chris@101 887 // std::cerr << "ignoring current editing point at " << i->frame << ", " << i->value << std::endl;
Chris@76 888 continue;
Chris@76 889 }
Chris@76 890 if (((i->frame / resolution) * resolution) != frame) {
Chris@101 891 // std::cerr << "ignoring out-of-range frame at " << i->frame << std::endl;
Chris@76 892 continue;
Chris@76 893 }
Chris@101 894 // std::cerr << "adjusting to new point at " << i->frame << ", " << i->value << std::endl;
Chris@76 895 m_editingPoint = *i;
Chris@76 896 m_originalPoint = m_editingPoint;
Chris@76 897 m_editingCommand->deletePoint(m_editingPoint);
Chris@76 898 havePoint = true;
Chris@76 899 }
Chris@76 900 }
Chris@76 901
Chris@76 902 if (!havePoint) {
Chris@76 903 if (frame == m_editingPoint.frame) {
Chris@76 904 m_editingCommand->deletePoint(m_editingPoint);
Chris@76 905 }
Chris@76 906 }
Chris@76 907
Chris@76 908 // m_editingCommand->deletePoint(m_editingPoint);
Chris@21 909 m_editingPoint.frame = frame;
Chris@21 910 m_editingPoint.value = value;
Chris@22 911 m_editingCommand->addPoint(m_editingPoint);
Chris@21 912 }
Chris@21 913
Chris@21 914 void
Chris@44 915 TimeValueLayer::drawEnd(View *v, QMouseEvent *e)
Chris@21 916 {
Chris@101 917 // std::cerr << "TimeValueLayer::drawEnd(" << e->x() << "," << e->y() << ")" << std::endl;
Chris@21 918 if (!m_model || !m_editing) return;
Chris@22 919 m_editingCommand->finish();
Chris@22 920 m_editingCommand = 0;
Chris@21 921 m_editing = false;
Chris@21 922 }
Chris@21 923
Chris@21 924 void
Chris@44 925 TimeValueLayer::editStart(View *v, QMouseEvent *e)
Chris@21 926 {
Chris@101 927 // std::cerr << "TimeValueLayer::editStart(" << e->x() << "," << e->y() << ")" << std::endl;
Chris@21 928
Chris@21 929 if (!m_model) return;
Chris@21 930
Chris@44 931 SparseTimeValueModel::PointList points = getLocalPoints(v, e->x());
Chris@21 932 if (points.empty()) return;
Chris@21 933
Chris@21 934 m_editingPoint = *points.begin();
Chris@23 935 m_originalPoint = m_editingPoint;
Chris@22 936
Chris@22 937 if (m_editingCommand) {
Chris@22 938 m_editingCommand->finish();
Chris@22 939 m_editingCommand = 0;
Chris@22 940 }
Chris@22 941
Chris@21 942 m_editing = true;
Chris@21 943 }
Chris@21 944
Chris@21 945 void
Chris@44 946 TimeValueLayer::editDrag(View *v, QMouseEvent *e)
Chris@21 947 {
Chris@101 948 // std::cerr << "TimeValueLayer::editDrag(" << e->x() << "," << e->y() << ")" << std::endl;
Chris@21 949
Chris@21 950 if (!m_model || !m_editing) return;
Chris@21 951
Chris@44 952 long frame = v->getFrameForX(e->x());
Chris@21 953 if (frame < 0) frame = 0;
Chris@21 954 frame = frame / m_model->getResolution() * m_model->getResolution();
Chris@21 955
Chris@44 956 float value = getValueForY(v, e->y());
Chris@21 957
Chris@22 958 if (!m_editingCommand) {
Chris@22 959 m_editingCommand = new SparseTimeValueModel::EditCommand(m_model,
Chris@22 960 tr("Drag Point"));
Chris@22 961 }
Chris@22 962
Chris@22 963 m_editingCommand->deletePoint(m_editingPoint);
Chris@21 964 m_editingPoint.frame = frame;
Chris@21 965 m_editingPoint.value = value;
Chris@22 966 m_editingCommand->addPoint(m_editingPoint);
Chris@21 967 }
Chris@21 968
Chris@21 969 void
Chris@44 970 TimeValueLayer::editEnd(View *v, QMouseEvent *e)
Chris@21 971 {
Chris@101 972 // std::cerr << "TimeValueLayer::editEnd(" << e->x() << "," << e->y() << ")" << std::endl;
Chris@21 973 if (!m_model || !m_editing) return;
Chris@23 974
Chris@23 975 if (m_editingCommand) {
Chris@23 976
Chris@23 977 QString newName = m_editingCommand->getName();
Chris@23 978
Chris@23 979 if (m_editingPoint.frame != m_originalPoint.frame) {
Chris@23 980 if (m_editingPoint.value != m_originalPoint.value) {
Chris@23 981 newName = tr("Edit Point");
Chris@23 982 } else {
Chris@23 983 newName = tr("Relocate Point");
Chris@23 984 }
Chris@23 985 } else {
Chris@23 986 newName = tr("Change Point Value");
Chris@23 987 }
Chris@23 988
Chris@23 989 m_editingCommand->setName(newName);
Chris@23 990 m_editingCommand->finish();
Chris@23 991 }
Chris@23 992
Chris@22 993 m_editingCommand = 0;
Chris@21 994 m_editing = false;
Chris@21 995 }
Chris@21 996
Chris@43 997 void
Chris@70 998 TimeValueLayer::editOpen(View *v, QMouseEvent *e)
Chris@70 999 {
Chris@70 1000 if (!m_model) return;
Chris@70 1001
Chris@70 1002 SparseTimeValueModel::PointList points = getLocalPoints(v, e->x());
Chris@70 1003 if (points.empty()) return;
Chris@70 1004
Chris@70 1005 SparseTimeValueModel::Point point = *points.begin();
Chris@70 1006
Chris@70 1007 ItemEditDialog *dialog = new ItemEditDialog
Chris@70 1008 (m_model->getSampleRate(),
Chris@70 1009 ItemEditDialog::ShowTime |
Chris@70 1010 ItemEditDialog::ShowValue |
Chris@73 1011 ItemEditDialog::ShowText,
Chris@73 1012 m_model->getScaleUnits());
Chris@70 1013
Chris@70 1014 dialog->setFrameTime(point.frame);
Chris@70 1015 dialog->setValue(point.value);
Chris@70 1016 dialog->setText(point.label);
Chris@70 1017
Chris@70 1018 if (dialog->exec() == QDialog::Accepted) {
Chris@70 1019
Chris@70 1020 SparseTimeValueModel::Point newPoint = point;
Chris@70 1021 newPoint.frame = dialog->getFrameTime();
Chris@70 1022 newPoint.value = dialog->getValue();
Chris@70 1023 newPoint.label = dialog->getText();
Chris@70 1024
Chris@70 1025 SparseTimeValueModel::EditCommand *command =
Chris@70 1026 new SparseTimeValueModel::EditCommand(m_model, tr("Edit Point"));
Chris@70 1027 command->deletePoint(point);
Chris@70 1028 command->addPoint(newPoint);
Chris@70 1029 command->finish();
Chris@70 1030 }
Chris@70 1031
Chris@70 1032 delete dialog;
Chris@70 1033 }
Chris@70 1034
Chris@70 1035 void
Chris@43 1036 TimeValueLayer::moveSelection(Selection s, size_t newStartFrame)
Chris@43 1037 {
Chris@99 1038 if (!m_model) return;
Chris@99 1039
Chris@43 1040 SparseTimeValueModel::EditCommand *command =
Chris@43 1041 new SparseTimeValueModel::EditCommand(m_model,
Chris@43 1042 tr("Drag Selection"));
Chris@43 1043
Chris@43 1044 SparseTimeValueModel::PointList points =
Chris@43 1045 m_model->getPoints(s.getStartFrame(), s.getEndFrame());
Chris@43 1046
Chris@43 1047 for (SparseTimeValueModel::PointList::iterator i = points.begin();
Chris@43 1048 i != points.end(); ++i) {
Chris@43 1049
Chris@43 1050 if (s.contains(i->frame)) {
Chris@43 1051 SparseTimeValueModel::Point newPoint(*i);
Chris@43 1052 newPoint.frame = i->frame + newStartFrame - s.getStartFrame();
Chris@43 1053 command->deletePoint(*i);
Chris@43 1054 command->addPoint(newPoint);
Chris@43 1055 }
Chris@43 1056 }
Chris@43 1057
Chris@43 1058 command->finish();
Chris@43 1059 }
Chris@43 1060
Chris@43 1061 void
Chris@43 1062 TimeValueLayer::resizeSelection(Selection s, Selection newSize)
Chris@43 1063 {
Chris@99 1064 if (!m_model) return;
Chris@99 1065
Chris@43 1066 SparseTimeValueModel::EditCommand *command =
Chris@43 1067 new SparseTimeValueModel::EditCommand(m_model,
Chris@43 1068 tr("Resize Selection"));
Chris@43 1069
Chris@43 1070 SparseTimeValueModel::PointList points =
Chris@43 1071 m_model->getPoints(s.getStartFrame(), s.getEndFrame());
Chris@43 1072
Chris@43 1073 double ratio =
Chris@43 1074 double(newSize.getEndFrame() - newSize.getStartFrame()) /
Chris@43 1075 double(s.getEndFrame() - s.getStartFrame());
Chris@43 1076
Chris@43 1077 for (SparseTimeValueModel::PointList::iterator i = points.begin();
Chris@43 1078 i != points.end(); ++i) {
Chris@43 1079
Chris@43 1080 if (s.contains(i->frame)) {
Chris@43 1081
Chris@43 1082 double target = i->frame;
Chris@43 1083 target = newSize.getStartFrame() +
Chris@43 1084 double(target - s.getStartFrame()) * ratio;
Chris@43 1085
Chris@43 1086 SparseTimeValueModel::Point newPoint(*i);
Chris@43 1087 newPoint.frame = lrint(target);
Chris@43 1088 command->deletePoint(*i);
Chris@43 1089 command->addPoint(newPoint);
Chris@43 1090 }
Chris@43 1091 }
Chris@43 1092
Chris@43 1093 command->finish();
Chris@43 1094 }
Chris@43 1095
Chris@76 1096 void
Chris@76 1097 TimeValueLayer::deleteSelection(Selection s)
Chris@76 1098 {
Chris@99 1099 if (!m_model) return;
Chris@99 1100
Chris@76 1101 SparseTimeValueModel::EditCommand *command =
Chris@76 1102 new SparseTimeValueModel::EditCommand(m_model,
Chris@76 1103 tr("Delete Selected Points"));
Chris@76 1104
Chris@76 1105 SparseTimeValueModel::PointList points =
Chris@76 1106 m_model->getPoints(s.getStartFrame(), s.getEndFrame());
Chris@76 1107
Chris@76 1108 for (SparseTimeValueModel::PointList::iterator i = points.begin();
Chris@76 1109 i != points.end(); ++i) {
Chris@76 1110
Chris@76 1111 if (s.contains(i->frame)) {
Chris@76 1112 command->deletePoint(*i);
Chris@76 1113 }
Chris@76 1114 }
Chris@76 1115
Chris@76 1116 command->finish();
Chris@76 1117 }
Chris@76 1118
Chris@76 1119 void
Chris@76 1120 TimeValueLayer::copy(Selection s, Clipboard &to)
Chris@76 1121 {
Chris@99 1122 if (!m_model) return;
Chris@99 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 if (s.contains(i->frame)) {
Chris@83 1130 Clipboard::Point point(i->frame, i->value, i->label);
Chris@76 1131 to.addPoint(point);
Chris@76 1132 }
Chris@76 1133 }
Chris@76 1134 }
Chris@76 1135
Chris@125 1136 bool
Chris@125 1137 TimeValueLayer::paste(const Clipboard &from, int frameOffset,
Chris@125 1138 bool interactive)
Chris@76 1139 {
Chris@125 1140 if (!m_model) return false;
Chris@99 1141
Chris@76 1142 const Clipboard::PointList &points = from.getPoints();
Chris@76 1143
Chris@76 1144 SparseTimeValueModel::EditCommand *command =
Chris@76 1145 new SparseTimeValueModel::EditCommand(m_model, tr("Paste"));
Chris@76 1146
Chris@125 1147 enum ValueAvailability {
Chris@125 1148 UnknownAvailability,
Chris@125 1149 NoValues,
Chris@125 1150 SomeValues,
Chris@125 1151 AllValues
Chris@125 1152 };
Chris@125 1153 enum ValueGeneration {
Chris@125 1154 GenerateNone,
Chris@125 1155 GenerateFromCounter,
Chris@125 1156 GenerateFromFrameNumber,
Chris@125 1157 GenerateFromRealTime,
Chris@125 1158 GenerateFromRealTimeDifference,
Chris@125 1159 GenerateFromTempo,
Chris@125 1160 GenerateFromExistingNeighbour,
Chris@125 1161 GenerateFromLabels
Chris@125 1162 };
Chris@125 1163
Chris@125 1164 ValueGeneration generation = GenerateNone;
Chris@125 1165
Chris@125 1166 bool haveUsableLabels = false;
Chris@125 1167 bool haveExistingItems = !(m_model->isEmpty());
Chris@125 1168
Chris@125 1169 if (interactive) {
Chris@125 1170
Chris@125 1171 ValueAvailability availability = UnknownAvailability;
Chris@125 1172
Chris@125 1173 for (Clipboard::PointList::const_iterator i = points.begin();
Chris@125 1174 i != points.end(); ++i) {
Chris@125 1175
Chris@125 1176 if (!i->haveFrame()) continue;
Chris@125 1177
Chris@125 1178 if (availability == UnknownAvailability) {
Chris@125 1179 if (i->haveValue()) availability = AllValues;
Chris@125 1180 else availability = NoValues;
Chris@125 1181 continue;
Chris@125 1182 }
Chris@125 1183
Chris@125 1184 if (i->haveValue()) {
Chris@125 1185 if (availability == NoValues) {
Chris@125 1186 availability = SomeValues;
Chris@125 1187 }
Chris@125 1188 } else {
Chris@125 1189 if (availability == AllValues) {
Chris@125 1190 availability = SomeValues;
Chris@125 1191 }
Chris@125 1192 }
Chris@125 1193
Chris@125 1194 if (!haveUsableLabels) {
Chris@125 1195 if (i->haveLabel()) {
Chris@125 1196 if (i->getLabel().contains(QRegExp("[0-9]"))) {
Chris@125 1197 haveUsableLabels = true;
Chris@125 1198 }
Chris@125 1199 }
Chris@125 1200 }
Chris@125 1201
Chris@125 1202 if (availability == SomeValues && haveUsableLabels) break;
Chris@125 1203 }
Chris@125 1204
Chris@125 1205 if (availability == NoValues || availability == SomeValues) {
Chris@125 1206
Chris@125 1207 QString text;
Chris@125 1208 if (availability == NoValues) {
Chris@125 1209 text = tr("The items you are pasting do not have values.\nWhat values do you want to use for these items?");
Chris@125 1210 } else {
Chris@125 1211 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 1212 }
Chris@125 1213
Chris@125 1214 QStringList options;
Chris@125 1215 std::vector<int> genopts;
Chris@125 1216
Chris@125 1217 options << tr("Zero for all items");
Chris@125 1218 genopts.push_back(int(GenerateNone));
Chris@125 1219
Chris@125 1220 options << tr("Whole numbers counting from 1");
Chris@125 1221 genopts.push_back(int(GenerateFromCounter));
Chris@125 1222
Chris@125 1223 options << tr("Item's audio sample frame number");
Chris@125 1224 genopts.push_back(int(GenerateFromFrameNumber));
Chris@125 1225
Chris@125 1226 options << tr("Item's time in seconds");
Chris@125 1227 genopts.push_back(int(GenerateFromRealTime));
Chris@125 1228
Chris@125 1229 options << tr("Duration from the item to the following item");
Chris@125 1230 genopts.push_back(int(GenerateFromRealTimeDifference));
Chris@125 1231
Chris@125 1232 options << tr("Tempo in bpm derived from the duration");
Chris@125 1233 genopts.push_back(int(GenerateFromTempo));
Chris@125 1234
Chris@125 1235 if (haveExistingItems) {
Chris@125 1236 options << tr("Value of the nearest existing item");
Chris@125 1237 genopts.push_back(int(GenerateFromExistingNeighbour));
Chris@125 1238 }
Chris@125 1239
Chris@125 1240 if (haveUsableLabels) {
Chris@125 1241 options << tr("Value extracted from the item's label (where possible)");
Chris@125 1242 genopts.push_back(int(GenerateFromLabels));
Chris@125 1243 }
Chris@125 1244
Chris@125 1245
Chris@125 1246 static int prevSelection = 0;
Chris@125 1247
Chris@125 1248 bool ok = false;
Chris@125 1249 QString selected = ListInputDialog::getItem
Chris@125 1250 (0, tr("Choose value calculation"),
Chris@125 1251 text, options, prevSelection, &ok);
Chris@125 1252
Chris@125 1253 if (!ok) return false;
Chris@125 1254 int selection = 0;
Chris@125 1255 generation = GenerateNone;
Chris@125 1256
Chris@125 1257 for (QStringList::const_iterator i = options.begin();
Chris@125 1258 i != options.end(); ++i) {
Chris@125 1259 if (selected == *i) {
Chris@125 1260 generation = ValueGeneration(genopts[selection]);
Chris@125 1261 break;
Chris@125 1262 }
Chris@125 1263 ++selection;
Chris@125 1264 }
Chris@125 1265
Chris@125 1266 prevSelection = selection;
Chris@125 1267 }
Chris@125 1268 }
Chris@125 1269
Chris@125 1270 int counter = 1;
Chris@125 1271 float prevBpm = 120.f;
Chris@125 1272
Chris@76 1273 for (Clipboard::PointList::const_iterator i = points.begin();
Chris@76 1274 i != points.end(); ++i) {
Chris@76 1275
Chris@76 1276 if (!i->haveFrame()) continue;
Chris@76 1277 size_t frame = 0;
Chris@76 1278 if (frameOffset > 0 || -frameOffset < i->getFrame()) {
Chris@76 1279 frame = i->getFrame() + frameOffset;
Chris@76 1280 }
Chris@76 1281 SparseTimeValueModel::Point newPoint(frame);
Chris@76 1282
Chris@125 1283 if (i->haveLabel()) {
Chris@125 1284 newPoint.label = i->getLabel();
Chris@125 1285 } else if (i->haveValue()) {
Chris@125 1286 newPoint.label = QString("%1").arg(i->getValue());
Chris@125 1287 }
Chris@125 1288
Chris@125 1289 if (i->haveValue()) {
Chris@125 1290 newPoint.value = i->getValue();
Chris@125 1291 } else {
Chris@125 1292
Chris@125 1293 switch (generation) {
Chris@125 1294
Chris@125 1295 case GenerateNone:
Chris@125 1296 newPoint.value = 0;
Chris@125 1297 break;
Chris@125 1298
Chris@125 1299 case GenerateFromCounter:
Chris@125 1300 newPoint.value = counter;
Chris@125 1301 break;
Chris@125 1302
Chris@125 1303 case GenerateFromFrameNumber:
Chris@125 1304 newPoint.value = frame;
Chris@125 1305 break;
Chris@125 1306
Chris@125 1307 case GenerateFromRealTime:
Chris@125 1308 newPoint.value = float(frame) / float(m_model->getSampleRate());
Chris@125 1309 break;
Chris@125 1310
Chris@125 1311 case GenerateFromRealTimeDifference:
Chris@125 1312 case GenerateFromTempo:
Chris@125 1313 {
Chris@125 1314 size_t nextFrame = frame;
Chris@125 1315 Clipboard::PointList::const_iterator j = i;
Chris@125 1316 for (; j != points.end(); ++j) {
Chris@125 1317 if (!j->haveFrame()) continue;
Chris@125 1318 if (j != i) break;
Chris@125 1319 }
Chris@125 1320 if (j != points.end()) {
Chris@125 1321 nextFrame = j->getFrame();
Chris@125 1322 }
Chris@125 1323 if (generation == GenerateFromRealTimeDifference) {
Chris@125 1324 newPoint.value = float(nextFrame - frame) /
Chris@125 1325 float(m_model->getSampleRate());
Chris@125 1326 } else {
Chris@125 1327 float bpm = prevBpm;
Chris@125 1328 if (nextFrame > frame) {
Chris@125 1329 bpm = (60.f * m_model->getSampleRate()) /
Chris@125 1330 (nextFrame - frame);
Chris@125 1331 }
Chris@125 1332 newPoint.value = bpm;
Chris@125 1333 prevBpm = bpm;
Chris@125 1334 }
Chris@125 1335 break;
Chris@125 1336 }
Chris@125 1337
Chris@125 1338 case GenerateFromExistingNeighbour:
Chris@125 1339 {
Chris@125 1340 SparseTimeValueModel::PointList points =
Chris@125 1341 m_model->getPoints(frame);
Chris@125 1342 if (points.empty()) points = m_model->getPreviousPoints(frame);
Chris@125 1343 if (points.empty()) points = m_model->getNextPoints(frame);
Chris@125 1344 if (points.empty()) {
Chris@125 1345 newPoint.value = 0.f;
Chris@125 1346 } else {
Chris@125 1347 newPoint.value = points.begin()->value;
Chris@125 1348 }
Chris@125 1349 }
Chris@125 1350
Chris@125 1351 case GenerateFromLabels:
Chris@125 1352 if (i->haveLabel()) {
Chris@125 1353 // more forgiving than QString::toFloat()
Chris@125 1354 newPoint.value = atof(i->getLabel().toLocal8Bit());
Chris@125 1355 } else {
Chris@125 1356 newPoint.value = 0.f;
Chris@125 1357 }
Chris@125 1358 }
Chris@125 1359 }
Chris@76 1360
Chris@76 1361 command->addPoint(newPoint);
Chris@125 1362
Chris@125 1363 ++counter;
Chris@76 1364 }
Chris@76 1365
Chris@76 1366 command->finish();
Chris@125 1367 return true;
Chris@76 1368 }
Chris@76 1369
Chris@6 1370 QString
Chris@6 1371 TimeValueLayer::toXmlString(QString indent, QString extraAttributes) const
Chris@6 1372 {
Chris@6 1373 return Layer::toXmlString(indent, extraAttributes +
Chris@6 1374 QString(" colour=\"%1\" plotStyle=\"%2\"")
Chris@6 1375 .arg(encodeColour(m_colour)).arg(m_plotStyle));
Chris@0 1376 }
Chris@0 1377
Chris@11 1378 void
Chris@11 1379 TimeValueLayer::setProperties(const QXmlAttributes &attributes)
Chris@11 1380 {
Chris@11 1381 QString colourSpec = attributes.value("colour");
Chris@11 1382 if (colourSpec != "") {
Chris@11 1383 QColor colour(colourSpec);
Chris@11 1384 if (colour.isValid()) {
Chris@11 1385 setBaseColour(QColor(colourSpec));
Chris@11 1386 }
Chris@11 1387 }
Chris@11 1388
Chris@11 1389 bool ok;
Chris@11 1390 PlotStyle style = (PlotStyle)
Chris@11 1391 attributes.value("plotStyle").toInt(&ok);
Chris@11 1392 if (ok) setPlotStyle(style);
Chris@11 1393 }
Chris@11 1394
Chris@0 1395
Chris@0 1396 #ifdef INCLUDE_MOCFILES
Chris@0 1397 #include "TimeValueLayer.moc.cpp"
Chris@0 1398 #endif
Chris@0 1399