annotate layer/TimeValueLayer.cpp @ 312:6de6f78b13a1

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