annotate layer/TimeInstantLayer.cpp @ 1245:f0e291fa7b9c

Use Range01 normalisation in Colour 3D Plot. This gives us the same column normalisation behaviour as in 2.5 (better than the Max1 option).
author Chris Cannam
date Tue, 28 Feb 2017 14:06:24 +0000
parents ee01a4062747
children a34a2a25907c
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 "TimeInstantLayer.h"
Chris@0 17
Chris@128 18 #include "data/model/Model.h"
Chris@0 19 #include "base/RealTime.h"
Chris@128 20 #include "view/View.h"
Chris@0 21 #include "base/Profiler.h"
Chris@76 22 #include "base/Clipboard.h"
Chris@1078 23
Chris@376 24 #include "ColourDatabase.h"
Chris@1078 25 #include "PaintAssistant.h"
Chris@0 26
Chris@128 27 #include "data/model/SparseOneDimensionalModel.h"
Chris@0 28
Chris@70 29 #include "widgets/ItemEditDialog.h"
Chris@358 30 #include "widgets/ListInputDialog.h"
Chris@70 31
Chris@0 32 #include <QPainter>
Chris@17 33 #include <QMouseEvent>
Chris@316 34 #include <QTextStream>
Chris@360 35 #include <QMessageBox>
Chris@0 36
Chris@0 37 #include <iostream>
Martin@46 38 #include <cmath>
Chris@0 39
Chris@429 40 //#define DEBUG_TIME_INSTANT_LAYER 1
Chris@387 41
Chris@44 42 TimeInstantLayer::TimeInstantLayer() :
Chris@287 43 SingleColourLayer(),
Chris@0 44 m_model(0),
Chris@18 45 m_editing(false),
Chris@17 46 m_editingPoint(0, tr("New Point")),
Chris@22 47 m_editingCommand(0),
Chris@28 48 m_plotStyle(PlotInstants)
Chris@0 49 {
Chris@308 50 }
Chris@308 51
Chris@308 52 TimeInstantLayer::~TimeInstantLayer()
Chris@308 53 {
Chris@0 54 }
Chris@0 55
Chris@0 56 void
Chris@0 57 TimeInstantLayer::setModel(SparseOneDimensionalModel *model)
Chris@0 58 {
Chris@0 59 if (m_model == model) return;
Chris@0 60 m_model = model;
Chris@0 61
Chris@320 62 connectSignals(m_model);
Chris@0 63
Chris@387 64 #ifdef DEBUG_TIME_INSTANT_LAYER
Chris@682 65 cerr << "TimeInstantLayer::setModel(" << model << ")" << endl;
Chris@387 66 #endif
Chris@0 67
Chris@494 68 if (m_model && m_model->getRDFTypeURI().endsWith("Segment")) {
Chris@494 69 setPlotStyle(PlotSegmentation);
Chris@494 70 }
Chris@494 71
Chris@0 72 emit modelReplaced();
Chris@0 73 }
Chris@0 74
Chris@0 75 Layer::PropertyList
Chris@0 76 TimeInstantLayer::getProperties() const
Chris@0 77 {
Chris@287 78 PropertyList list = SingleColourLayer::getProperties();
Chris@87 79 list.push_back("Plot Type");
Chris@0 80 return list;
Chris@0 81 }
Chris@0 82
Chris@87 83 QString
Chris@87 84 TimeInstantLayer::getPropertyLabel(const PropertyName &name) const
Chris@87 85 {
Chris@87 86 if (name == "Plot Type") return tr("Plot Type");
Chris@287 87 return SingleColourLayer::getPropertyLabel(name);
Chris@87 88 }
Chris@87 89
Chris@0 90 Layer::PropertyType
Chris@287 91 TimeInstantLayer::getPropertyType(const PropertyName &name) const
Chris@0 92 {
Chris@287 93 if (name == "Plot Type") return ValueProperty;
Chris@287 94 return SingleColourLayer::getPropertyType(name);
Chris@0 95 }
Chris@0 96
Chris@0 97 int
Chris@0 98 TimeInstantLayer::getPropertyRangeAndValue(const PropertyName &name,
Chris@216 99 int *min, int *max, int *deflt) const
Chris@0 100 {
Chris@216 101 int val = 0;
Chris@0 102
Chris@287 103 if (name == "Plot Type") {
Chris@28 104
Chris@28 105 if (min) *min = 0;
Chris@28 106 if (max) *max = 1;
Chris@216 107 if (deflt) *deflt = 0;
Chris@28 108
Chris@216 109 val = int(m_plotStyle);
Chris@28 110
Chris@0 111 } else {
Chris@0 112
Chris@287 113 val = SingleColourLayer::getPropertyRangeAndValue(name, min, max, deflt);
Chris@0 114 }
Chris@0 115
Chris@216 116 return val;
Chris@0 117 }
Chris@0 118
Chris@0 119 QString
Chris@0 120 TimeInstantLayer::getPropertyValueLabel(const PropertyName &name,
Chris@287 121 int value) const
Chris@0 122 {
Chris@287 123 if (name == "Plot Type") {
Chris@28 124 switch (value) {
Chris@28 125 default:
Chris@28 126 case 0: return tr("Instants");
Chris@28 127 case 1: return tr("Segmentation");
Chris@28 128 }
Chris@0 129 }
Chris@287 130 return SingleColourLayer::getPropertyValueLabel(name, value);
Chris@0 131 }
Chris@0 132
Chris@0 133 void
Chris@0 134 TimeInstantLayer::setProperty(const PropertyName &name, int value)
Chris@0 135 {
Chris@287 136 if (name == "Plot Type") {
Chris@28 137 setPlotStyle(PlotStyle(value));
Chris@287 138 } else {
Chris@287 139 SingleColourLayer::setProperty(name, value);
Chris@0 140 }
Chris@0 141 }
Chris@0 142
Chris@0 143 void
Chris@28 144 TimeInstantLayer::setPlotStyle(PlotStyle style)
Chris@28 145 {
Chris@28 146 if (m_plotStyle == style) return;
Chris@28 147 m_plotStyle = style;
Chris@28 148 emit layerParametersChanged();
Chris@28 149 }
Chris@28 150
Chris@0 151 bool
Chris@918 152 TimeInstantLayer::isLayerScrollable(const LayerGeometryProvider *v) const
Chris@0 153 {
Chris@0 154 QPoint discard;
Chris@44 155 return !v->shouldIlluminateLocalFeatures(this, discard);
Chris@0 156 }
Chris@0 157
Chris@0 158 SparseOneDimensionalModel::PointList
Chris@918 159 TimeInstantLayer::getLocalPoints(LayerGeometryProvider *v, int x) const
Chris@0 160 {
Chris@28 161 // Return a set of points that all have the same frame number, the
Chris@28 162 // nearest to the given x coordinate, and that are within a
Chris@28 163 // certain fuzz distance of that x coordinate.
Chris@28 164
Chris@0 165 if (!m_model) return SparseOneDimensionalModel::PointList();
Chris@0 166
Chris@989 167 sv_frame_t frame = v->getFrameForX(x);
Chris@0 168
Chris@0 169 SparseOneDimensionalModel::PointList onPoints =
Chris@0 170 m_model->getPoints(frame);
Chris@0 171
Chris@0 172 if (!onPoints.empty()) {
Chris@0 173 return onPoints;
Chris@0 174 }
Chris@0 175
Chris@0 176 SparseOneDimensionalModel::PointList prevPoints =
Chris@0 177 m_model->getPreviousPoints(frame);
Chris@0 178 SparseOneDimensionalModel::PointList nextPoints =
Chris@0 179 m_model->getNextPoints(frame);
Chris@0 180
Chris@0 181 SparseOneDimensionalModel::PointList usePoints = prevPoints;
Chris@0 182
Chris@0 183 if (prevPoints.empty()) {
Chris@0 184 usePoints = nextPoints;
Chris@248 185 } else if (long(prevPoints.begin()->frame) < v->getStartFrame() &&
Chris@44 186 !(nextPoints.begin()->frame > v->getEndFrame())) {
Chris@0 187 usePoints = nextPoints;
Chris@0 188 } else if (nextPoints.begin()->frame - frame <
Chris@0 189 frame - prevPoints.begin()->frame) {
Chris@0 190 usePoints = nextPoints;
Chris@0 191 }
Chris@0 192
Chris@28 193 if (!usePoints.empty()) {
Chris@28 194 int fuzz = 2;
Chris@44 195 int px = v->getXForFrame(usePoints.begin()->frame);
Chris@28 196 if ((px > x && px - x > fuzz) ||
Chris@28 197 (px < x && x - px > fuzz + 1)) {
Chris@28 198 usePoints.clear();
Chris@28 199 }
Chris@28 200 }
Chris@28 201
Chris@0 202 return usePoints;
Chris@0 203 }
Chris@0 204
Chris@25 205 QString
Chris@909 206 TimeInstantLayer::getLabelPreceding(sv_frame_t frame) const
Chris@552 207 {
Chris@552 208 if (!m_model) return "";
Chris@552 209 SparseOneDimensionalModel::PointList points = m_model->getPreviousPoints(frame);
Chris@552 210 for (SparseOneDimensionalModel::PointList::const_iterator i = points.begin();
Chris@552 211 i != points.end(); ++i) {
Chris@552 212 if (i->label != "") return i->label;
Chris@552 213 }
Chris@552 214 return "";
Chris@552 215 }
Chris@552 216
Chris@552 217 QString
Chris@918 218 TimeInstantLayer::getFeatureDescription(LayerGeometryProvider *v, QPoint &pos) const
Chris@0 219 {
Chris@25 220 int x = pos.x();
Chris@0 221
Chris@25 222 if (!m_model || !m_model->getSampleRate()) return "";
Chris@0 223
Chris@44 224 SparseOneDimensionalModel::PointList points = getLocalPoints(v, x);
Chris@0 225
Chris@0 226 if (points.empty()) {
Chris@0 227 if (!m_model->isReady()) {
Chris@25 228 return tr("In progress");
Chris@25 229 } else {
Chris@25 230 return tr("No local points");
Chris@0 231 }
Chris@0 232 }
Chris@0 233
Chris@989 234 sv_frame_t useFrame = points.begin()->frame;
Chris@0 235
Chris@0 236 RealTime rt = RealTime::frame2RealTime(useFrame, m_model->getSampleRate());
Chris@25 237
Chris@25 238 QString text;
Chris@0 239
Chris@25 240 if (points.begin()->label == "") {
Chris@25 241 text = QString(tr("Time:\t%1\nNo label"))
Chris@25 242 .arg(rt.toText(true).c_str());
Chris@25 243 } else {
Chris@25 244 text = QString(tr("Time:\t%1\nLabel:\t%2"))
Chris@25 245 .arg(rt.toText(true).c_str())
Chris@25 246 .arg(points.begin()->label);
Chris@25 247 }
Chris@0 248
Chris@44 249 pos = QPoint(v->getXForFrame(useFrame), pos.y());
Chris@25 250 return text;
Chris@0 251 }
Chris@0 252
Chris@28 253 bool
Chris@918 254 TimeInstantLayer::snapToFeatureFrame(LayerGeometryProvider *v, sv_frame_t &frame,
Chris@805 255 int &resolution,
Chris@28 256 SnapType snap) const
Chris@13 257 {
Chris@13 258 if (!m_model) {
Chris@44 259 return Layer::snapToFeatureFrame(v, frame, resolution, snap);
Chris@13 260 }
Chris@13 261
Chris@13 262 resolution = m_model->getResolution();
Chris@28 263 SparseOneDimensionalModel::PointList points;
Chris@13 264
Chris@28 265 if (snap == SnapNeighbouring) {
Chris@28 266
Chris@44 267 points = getLocalPoints(v, v->getXForFrame(frame));
Chris@28 268 if (points.empty()) return false;
Chris@28 269 frame = points.begin()->frame;
Chris@28 270 return true;
Chris@28 271 }
Chris@28 272
Chris@28 273 points = m_model->getPoints(frame, frame);
Chris@908 274 sv_frame_t snapped = frame;
Chris@28 275 bool found = false;
Chris@13 276
Chris@13 277 for (SparseOneDimensionalModel::PointList::const_iterator i = points.begin();
Chris@13 278 i != points.end(); ++i) {
Chris@13 279
Chris@28 280 if (snap == SnapRight) {
Chris@28 281
Chris@28 282 if (i->frame >= frame) {
Chris@28 283 snapped = i->frame;
Chris@28 284 found = true;
Chris@13 285 break;
Chris@13 286 }
Chris@28 287
Chris@28 288 } else if (snap == SnapLeft) {
Chris@28 289
Chris@13 290 if (i->frame <= frame) {
Chris@28 291 snapped = i->frame;
Chris@28 292 found = true; // don't break, as the next may be better
Chris@28 293 } else {
Chris@28 294 break;
Chris@28 295 }
Chris@28 296
Chris@28 297 } else { // nearest
Chris@28 298
Chris@28 299 SparseOneDimensionalModel::PointList::const_iterator j = i;
Chris@28 300 ++j;
Chris@28 301
Chris@28 302 if (j == points.end()) {
Chris@28 303
Chris@28 304 snapped = i->frame;
Chris@28 305 found = true;
Chris@28 306 break;
Chris@28 307
Chris@28 308 } else if (j->frame >= frame) {
Chris@28 309
Chris@28 310 if (j->frame - frame < frame - i->frame) {
Chris@28 311 snapped = j->frame;
Chris@28 312 } else {
Chris@28 313 snapped = i->frame;
Chris@28 314 }
Chris@28 315 found = true;
Chris@28 316 break;
Chris@13 317 }
Chris@13 318 }
Chris@13 319 }
Chris@13 320
Chris@28 321 frame = snapped;
Chris@28 322 return found;
Chris@13 323 }
Chris@13 324
Chris@0 325 void
Chris@916 326 TimeInstantLayer::paint(LayerGeometryProvider *v, QPainter &paint, QRect rect) const
Chris@0 327 {
Chris@0 328 if (!m_model || !m_model->isOK()) return;
Chris@0 329
Chris@0 330 // Profiler profiler("TimeInstantLayer::paint", true);
Chris@0 331
Chris@20 332 int x0 = rect.left(), x1 = rect.right();
Chris@0 333
Chris@989 334 sv_frame_t frame0 = v->getFrameForX(x0);
Chris@989 335 sv_frame_t frame1 = v->getFrameForX(x1);
Chris@0 336
Chris@0 337 SparseOneDimensionalModel::PointList points(m_model->getPoints
Chris@0 338 (frame0, frame1));
Chris@0 339
Chris@28 340 bool odd = false;
Chris@28 341 if (m_plotStyle == PlotSegmentation && !points.empty()) {
Chris@28 342 int index = m_model->getIndexOf(*points.begin());
Chris@28 343 odd = ((index % 2) == 1);
Chris@28 344 }
Chris@28 345
Chris@287 346 paint.setPen(getBaseQColor());
Chris@0 347
Chris@287 348 QColor brushColour(getBaseQColor());
Chris@0 349 brushColour.setAlpha(100);
Chris@0 350 paint.setBrush(brushColour);
Chris@0 351
Chris@28 352 QColor oddBrushColour(brushColour);
Chris@28 353 if (m_plotStyle == PlotSegmentation) {
Chris@287 354 if (getBaseQColor() == Qt::black) {
Chris@28 355 oddBrushColour = Qt::gray;
Chris@287 356 } else if (getBaseQColor() == Qt::darkRed) {
Chris@28 357 oddBrushColour = Qt::red;
Chris@287 358 } else if (getBaseQColor() == Qt::darkBlue) {
Chris@28 359 oddBrushColour = Qt::blue;
Chris@287 360 } else if (getBaseQColor() == Qt::darkGreen) {
Chris@28 361 oddBrushColour = Qt::green;
Chris@28 362 } else {
Chris@28 363 oddBrushColour = oddBrushColour.light(150);
Chris@28 364 }
Chris@28 365 oddBrushColour.setAlpha(100);
Chris@28 366 }
Chris@28 367
Chris@587 368 // SVDEBUG << "TimeInstantLayer::paint: resolution is "
Chris@585 369 // << m_model->getResolution() << " frames" << endl;
Chris@0 370
Chris@0 371 QPoint localPos;
Chris@989 372 sv_frame_t illuminateFrame = -1;
Chris@0 373
Chris@44 374 if (v->shouldIlluminateLocalFeatures(this, localPos)) {
Chris@0 375 SparseOneDimensionalModel::PointList localPoints =
Chris@44 376 getLocalPoints(v, localPos.x());
Chris@0 377 if (!localPoints.empty()) illuminateFrame = localPoints.begin()->frame;
Chris@0 378 }
Chris@0 379
Chris@23 380 int prevX = -1;
Chris@79 381 int textY = v->getTextLabelHeight(this, paint);
Chris@79 382
Chris@0 383 for (SparseOneDimensionalModel::PointList::const_iterator i = points.begin();
Chris@0 384 i != points.end(); ++i) {
Chris@0 385
Chris@0 386 const SparseOneDimensionalModel::Point &p(*i);
Chris@17 387 SparseOneDimensionalModel::PointList::const_iterator j = i;
Chris@17 388 ++j;
Chris@0 389
Chris@44 390 int x = v->getXForFrame(p.frame);
Chris@576 391 if (x == prevX && m_plotStyle == PlotInstants &&
Chris@576 392 p.frame != illuminateFrame) continue;
Chris@23 393
Chris@44 394 int iw = v->getXForFrame(p.frame + m_model->getResolution()) - x;
Chris@16 395 if (iw < 2) {
Chris@17 396 if (iw < 1) {
Chris@17 397 iw = 2;
Chris@17 398 if (j != points.end()) {
Chris@44 399 int nx = v->getXForFrame(j->frame);
Chris@17 400 if (nx < x + 3) iw = 1;
Chris@17 401 }
Chris@17 402 } else {
Chris@17 403 iw = 2;
Chris@17 404 }
Chris@16 405 }
Chris@20 406
Chris@0 407 if (p.frame == illuminateFrame) {
Chris@918 408 paint.setPen(getForegroundQColor(v->getView()));
Chris@0 409 } else {
Chris@0 410 paint.setPen(brushColour);
Chris@0 411 }
Chris@23 412
Chris@28 413 if (m_plotStyle == PlotInstants) {
Chris@28 414 if (iw > 1) {
Chris@918 415 paint.drawRect(x, 0, iw - 1, v->getPaintHeight() - 1);
Chris@28 416 } else {
Chris@918 417 paint.drawLine(x, 0, x, v->getPaintHeight() - 1);
Chris@28 418 }
Chris@23 419 } else {
Chris@28 420
Chris@28 421 if (odd) paint.setBrush(oddBrushColour);
Chris@28 422 else paint.setBrush(brushColour);
Chris@28 423
Chris@28 424 int nx;
Chris@28 425
Chris@28 426 if (j != points.end()) {
Chris@28 427 const SparseOneDimensionalModel::Point &q(*j);
Chris@44 428 nx = v->getXForFrame(q.frame);
Chris@28 429 } else {
Chris@44 430 nx = v->getXForFrame(m_model->getEndFrame());
Chris@28 431 }
Chris@28 432
Chris@28 433 if (nx >= x) {
Chris@28 434
Chris@28 435 if (illuminateFrame != p.frame &&
Chris@918 436 (nx < x + 5 || x >= v->getPaintWidth() - 1)) {
Chris@28 437 paint.setPen(Qt::NoPen);
Chris@28 438 }
Chris@28 439
Chris@918 440 paint.drawRect(x, -1, nx - x, v->getPaintHeight() + 1);
Chris@28 441 }
Chris@28 442
Chris@28 443 odd = !odd;
Chris@23 444 }
Chris@28 445
Chris@287 446 paint.setPen(getBaseQColor());
Chris@0 447
Chris@0 448 if (p.label != "") {
Chris@0 449
Chris@0 450 // only draw if there's enough room from here to the next point
Chris@0 451
Chris@0 452 int lw = paint.fontMetrics().width(p.label);
Chris@0 453 bool good = true;
Chris@0 454
Chris@17 455 if (j != points.end()) {
Chris@44 456 int nx = v->getXForFrame(j->frame);
Chris@20 457 if (nx >= x && nx - x - iw - 3 <= lw) good = false;
Chris@0 458 }
Chris@0 459
Chris@0 460 if (good) {
Chris@1078 461 PaintAssistant::drawVisibleText(v, paint, x + iw + 2, textY, p.label, PaintAssistant::OutlinedText);
Chris@576 462 // paint.drawText(x + iw + 2, textY, p.label);
Chris@0 463 }
Chris@0 464 }
Chris@23 465
Chris@23 466 prevX = x;
Chris@0 467 }
Chris@0 468 }
Chris@0 469
Chris@17 470 void
Chris@918 471 TimeInstantLayer::drawStart(LayerGeometryProvider *v, QMouseEvent *e)
Chris@17 472 {
Chris@387 473 #ifdef DEBUG_TIME_INSTANT_LAYER
Chris@682 474 cerr << "TimeInstantLayer::drawStart(" << e->x() << ")" << endl;
Chris@387 475 #endif
Chris@17 476
Chris@17 477 if (!m_model) return;
Chris@17 478
Chris@989 479 sv_frame_t frame = v->getFrameForX(e->x());
Chris@17 480 if (frame < 0) frame = 0;
Chris@21 481 frame = frame / m_model->getResolution() * m_model->getResolution();
Chris@22 482
Chris@17 483 m_editingPoint = SparseOneDimensionalModel::Point(frame, tr("New Point"));
Chris@22 484
Chris@376 485 if (m_editingCommand) finish(m_editingCommand);
Chris@22 486 m_editingCommand = new SparseOneDimensionalModel::EditCommand(m_model,
Chris@22 487 tr("Draw Point"));
Chris@22 488 m_editingCommand->addPoint(m_editingPoint);
Chris@22 489
Chris@18 490 m_editing = true;
Chris@17 491 }
Chris@17 492
Chris@17 493 void
Chris@918 494 TimeInstantLayer::drawDrag(LayerGeometryProvider *v, QMouseEvent *e)
Chris@17 495 {
Chris@387 496 #ifdef DEBUG_TIME_INSTANT_LAYER
Chris@682 497 cerr << "TimeInstantLayer::drawDrag(" << e->x() << ")" << endl;
Chris@387 498 #endif
Chris@17 499
Chris@18 500 if (!m_model || !m_editing) return;
Chris@17 501
Chris@989 502 sv_frame_t frame = v->getFrameForX(e->x());
Chris@17 503 if (frame < 0) frame = 0;
Chris@21 504 frame = frame / m_model->getResolution() * m_model->getResolution();
Chris@22 505 m_editingCommand->deletePoint(m_editingPoint);
Chris@17 506 m_editingPoint.frame = frame;
Chris@22 507 m_editingCommand->addPoint(m_editingPoint);
Chris@17 508 }
Chris@17 509
Chris@17 510 void
Chris@918 511 TimeInstantLayer::drawEnd(LayerGeometryProvider *, QMouseEvent *)
Chris@17 512 {
Chris@387 513 #ifdef DEBUG_TIME_INSTANT_LAYER
Chris@682 514 cerr << "TimeInstantLayer::drawEnd(" << e->x() << ")" << endl;
Chris@387 515 #endif
Chris@18 516 if (!m_model || !m_editing) return;
Chris@23 517 QString newName = tr("Add Point at %1 s")
Chris@23 518 .arg(RealTime::frame2RealTime(m_editingPoint.frame,
Chris@23 519 m_model->getSampleRate())
Chris@23 520 .toText(false).c_str());
Chris@23 521 m_editingCommand->setName(newName);
Chris@376 522 finish(m_editingCommand);
Chris@22 523 m_editingCommand = 0;
Chris@18 524 m_editing = false;
Chris@18 525 }
Chris@18 526
Chris@18 527 void
Chris@918 528 TimeInstantLayer::eraseStart(LayerGeometryProvider *v, QMouseEvent *e)
Chris@335 529 {
Chris@335 530 if (!m_model) return;
Chris@335 531
Chris@335 532 SparseOneDimensionalModel::PointList points = getLocalPoints(v, e->x());
Chris@335 533 if (points.empty()) return;
Chris@335 534
Chris@335 535 m_editingPoint = *points.begin();
Chris@335 536
Chris@335 537 if (m_editingCommand) {
Chris@376 538 finish(m_editingCommand);
Chris@335 539 m_editingCommand = 0;
Chris@335 540 }
Chris@335 541
Chris@335 542 m_editing = true;
Chris@335 543 }
Chris@335 544
Chris@335 545 void
Chris@918 546 TimeInstantLayer::eraseDrag(LayerGeometryProvider *, QMouseEvent *)
Chris@335 547 {
Chris@335 548 }
Chris@335 549
Chris@335 550 void
Chris@918 551 TimeInstantLayer::eraseEnd(LayerGeometryProvider *v, QMouseEvent *e)
Chris@335 552 {
Chris@335 553 if (!m_model || !m_editing) return;
Chris@335 554
Chris@335 555 m_editing = false;
Chris@335 556
Chris@335 557 SparseOneDimensionalModel::PointList points = getLocalPoints(v, e->x());
Chris@335 558 if (points.empty()) return;
Chris@335 559 if (points.begin()->frame != m_editingPoint.frame) return;
Chris@335 560
Chris@335 561 m_editingCommand = new SparseOneDimensionalModel::EditCommand
Chris@335 562 (m_model, tr("Erase Point"));
Chris@335 563
Chris@335 564 m_editingCommand->deletePoint(m_editingPoint);
Chris@335 565
Chris@376 566 finish(m_editingCommand);
Chris@335 567 m_editingCommand = 0;
Chris@335 568 m_editing = false;
Chris@335 569 }
Chris@335 570
Chris@335 571 void
Chris@918 572 TimeInstantLayer::editStart(LayerGeometryProvider *v, QMouseEvent *e)
Chris@18 573 {
Chris@387 574 #ifdef DEBUG_TIME_INSTANT_LAYER
Chris@682 575 cerr << "TimeInstantLayer::editStart(" << e->x() << ")" << endl;
Chris@387 576 #endif
Chris@18 577
Chris@17 578 if (!m_model) return;
Chris@18 579
Chris@44 580 SparseOneDimensionalModel::PointList points = getLocalPoints(v, e->x());
Chris@18 581 if (points.empty()) return;
Chris@18 582
Chris@18 583 m_editingPoint = *points.begin();
Chris@22 584
Chris@22 585 if (m_editingCommand) {
Chris@376 586 finish(m_editingCommand);
Chris@22 587 m_editingCommand = 0;
Chris@22 588 }
Chris@22 589
Chris@18 590 m_editing = true;
Chris@18 591 }
Chris@18 592
Chris@18 593 void
Chris@918 594 TimeInstantLayer::editDrag(LayerGeometryProvider *v, QMouseEvent *e)
Chris@18 595 {
Chris@387 596 #ifdef DEBUG_TIME_INSTANT_LAYER
Chris@682 597 cerr << "TimeInstantLayer::editDrag(" << e->x() << ")" << endl;
Chris@387 598 #endif
Chris@18 599
Chris@18 600 if (!m_model || !m_editing) return;
Chris@18 601
Chris@989 602 sv_frame_t frame = v->getFrameForX(e->x());
Chris@18 603 if (frame < 0) frame = 0;
Chris@21 604 frame = frame / m_model->getResolution() * m_model->getResolution();
Chris@22 605
Chris@22 606 if (!m_editingCommand) {
Chris@22 607 m_editingCommand = new SparseOneDimensionalModel::EditCommand(m_model,
Chris@22 608 tr("Drag Point"));
Chris@22 609 }
Chris@22 610
Chris@22 611 m_editingCommand->deletePoint(m_editingPoint);
Chris@18 612 m_editingPoint.frame = frame;
Chris@22 613 m_editingCommand->addPoint(m_editingPoint);
Chris@18 614 }
Chris@18 615
Chris@18 616 void
Chris@918 617 TimeInstantLayer::editEnd(LayerGeometryProvider *, QMouseEvent *)
Chris@18 618 {
Chris@387 619 #ifdef DEBUG_TIME_INSTANT_LAYER
Chris@682 620 cerr << "TimeInstantLayer::editEnd(" << e->x() << ")" << endl;
Chris@387 621 #endif
Chris@18 622 if (!m_model || !m_editing) return;
Chris@23 623 if (m_editingCommand) {
Chris@23 624 QString newName = tr("Move Point to %1 s")
Chris@23 625 .arg(RealTime::frame2RealTime(m_editingPoint.frame,
Chris@23 626 m_model->getSampleRate())
Chris@23 627 .toText(false).c_str());
Chris@23 628 m_editingCommand->setName(newName);
Chris@376 629 finish(m_editingCommand);
Chris@23 630 }
Chris@22 631 m_editingCommand = 0;
Chris@18 632 m_editing = false;
Chris@17 633 }
Chris@17 634
Chris@255 635 bool
Chris@918 636 TimeInstantLayer::editOpen(LayerGeometryProvider *v, QMouseEvent *e)
Chris@70 637 {
Chris@255 638 if (!m_model) return false;
Chris@70 639
Chris@70 640 SparseOneDimensionalModel::PointList points = getLocalPoints(v, e->x());
Chris@255 641 if (points.empty()) return false;
Chris@70 642
Chris@70 643 SparseOneDimensionalModel::Point point = *points.begin();
Chris@70 644
Chris@70 645 ItemEditDialog *dialog = new ItemEditDialog
Chris@70 646 (m_model->getSampleRate(),
Chris@70 647 ItemEditDialog::ShowTime |
Chris@70 648 ItemEditDialog::ShowText);
Chris@70 649
Chris@70 650 dialog->setFrameTime(point.frame);
Chris@70 651 dialog->setText(point.label);
Chris@70 652
Chris@70 653 if (dialog->exec() == QDialog::Accepted) {
Chris@70 654
Chris@70 655 SparseOneDimensionalModel::Point newPoint = point;
Chris@70 656 newPoint.frame = dialog->getFrameTime();
Chris@70 657 newPoint.label = dialog->getText();
Chris@70 658
Chris@70 659 SparseOneDimensionalModel::EditCommand *command =
Chris@70 660 new SparseOneDimensionalModel::EditCommand(m_model, tr("Edit Point"));
Chris@70 661 command->deletePoint(point);
Chris@70 662 command->addPoint(newPoint);
Chris@376 663 finish(command);
Chris@70 664 }
Chris@70 665
Chris@70 666 delete dialog;
Chris@255 667 return true;
Chris@70 668 }
Chris@70 669
Chris@70 670 void
Chris@908 671 TimeInstantLayer::moveSelection(Selection s, sv_frame_t newStartFrame)
Chris@43 672 {
Chris@99 673 if (!m_model) return;
Chris@99 674
Chris@43 675 SparseOneDimensionalModel::EditCommand *command =
Chris@43 676 new SparseOneDimensionalModel::EditCommand(m_model,
Chris@43 677 tr("Drag Selection"));
Chris@43 678
Chris@43 679 SparseOneDimensionalModel::PointList points =
Chris@43 680 m_model->getPoints(s.getStartFrame(), s.getEndFrame());
Chris@43 681
Chris@43 682 for (SparseOneDimensionalModel::PointList::iterator i = points.begin();
Chris@43 683 i != points.end(); ++i) {
Chris@43 684
Chris@43 685 if (s.contains(i->frame)) {
Chris@43 686 SparseOneDimensionalModel::Point newPoint(*i);
Chris@43 687 newPoint.frame = i->frame + newStartFrame - s.getStartFrame();
Chris@43 688 command->deletePoint(*i);
Chris@43 689 command->addPoint(newPoint);
Chris@43 690 }
Chris@43 691 }
Chris@43 692
Chris@376 693 finish(command);
Chris@43 694 }
Chris@43 695
Chris@43 696 void
Chris@43 697 TimeInstantLayer::resizeSelection(Selection s, Selection newSize)
Chris@43 698 {
Chris@99 699 if (!m_model) return;
Chris@99 700
Chris@43 701 SparseOneDimensionalModel::EditCommand *command =
Chris@43 702 new SparseOneDimensionalModel::EditCommand(m_model,
Chris@43 703 tr("Resize Selection"));
Chris@43 704
Chris@43 705 SparseOneDimensionalModel::PointList points =
Chris@43 706 m_model->getPoints(s.getStartFrame(), s.getEndFrame());
Chris@43 707
Chris@43 708 double ratio =
Chris@43 709 double(newSize.getEndFrame() - newSize.getStartFrame()) /
Chris@43 710 double(s.getEndFrame() - s.getStartFrame());
Chris@43 711
Chris@43 712 for (SparseOneDimensionalModel::PointList::iterator i = points.begin();
Chris@43 713 i != points.end(); ++i) {
Chris@43 714
Chris@43 715 if (s.contains(i->frame)) {
Chris@43 716
Chris@908 717 double target = double(i->frame);
Chris@908 718 target = double(newSize.getStartFrame()) +
Chris@908 719 target - double(s.getStartFrame()) * ratio;
Chris@43 720
Chris@43 721 SparseOneDimensionalModel::Point newPoint(*i);
Chris@43 722 newPoint.frame = lrint(target);
Chris@43 723 command->deletePoint(*i);
Chris@43 724 command->addPoint(newPoint);
Chris@43 725 }
Chris@43 726 }
Chris@43 727
Chris@376 728 finish(command);
Chris@43 729 }
Chris@43 730
Chris@43 731 void
Chris@43 732 TimeInstantLayer::deleteSelection(Selection s)
Chris@43 733 {
Chris@99 734 if (!m_model) return;
Chris@99 735
Chris@43 736 SparseOneDimensionalModel::EditCommand *command =
Chris@43 737 new SparseOneDimensionalModel::EditCommand(m_model,
Chris@43 738 tr("Delete Selection"));
Chris@43 739
Chris@43 740 SparseOneDimensionalModel::PointList points =
Chris@43 741 m_model->getPoints(s.getStartFrame(), s.getEndFrame());
Chris@43 742
Chris@43 743 for (SparseOneDimensionalModel::PointList::iterator i = points.begin();
Chris@43 744 i != points.end(); ++i) {
Chris@43 745 if (s.contains(i->frame)) command->deletePoint(*i);
Chris@43 746 }
Chris@43 747
Chris@376 748 finish(command);
Chris@43 749 }
Chris@76 750
Chris@76 751 void
Chris@918 752 TimeInstantLayer::copy(LayerGeometryProvider *v, Selection s, Clipboard &to)
Chris@76 753 {
Chris@99 754 if (!m_model) return;
Chris@99 755
Chris@76 756 SparseOneDimensionalModel::PointList points =
Chris@76 757 m_model->getPoints(s.getStartFrame(), s.getEndFrame());
Chris@76 758
Chris@76 759 for (SparseOneDimensionalModel::PointList::iterator i = points.begin();
Chris@76 760 i != points.end(); ++i) {
Chris@76 761 if (s.contains(i->frame)) {
Chris@76 762 Clipboard::Point point(i->frame, i->label);
Chris@359 763 point.setReferenceFrame(alignToReference(v, i->frame));
Chris@76 764 to.addPoint(point);
Chris@76 765 }
Chris@76 766 }
Chris@76 767 }
Chris@76 768
Chris@125 769 bool
Chris@918 770 TimeInstantLayer::paste(LayerGeometryProvider *v, const Clipboard &from, sv_frame_t frameOffset, bool)
Chris@76 771 {
Chris@125 772 if (!m_model) return false;
Chris@99 773
Chris@76 774 const Clipboard::PointList &points = from.getPoints();
Chris@76 775
Chris@358 776 bool realign = false;
Chris@358 777
Chris@360 778 if (clipboardHasDifferentAlignment(v, from)) {
Chris@358 779
Chris@360 780 QMessageBox::StandardButton button =
Chris@918 781 QMessageBox::question(v->getView(), tr("Re-align pasted instants?"),
Chris@360 782 tr("The instants you are pasting came from a layer with different source material from this one. Do you want to re-align them in time, to match the source material for this layer?"),
Chris@360 783 QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel,
Chris@360 784 QMessageBox::Yes);
Chris@358 785
Chris@360 786 if (button == QMessageBox::Cancel) {
Chris@360 787 return false;
Chris@360 788 }
Chris@358 789
Chris@360 790 if (button == QMessageBox::Yes) {
Chris@360 791 realign = true;
Chris@360 792 }
Chris@358 793 }
Chris@358 794
Chris@358 795 SparseOneDimensionalModel::EditCommand *command =
Chris@358 796 new SparseOneDimensionalModel::EditCommand(m_model, tr("Paste"));
Chris@358 797
Chris@76 798 for (Clipboard::PointList::const_iterator i = points.begin();
Chris@76 799 i != points.end(); ++i) {
Chris@76 800
Chris@76 801 if (!i->haveFrame()) continue;
Chris@359 802
Chris@908 803 sv_frame_t frame = 0;
Chris@359 804
Chris@359 805 if (!realign) {
Chris@359 806
Chris@359 807 frame = i->getFrame();
Chris@359 808
Chris@359 809 } else {
Chris@359 810
Chris@359 811 if (i->haveReferenceFrame()) {
Chris@359 812 frame = i->getReferenceFrame();
Chris@359 813 frame = alignFromReference(v, frame);
Chris@359 814 } else {
Chris@359 815 frame = i->getFrame();
Chris@359 816 }
Chris@76 817 }
Chris@359 818
Chris@359 819 if (frameOffset > 0) frame += frameOffset;
Chris@359 820 else if (frameOffset < 0) {
Chris@359 821 if (frame > -frameOffset) frame += frameOffset;
Chris@359 822 else frame = 0;
Chris@359 823 }
Chris@359 824
Chris@76 825 SparseOneDimensionalModel::Point newPoint(frame);
Chris@125 826 if (i->haveLabel()) {
Chris@125 827 newPoint.label = i->getLabel();
Chris@125 828 } else if (i->haveValue()) {
Chris@125 829 newPoint.label = QString("%1").arg(i->getValue());
Chris@125 830 }
Chris@76 831
Chris@76 832 command->addPoint(newPoint);
Chris@76 833 }
Chris@76 834
Chris@376 835 finish(command);
Chris@125 836 return true;
Chris@76 837 }
Chris@43 838
Chris@287 839 int
Chris@287 840 TimeInstantLayer::getDefaultColourHint(bool darkbg, bool &impose)
Chris@287 841 {
Chris@287 842 impose = false;
Chris@287 843 return ColourDatabase::getInstance()->getColourIndex
Chris@287 844 (QString(darkbg ? "Bright Purple" : "Purple"));
Chris@287 845 }
Chris@287 846
Chris@316 847 void
Chris@316 848 TimeInstantLayer::toXml(QTextStream &stream,
Chris@316 849 QString indent, QString extraAttributes) const
Chris@6 850 {
Chris@316 851 SingleColourLayer::toXml(stream, indent,
Chris@316 852 extraAttributes +
Chris@316 853 QString(" plotStyle=\"%1\"")
Chris@316 854 .arg(m_plotStyle));
Chris@6 855 }
Chris@0 856
Chris@11 857 void
Chris@11 858 TimeInstantLayer::setProperties(const QXmlAttributes &attributes)
Chris@11 859 {
Chris@287 860 SingleColourLayer::setProperties(attributes);
Chris@28 861
Chris@28 862 bool ok;
Chris@28 863 PlotStyle style = (PlotStyle)
Chris@28 864 attributes.value("plotStyle").toInt(&ok);
Chris@28 865 if (ok) setPlotStyle(style);
Chris@11 866 }
Chris@11 867