annotate layer/TimeInstantLayer.cpp @ 1423:62e908518c71 single-point

Update to Event api in place of Clipboard points
author Chris Cannam
date Fri, 08 Mar 2019 13:37:30 +0000
parents c8a6fd3f9dff
children 9abddbd57667
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@1408 44 m_model(nullptr),
Chris@18 45 m_editing(false),
Chris@17 46 m_editingPoint(0, tr("New Point")),
Chris@1408 47 m_editingCommand(nullptr),
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@1266 104
Chris@1266 105 if (min) *min = 0;
Chris@1266 106 if (max) *max = 1;
Chris@216 107 if (deflt) *deflt = 0;
Chris@1266 108
Chris@1266 109 val = int(m_plotStyle);
Chris@28 110
Chris@0 111 } else {
Chris@1266 112
Chris@1266 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@1266 124 switch (value) {
Chris@1266 125 default:
Chris@1266 126 case 0: return tr("Instants");
Chris@1266 127 case 1: return tr("Segmentation");
Chris@1266 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@1266 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@1266 170 m_model->getPoints(frame);
Chris@0 171
Chris@0 172 if (!onPoints.empty()) {
Chris@1266 173 return onPoints;
Chris@0 174 }
Chris@0 175
Chris@0 176 SparseOneDimensionalModel::PointList prevPoints =
Chris@1266 177 m_model->getPreviousPoints(frame);
Chris@0 178 SparseOneDimensionalModel::PointList nextPoints =
Chris@1266 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@1266 184 usePoints = nextPoints;
Chris@248 185 } else if (long(prevPoints.begin()->frame) < v->getStartFrame() &&
Chris@1266 186 !(nextPoints.begin()->frame > v->getEndFrame())) {
Chris@1266 187 usePoints = nextPoints;
Chris@0 188 } else if (nextPoints.begin()->frame - frame <
Chris@1266 189 frame - prevPoints.begin()->frame) {
Chris@1266 190 usePoints = nextPoints;
Chris@0 191 }
Chris@0 192
Chris@28 193 if (!usePoints.empty()) {
Chris@1391 194 int fuzz = ViewManager::scalePixelSize(2);
Chris@1266 195 int px = v->getXForFrame(usePoints.begin()->frame);
Chris@1266 196 if ((px > x && px - x > fuzz) ||
Chris@1266 197 (px < x && x - px > fuzz + 1)) {
Chris@1266 198 usePoints.clear();
Chris@1266 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@1266 227 if (!m_model->isReady()) {
Chris@1266 228 return tr("In progress");
Chris@1266 229 } else {
Chris@1266 230 return tr("No local points");
Chris@1266 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@1266 241 text = QString(tr("Time:\t%1\nNo label"))
Chris@1266 242 .arg(rt.toText(true).c_str());
Chris@25 243 } else {
Chris@1266 244 text = QString(tr("Time:\t%1\nLabel:\t%2"))
Chris@1266 245 .arg(rt.toText(true).c_str())
Chris@1266 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@1266 255 int &resolution,
Chris@1266 256 SnapType snap) const
Chris@13 257 {
Chris@13 258 if (!m_model) {
Chris@1266 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@1266 266
Chris@1266 267 points = getLocalPoints(v, v->getXForFrame(frame));
Chris@1266 268 if (points.empty()) return false;
Chris@1266 269 frame = points.begin()->frame;
Chris@1266 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@1266 278 i != points.end(); ++i) {
Chris@13 279
Chris@1266 280 if (snap == SnapRight) {
Chris@28 281
Chris@1266 282 if (i->frame >= frame) {
Chris@1266 283 snapped = i->frame;
Chris@1266 284 found = true;
Chris@1266 285 break;
Chris@1266 286 }
Chris@28 287
Chris@1266 288 } else if (snap == SnapLeft) {
Chris@28 289
Chris@1266 290 if (i->frame <= frame) {
Chris@1266 291 snapped = i->frame;
Chris@1266 292 found = true; // don't break, as the next may be better
Chris@1266 293 } else {
Chris@1266 294 break;
Chris@1266 295 }
Chris@28 296
Chris@1266 297 } else { // nearest
Chris@28 298
Chris@1266 299 SparseOneDimensionalModel::PointList::const_iterator j = i;
Chris@1266 300 ++j;
Chris@28 301
Chris@1266 302 if (j == points.end()) {
Chris@28 303
Chris@1266 304 snapped = i->frame;
Chris@1266 305 found = true;
Chris@1266 306 break;
Chris@28 307
Chris@1266 308 } else if (j->frame >= frame) {
Chris@28 309
Chris@1266 310 if (j->frame - frame < frame - i->frame) {
Chris@1266 311 snapped = j->frame;
Chris@1266 312 } else {
Chris@1266 313 snapped = i->frame;
Chris@1266 314 }
Chris@1266 315 found = true;
Chris@1266 316 break;
Chris@1266 317 }
Chris@1266 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@1266 338 (frame0, frame1));
Chris@0 339
Chris@28 340 bool odd = false;
Chris@28 341 if (m_plotStyle == PlotSegmentation && !points.empty()) {
Chris@1266 342 int index = m_model->getIndexOf(*points.begin());
Chris@1266 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@1266 354 if (getBaseQColor() == Qt::black) {
Chris@1266 355 oddBrushColour = Qt::gray;
Chris@1266 356 } else if (getBaseQColor() == Qt::darkRed) {
Chris@1266 357 oddBrushColour = Qt::red;
Chris@1266 358 } else if (getBaseQColor() == Qt::darkBlue) {
Chris@1266 359 oddBrushColour = Qt::blue;
Chris@1266 360 } else if (getBaseQColor() == Qt::darkGreen) {
Chris@1266 361 oddBrushColour = Qt::green;
Chris@1266 362 } else {
Chris@1266 363 oddBrushColour = oddBrushColour.light(150);
Chris@1266 364 }
Chris@1266 365 oddBrushColour.setAlpha(100);
Chris@28 366 }
Chris@28 367
Chris@587 368 // SVDEBUG << "TimeInstantLayer::paint: resolution is "
Chris@1266 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@1266 375 SparseOneDimensionalModel::PointList localPoints =
Chris@1266 376 getLocalPoints(v, localPos.x());
Chris@1266 377 if (!localPoints.empty()) illuminateFrame = localPoints.begin()->frame;
Chris@0 378 }
Chris@1266 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@1266 384 i != points.end(); ++i) {
Chris@0 385
Chris@1266 386 const SparseOneDimensionalModel::Point &p(*i);
Chris@1266 387 SparseOneDimensionalModel::PointList::const_iterator j = i;
Chris@1266 388 ++j;
Chris@0 389
Chris@1266 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@1266 394 int iw = v->getXForFrame(p.frame + m_model->getResolution()) - x;
Chris@1266 395 if (iw < 2) {
Chris@1266 396 if (iw < 1) {
Chris@1266 397 iw = 2;
Chris@1266 398 if (j != points.end()) {
Chris@1266 399 int nx = v->getXForFrame(j->frame);
Chris@1266 400 if (nx < x + 3) iw = 1;
Chris@1266 401 }
Chris@1266 402 } else {
Chris@1266 403 iw = 2;
Chris@1266 404 }
Chris@1266 405 }
Chris@1266 406
Chris@1266 407 if (p.frame == illuminateFrame) {
Chris@1266 408 paint.setPen(getForegroundQColor(v->getView()));
Chris@1266 409 } else {
Chris@1266 410 paint.setPen(brushColour);
Chris@1266 411 }
Chris@23 412
Chris@1266 413 if (m_plotStyle == PlotInstants) {
Chris@1266 414 if (iw > 1) {
Chris@1266 415 paint.drawRect(x, 0, iw - 1, v->getPaintHeight() - 1);
Chris@1266 416 } else {
Chris@1266 417 paint.drawLine(x, 0, x, v->getPaintHeight() - 1);
Chris@1266 418 }
Chris@1266 419 } else {
Chris@28 420
Chris@1266 421 if (odd) paint.setBrush(oddBrushColour);
Chris@1266 422 else paint.setBrush(brushColour);
Chris@1266 423
Chris@1266 424 int nx;
Chris@1266 425
Chris@1266 426 if (j != points.end()) {
Chris@1266 427 const SparseOneDimensionalModel::Point &q(*j);
Chris@1266 428 nx = v->getXForFrame(q.frame);
Chris@1266 429 } else {
Chris@1266 430 nx = v->getXForFrame(m_model->getEndFrame());
Chris@1266 431 }
Chris@28 432
Chris@1266 433 if (nx >= x) {
Chris@1266 434
Chris@1266 435 if (illuminateFrame != p.frame &&
Chris@1266 436 (nx < x + 5 || x >= v->getPaintWidth() - 1)) {
Chris@1266 437 paint.setPen(Qt::NoPen);
Chris@1266 438 }
Chris@28 439
Chris@918 440 paint.drawRect(x, -1, nx - x, v->getPaintHeight() + 1);
Chris@1266 441 }
Chris@28 442
Chris@1266 443 odd = !odd;
Chris@1266 444 }
Chris@28 445
Chris@1266 446 paint.setPen(getBaseQColor());
Chris@1266 447
Chris@1266 448 if (p.label != "") {
Chris@0 449
Chris@1266 450 // only draw if there's enough room from here to the next point
Chris@0 451
Chris@1266 452 int lw = paint.fontMetrics().width(p.label);
Chris@1266 453 bool good = true;
Chris@0 454
Chris@1266 455 if (j != points.end()) {
Chris@1266 456 int nx = v->getXForFrame(j->frame);
Chris@1266 457 if (nx >= x && nx - x - iw - 3 <= lw) good = false;
Chris@1266 458 }
Chris@0 459
Chris@1266 460 if (good) {
Chris@1273 461 PaintAssistant::drawVisibleText(v, paint,
Chris@1273 462 x + iw + 2, textY,
Chris@1273 463 p.label,
Chris@1273 464 PaintAssistant::OutlinedText);
Chris@1266 465 }
Chris@1266 466 }
Chris@23 467
Chris@1266 468 prevX = x;
Chris@0 469 }
Chris@0 470 }
Chris@0 471
Chris@17 472 void
Chris@918 473 TimeInstantLayer::drawStart(LayerGeometryProvider *v, QMouseEvent *e)
Chris@17 474 {
Chris@387 475 #ifdef DEBUG_TIME_INSTANT_LAYER
Chris@682 476 cerr << "TimeInstantLayer::drawStart(" << e->x() << ")" << endl;
Chris@387 477 #endif
Chris@17 478
Chris@17 479 if (!m_model) return;
Chris@17 480
Chris@989 481 sv_frame_t frame = v->getFrameForX(e->x());
Chris@17 482 if (frame < 0) frame = 0;
Chris@21 483 frame = frame / m_model->getResolution() * m_model->getResolution();
Chris@22 484
Chris@17 485 m_editingPoint = SparseOneDimensionalModel::Point(frame, tr("New Point"));
Chris@22 486
Chris@376 487 if (m_editingCommand) finish(m_editingCommand);
Chris@22 488 m_editingCommand = new SparseOneDimensionalModel::EditCommand(m_model,
Chris@1266 489 tr("Draw Point"));
Chris@22 490 m_editingCommand->addPoint(m_editingPoint);
Chris@22 491
Chris@18 492 m_editing = true;
Chris@17 493 }
Chris@17 494
Chris@17 495 void
Chris@918 496 TimeInstantLayer::drawDrag(LayerGeometryProvider *v, QMouseEvent *e)
Chris@17 497 {
Chris@387 498 #ifdef DEBUG_TIME_INSTANT_LAYER
Chris@682 499 cerr << "TimeInstantLayer::drawDrag(" << e->x() << ")" << endl;
Chris@387 500 #endif
Chris@17 501
Chris@18 502 if (!m_model || !m_editing) return;
Chris@17 503
Chris@989 504 sv_frame_t frame = v->getFrameForX(e->x());
Chris@17 505 if (frame < 0) frame = 0;
Chris@21 506 frame = frame / m_model->getResolution() * m_model->getResolution();
Chris@22 507 m_editingCommand->deletePoint(m_editingPoint);
Chris@17 508 m_editingPoint.frame = frame;
Chris@22 509 m_editingCommand->addPoint(m_editingPoint);
Chris@17 510 }
Chris@17 511
Chris@17 512 void
Chris@918 513 TimeInstantLayer::drawEnd(LayerGeometryProvider *, QMouseEvent *)
Chris@17 514 {
Chris@387 515 #ifdef DEBUG_TIME_INSTANT_LAYER
Chris@682 516 cerr << "TimeInstantLayer::drawEnd(" << e->x() << ")" << endl;
Chris@387 517 #endif
Chris@18 518 if (!m_model || !m_editing) return;
Chris@23 519 QString newName = tr("Add Point at %1 s")
Chris@1266 520 .arg(RealTime::frame2RealTime(m_editingPoint.frame,
Chris@1266 521 m_model->getSampleRate())
Chris@1266 522 .toText(false).c_str());
Chris@23 523 m_editingCommand->setName(newName);
Chris@376 524 finish(m_editingCommand);
Chris@1408 525 m_editingCommand = nullptr;
Chris@18 526 m_editing = false;
Chris@18 527 }
Chris@18 528
Chris@18 529 void
Chris@918 530 TimeInstantLayer::eraseStart(LayerGeometryProvider *v, QMouseEvent *e)
Chris@335 531 {
Chris@335 532 if (!m_model) return;
Chris@335 533
Chris@335 534 SparseOneDimensionalModel::PointList points = getLocalPoints(v, e->x());
Chris@335 535 if (points.empty()) return;
Chris@335 536
Chris@335 537 m_editingPoint = *points.begin();
Chris@335 538
Chris@335 539 if (m_editingCommand) {
Chris@1266 540 finish(m_editingCommand);
Chris@1408 541 m_editingCommand = nullptr;
Chris@335 542 }
Chris@335 543
Chris@335 544 m_editing = true;
Chris@335 545 }
Chris@335 546
Chris@335 547 void
Chris@918 548 TimeInstantLayer::eraseDrag(LayerGeometryProvider *, QMouseEvent *)
Chris@335 549 {
Chris@335 550 }
Chris@335 551
Chris@335 552 void
Chris@918 553 TimeInstantLayer::eraseEnd(LayerGeometryProvider *v, QMouseEvent *e)
Chris@335 554 {
Chris@335 555 if (!m_model || !m_editing) return;
Chris@335 556
Chris@335 557 m_editing = false;
Chris@335 558
Chris@335 559 SparseOneDimensionalModel::PointList points = getLocalPoints(v, e->x());
Chris@335 560 if (points.empty()) return;
Chris@335 561 if (points.begin()->frame != m_editingPoint.frame) return;
Chris@335 562
Chris@335 563 m_editingCommand = new SparseOneDimensionalModel::EditCommand
Chris@335 564 (m_model, tr("Erase Point"));
Chris@335 565
Chris@335 566 m_editingCommand->deletePoint(m_editingPoint);
Chris@335 567
Chris@376 568 finish(m_editingCommand);
Chris@1408 569 m_editingCommand = nullptr;
Chris@335 570 m_editing = false;
Chris@335 571 }
Chris@335 572
Chris@335 573 void
Chris@918 574 TimeInstantLayer::editStart(LayerGeometryProvider *v, QMouseEvent *e)
Chris@18 575 {
Chris@387 576 #ifdef DEBUG_TIME_INSTANT_LAYER
Chris@682 577 cerr << "TimeInstantLayer::editStart(" << e->x() << ")" << endl;
Chris@387 578 #endif
Chris@18 579
Chris@17 580 if (!m_model) return;
Chris@18 581
Chris@44 582 SparseOneDimensionalModel::PointList points = getLocalPoints(v, e->x());
Chris@18 583 if (points.empty()) return;
Chris@18 584
Chris@18 585 m_editingPoint = *points.begin();
Chris@22 586
Chris@22 587 if (m_editingCommand) {
Chris@1266 588 finish(m_editingCommand);
Chris@1408 589 m_editingCommand = nullptr;
Chris@22 590 }
Chris@22 591
Chris@18 592 m_editing = true;
Chris@18 593 }
Chris@18 594
Chris@18 595 void
Chris@918 596 TimeInstantLayer::editDrag(LayerGeometryProvider *v, QMouseEvent *e)
Chris@18 597 {
Chris@387 598 #ifdef DEBUG_TIME_INSTANT_LAYER
Chris@682 599 cerr << "TimeInstantLayer::editDrag(" << e->x() << ")" << endl;
Chris@387 600 #endif
Chris@18 601
Chris@18 602 if (!m_model || !m_editing) return;
Chris@18 603
Chris@989 604 sv_frame_t frame = v->getFrameForX(e->x());
Chris@18 605 if (frame < 0) frame = 0;
Chris@21 606 frame = frame / m_model->getResolution() * m_model->getResolution();
Chris@22 607
Chris@22 608 if (!m_editingCommand) {
Chris@1266 609 m_editingCommand = new SparseOneDimensionalModel::EditCommand(m_model,
Chris@1266 610 tr("Drag Point"));
Chris@22 611 }
Chris@22 612
Chris@22 613 m_editingCommand->deletePoint(m_editingPoint);
Chris@18 614 m_editingPoint.frame = frame;
Chris@22 615 m_editingCommand->addPoint(m_editingPoint);
Chris@18 616 }
Chris@18 617
Chris@18 618 void
Chris@918 619 TimeInstantLayer::editEnd(LayerGeometryProvider *, QMouseEvent *)
Chris@18 620 {
Chris@387 621 #ifdef DEBUG_TIME_INSTANT_LAYER
Chris@682 622 cerr << "TimeInstantLayer::editEnd(" << e->x() << ")" << endl;
Chris@387 623 #endif
Chris@18 624 if (!m_model || !m_editing) return;
Chris@23 625 if (m_editingCommand) {
Chris@1266 626 QString newName = tr("Move Point to %1 s")
Chris@1266 627 .arg(RealTime::frame2RealTime(m_editingPoint.frame,
Chris@1266 628 m_model->getSampleRate())
Chris@1266 629 .toText(false).c_str());
Chris@1266 630 m_editingCommand->setName(newName);
Chris@1266 631 finish(m_editingCommand);
Chris@23 632 }
Chris@1408 633 m_editingCommand = nullptr;
Chris@18 634 m_editing = false;
Chris@17 635 }
Chris@17 636
Chris@255 637 bool
Chris@918 638 TimeInstantLayer::editOpen(LayerGeometryProvider *v, QMouseEvent *e)
Chris@70 639 {
Chris@255 640 if (!m_model) return false;
Chris@70 641
Chris@70 642 SparseOneDimensionalModel::PointList points = getLocalPoints(v, e->x());
Chris@255 643 if (points.empty()) return false;
Chris@70 644
Chris@70 645 SparseOneDimensionalModel::Point point = *points.begin();
Chris@70 646
Chris@70 647 ItemEditDialog *dialog = new ItemEditDialog
Chris@70 648 (m_model->getSampleRate(),
Chris@70 649 ItemEditDialog::ShowTime |
Chris@70 650 ItemEditDialog::ShowText);
Chris@70 651
Chris@70 652 dialog->setFrameTime(point.frame);
Chris@70 653 dialog->setText(point.label);
Chris@70 654
Chris@70 655 if (dialog->exec() == QDialog::Accepted) {
Chris@70 656
Chris@70 657 SparseOneDimensionalModel::Point newPoint = point;
Chris@70 658 newPoint.frame = dialog->getFrameTime();
Chris@70 659 newPoint.label = dialog->getText();
Chris@70 660
Chris@70 661 SparseOneDimensionalModel::EditCommand *command =
Chris@70 662 new SparseOneDimensionalModel::EditCommand(m_model, tr("Edit Point"));
Chris@70 663 command->deletePoint(point);
Chris@70 664 command->addPoint(newPoint);
Chris@376 665 finish(command);
Chris@70 666 }
Chris@70 667
Chris@70 668 delete dialog;
Chris@255 669 return true;
Chris@70 670 }
Chris@70 671
Chris@70 672 void
Chris@908 673 TimeInstantLayer::moveSelection(Selection s, sv_frame_t newStartFrame)
Chris@43 674 {
Chris@99 675 if (!m_model) return;
Chris@99 676
Chris@43 677 SparseOneDimensionalModel::EditCommand *command =
Chris@1266 678 new SparseOneDimensionalModel::EditCommand(m_model,
Chris@1266 679 tr("Drag Selection"));
Chris@43 680
Chris@43 681 SparseOneDimensionalModel::PointList points =
Chris@1266 682 m_model->getPoints(s.getStartFrame(), s.getEndFrame());
Chris@43 683
Chris@43 684 for (SparseOneDimensionalModel::PointList::iterator i = points.begin();
Chris@1266 685 i != points.end(); ++i) {
Chris@43 686
Chris@1266 687 if (s.contains(i->frame)) {
Chris@1266 688 SparseOneDimensionalModel::Point newPoint(*i);
Chris@1266 689 newPoint.frame = i->frame + newStartFrame - s.getStartFrame();
Chris@1266 690 command->deletePoint(*i);
Chris@1266 691 command->addPoint(newPoint);
Chris@1266 692 }
Chris@43 693 }
Chris@43 694
Chris@376 695 finish(command);
Chris@43 696 }
Chris@43 697
Chris@43 698 void
Chris@43 699 TimeInstantLayer::resizeSelection(Selection s, Selection newSize)
Chris@43 700 {
Chris@99 701 if (!m_model) return;
Chris@99 702
Chris@43 703 SparseOneDimensionalModel::EditCommand *command =
Chris@1266 704 new SparseOneDimensionalModel::EditCommand(m_model,
Chris@1266 705 tr("Resize Selection"));
Chris@43 706
Chris@43 707 SparseOneDimensionalModel::PointList points =
Chris@1266 708 m_model->getPoints(s.getStartFrame(), s.getEndFrame());
Chris@43 709
Chris@43 710 double ratio =
Chris@1266 711 double(newSize.getEndFrame() - newSize.getStartFrame()) /
Chris@1266 712 double(s.getEndFrame() - s.getStartFrame());
Chris@43 713
Chris@43 714 for (SparseOneDimensionalModel::PointList::iterator i = points.begin();
Chris@1266 715 i != points.end(); ++i) {
Chris@43 716
Chris@1266 717 if (s.contains(i->frame)) {
Chris@43 718
Chris@1266 719 double target = double(i->frame);
Chris@1266 720 target = double(newSize.getStartFrame()) +
Chris@1266 721 target - double(s.getStartFrame()) * ratio;
Chris@43 722
Chris@1266 723 SparseOneDimensionalModel::Point newPoint(*i);
Chris@1266 724 newPoint.frame = lrint(target);
Chris@1266 725 command->deletePoint(*i);
Chris@1266 726 command->addPoint(newPoint);
Chris@1266 727 }
Chris@43 728 }
Chris@43 729
Chris@376 730 finish(command);
Chris@43 731 }
Chris@43 732
Chris@43 733 void
Chris@43 734 TimeInstantLayer::deleteSelection(Selection s)
Chris@43 735 {
Chris@99 736 if (!m_model) return;
Chris@99 737
Chris@43 738 SparseOneDimensionalModel::EditCommand *command =
Chris@1266 739 new SparseOneDimensionalModel::EditCommand(m_model,
Chris@1266 740 tr("Delete Selection"));
Chris@43 741
Chris@43 742 SparseOneDimensionalModel::PointList points =
Chris@1266 743 m_model->getPoints(s.getStartFrame(), s.getEndFrame());
Chris@43 744
Chris@43 745 for (SparseOneDimensionalModel::PointList::iterator i = points.begin();
Chris@1266 746 i != points.end(); ++i) {
Chris@1266 747 if (s.contains(i->frame)) command->deletePoint(*i);
Chris@43 748 }
Chris@43 749
Chris@376 750 finish(command);
Chris@43 751 }
Chris@76 752
Chris@76 753 void
Chris@918 754 TimeInstantLayer::copy(LayerGeometryProvider *v, Selection s, Clipboard &to)
Chris@76 755 {
Chris@99 756 if (!m_model) return;
Chris@99 757
Chris@76 758 SparseOneDimensionalModel::PointList points =
Chris@1266 759 m_model->getPoints(s.getStartFrame(), s.getEndFrame());
Chris@76 760
Chris@76 761 for (SparseOneDimensionalModel::PointList::iterator i = points.begin();
Chris@1266 762 i != points.end(); ++i) {
Chris@1266 763 if (s.contains(i->frame)) {
Chris@1423 764 Event point(i->frame, i->label);
Chris@1423 765 to.addPoint(point.withReferenceFrame(alignToReference(v, i->frame)));
Chris@76 766 }
Chris@76 767 }
Chris@76 768 }
Chris@76 769
Chris@125 770 bool
Chris@918 771 TimeInstantLayer::paste(LayerGeometryProvider *v, const Clipboard &from, sv_frame_t frameOffset, bool)
Chris@76 772 {
Chris@125 773 if (!m_model) return false;
Chris@99 774
Chris@1423 775 const EventVector &points = from.getPoints();
Chris@76 776
Chris@358 777 bool realign = false;
Chris@358 778
Chris@360 779 if (clipboardHasDifferentAlignment(v, from)) {
Chris@358 780
Chris@360 781 QMessageBox::StandardButton button =
Chris@918 782 QMessageBox::question(v->getView(), tr("Re-align pasted instants?"),
Chris@360 783 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 784 QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel,
Chris@360 785 QMessageBox::Yes);
Chris@358 786
Chris@360 787 if (button == QMessageBox::Cancel) {
Chris@360 788 return false;
Chris@360 789 }
Chris@358 790
Chris@360 791 if (button == QMessageBox::Yes) {
Chris@360 792 realign = true;
Chris@360 793 }
Chris@358 794 }
Chris@358 795
Chris@358 796 SparseOneDimensionalModel::EditCommand *command =
Chris@1266 797 new SparseOneDimensionalModel::EditCommand(m_model, tr("Paste"));
Chris@358 798
Chris@1423 799 for (EventVector::const_iterator i = points.begin();
Chris@76 800 i != points.end(); ++i) {
Chris@76 801
Chris@908 802 sv_frame_t frame = 0;
Chris@359 803
Chris@359 804 if (!realign) {
Chris@359 805
Chris@359 806 frame = i->getFrame();
Chris@359 807
Chris@359 808 } else {
Chris@359 809
Chris@1423 810 if (i->hasReferenceFrame()) {
Chris@359 811 frame = i->getReferenceFrame();
Chris@359 812 frame = alignFromReference(v, frame);
Chris@359 813 } else {
Chris@359 814 frame = i->getFrame();
Chris@359 815 }
Chris@76 816 }
Chris@359 817
Chris@359 818 if (frameOffset > 0) frame += frameOffset;
Chris@359 819 else if (frameOffset < 0) {
Chris@359 820 if (frame > -frameOffset) frame += frameOffset;
Chris@359 821 else frame = 0;
Chris@359 822 }
Chris@359 823
Chris@76 824 SparseOneDimensionalModel::Point newPoint(frame);
Chris@1423 825 if (i->hasLabel()) {
Chris@125 826 newPoint.label = i->getLabel();
Chris@1423 827 } else if (i->hasValue()) {
Chris@125 828 newPoint.label = QString("%1").arg(i->getValue());
Chris@125 829 }
Chris@76 830
Chris@76 831 command->addPoint(newPoint);
Chris@76 832 }
Chris@76 833
Chris@376 834 finish(command);
Chris@125 835 return true;
Chris@76 836 }
Chris@43 837
Chris@287 838 int
Chris@287 839 TimeInstantLayer::getDefaultColourHint(bool darkbg, bool &impose)
Chris@287 840 {
Chris@287 841 impose = false;
Chris@287 842 return ColourDatabase::getInstance()->getColourIndex
Chris@287 843 (QString(darkbg ? "Bright Purple" : "Purple"));
Chris@287 844 }
Chris@287 845
Chris@316 846 void
Chris@316 847 TimeInstantLayer::toXml(QTextStream &stream,
Chris@316 848 QString indent, QString extraAttributes) const
Chris@6 849 {
Chris@316 850 SingleColourLayer::toXml(stream, indent,
Chris@316 851 extraAttributes +
Chris@316 852 QString(" plotStyle=\"%1\"")
Chris@316 853 .arg(m_plotStyle));
Chris@6 854 }
Chris@0 855
Chris@11 856 void
Chris@11 857 TimeInstantLayer::setProperties(const QXmlAttributes &attributes)
Chris@11 858 {
Chris@287 859 SingleColourLayer::setProperties(attributes);
Chris@28 860
Chris@28 861 bool ok;
Chris@28 862 PlotStyle style = (PlotStyle)
Chris@1266 863 attributes.value("plotStyle").toInt(&ok);
Chris@28 864 if (ok) setPlotStyle(style);
Chris@11 865 }
Chris@11 866