annotate layer/TimeInstantLayer.cpp @ 1438:09d008b5c8f4 single-point

Use updated ImageModel
author Chris Cannam
date Fri, 22 Mar 2019 17:49:15 +0000
parents 9abddbd57667
children e561f0a8d75b
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@1433 158 EventVector
Chris@918 159 TimeInstantLayer::getLocalPoints(LayerGeometryProvider *v, int x) const
Chris@0 160 {
Chris@1433 161 if (!m_model) return {};
Chris@1433 162
Chris@28 163 // Return a set of points that all have the same frame number, the
Chris@28 164 // nearest to the given x coordinate, and that are within a
Chris@28 165 // certain fuzz distance of that x coordinate.
Chris@28 166
Chris@989 167 sv_frame_t frame = v->getFrameForX(x);
Chris@0 168
Chris@1433 169 EventVector exact = m_model->getEventsStartingAt(frame);
Chris@1433 170 if (!exact.empty()) return exact;
Chris@0 171
Chris@1433 172 // overspill == 1, so one event either side of the given span
Chris@1433 173 EventVector neighbouring = m_model->getEventsWithin
Chris@1433 174 (frame, m_model->getResolution(), 1);
Chris@0 175
Chris@1433 176 double fuzz = v->scaleSize(2);
Chris@1433 177 sv_frame_t suitable = 0;
Chris@1433 178 bool have = false;
Chris@1433 179
Chris@1433 180 for (Event e: neighbouring) {
Chris@1433 181 sv_frame_t f = e.getFrame();
Chris@1433 182 if (f < v->getStartFrame() || f > v->getEndFrame()) {
Chris@1433 183 continue;
Chris@1433 184 }
Chris@1433 185 int px = v->getXForFrame(f);
Chris@1433 186 if ((px > x && px - x > fuzz) || (px < x && x - px > fuzz + 3)) {
Chris@1433 187 continue;
Chris@1433 188 }
Chris@1433 189 if (!have) {
Chris@1433 190 suitable = f;
Chris@1433 191 have = true;
Chris@1433 192 } else if (llabs(frame - f) < llabs(suitable - f)) {
Chris@1433 193 suitable = f;
Chris@1266 194 }
Chris@28 195 }
Chris@28 196
Chris@1433 197 if (have) {
Chris@1433 198 return m_model->getEventsStartingAt(suitable);
Chris@1433 199 } else {
Chris@1433 200 return {};
Chris@1433 201 }
Chris@0 202 }
Chris@0 203
Chris@25 204 QString
Chris@909 205 TimeInstantLayer::getLabelPreceding(sv_frame_t frame) const
Chris@552 206 {
Chris@1433 207 if (!m_model || !m_model->hasTextLabels()) return "";
Chris@1433 208
Chris@1433 209 Event e;
Chris@1433 210 if (m_model->getNearestEventMatching
Chris@1433 211 (frame,
Chris@1433 212 [](Event e) { return e.hasLabel() && e.getLabel() != ""; },
Chris@1433 213 EventSeries::Backward,
Chris@1433 214 e)) {
Chris@1433 215 return e.getLabel();
Chris@552 216 }
Chris@1433 217
Chris@552 218 return "";
Chris@552 219 }
Chris@552 220
Chris@552 221 QString
Chris@918 222 TimeInstantLayer::getFeatureDescription(LayerGeometryProvider *v, QPoint &pos) const
Chris@0 223 {
Chris@25 224 int x = pos.x();
Chris@0 225
Chris@25 226 if (!m_model || !m_model->getSampleRate()) return "";
Chris@0 227
Chris@1433 228 EventVector points = getLocalPoints(v, x);
Chris@0 229
Chris@0 230 if (points.empty()) {
Chris@1266 231 if (!m_model->isReady()) {
Chris@1266 232 return tr("In progress");
Chris@1266 233 } else {
Chris@1266 234 return tr("No local points");
Chris@1266 235 }
Chris@0 236 }
Chris@0 237
Chris@1433 238 sv_frame_t useFrame = points.begin()->getFrame();
Chris@0 239
Chris@0 240 RealTime rt = RealTime::frame2RealTime(useFrame, m_model->getSampleRate());
Chris@25 241
Chris@25 242 QString text;
Chris@0 243
Chris@1433 244 if (points.begin()->getLabel() == "") {
Chris@1266 245 text = QString(tr("Time:\t%1\nNo label"))
Chris@1266 246 .arg(rt.toText(true).c_str());
Chris@25 247 } else {
Chris@1266 248 text = QString(tr("Time:\t%1\nLabel:\t%2"))
Chris@1266 249 .arg(rt.toText(true).c_str())
Chris@1433 250 .arg(points.begin()->getLabel());
Chris@25 251 }
Chris@0 252
Chris@44 253 pos = QPoint(v->getXForFrame(useFrame), pos.y());
Chris@25 254 return text;
Chris@0 255 }
Chris@0 256
Chris@28 257 bool
Chris@918 258 TimeInstantLayer::snapToFeatureFrame(LayerGeometryProvider *v, sv_frame_t &frame,
Chris@1266 259 int &resolution,
Chris@1266 260 SnapType snap) const
Chris@13 261 {
Chris@13 262 if (!m_model) {
Chris@1266 263 return Layer::snapToFeatureFrame(v, frame, resolution, snap);
Chris@13 264 }
Chris@13 265
Chris@1433 266 // SnapLeft / SnapRight: return frame of nearest feature in that
Chris@1433 267 // direction no matter how far away
Chris@1433 268 //
Chris@1433 269 // SnapNeighbouring: return frame of feature that would be used in
Chris@1433 270 // an editing operation, i.e. closest feature in either direction
Chris@1433 271 // but only if it is "close enough"
Chris@1433 272
Chris@13 273 resolution = m_model->getResolution();
Chris@13 274
Chris@28 275 if (snap == SnapNeighbouring) {
Chris@1433 276 EventVector points = getLocalPoints(v, v->getXForFrame(frame));
Chris@1266 277 if (points.empty()) return false;
Chris@1433 278 frame = points.begin()->getFrame();
Chris@1266 279 return true;
Chris@13 280 }
Chris@13 281
Chris@1433 282 Event e;
Chris@1433 283 if (m_model->getNearestEventMatching
Chris@1433 284 (frame,
Chris@1433 285 [](Event) { return true; },
Chris@1433 286 snap == SnapLeft ? EventSeries::Backward : EventSeries::Forward,
Chris@1433 287 e)) {
Chris@1433 288 frame = e.getFrame();
Chris@1433 289 return true;
Chris@1433 290 }
Chris@1433 291
Chris@1433 292 return false;
Chris@13 293 }
Chris@13 294
Chris@0 295 void
Chris@916 296 TimeInstantLayer::paint(LayerGeometryProvider *v, QPainter &paint, QRect rect) const
Chris@0 297 {
Chris@0 298 if (!m_model || !m_model->isOK()) return;
Chris@0 299
Chris@0 300 // Profiler profiler("TimeInstantLayer::paint", true);
Chris@0 301
Chris@20 302 int x0 = rect.left(), x1 = rect.right();
Chris@0 303
Chris@989 304 sv_frame_t frame0 = v->getFrameForX(x0);
Chris@989 305 sv_frame_t frame1 = v->getFrameForX(x1);
Chris@0 306
Chris@1433 307 EventVector points(m_model->getEventsWithin(frame0, frame1 - frame0));
Chris@0 308
Chris@28 309 bool odd = false;
Chris@28 310 if (m_plotStyle == PlotSegmentation && !points.empty()) {
Chris@1433 311 int index = m_model->getRowForFrame(points.begin()->getFrame());
Chris@1266 312 odd = ((index % 2) == 1);
Chris@28 313 }
Chris@28 314
Chris@287 315 paint.setPen(getBaseQColor());
Chris@0 316
Chris@287 317 QColor brushColour(getBaseQColor());
Chris@0 318 brushColour.setAlpha(100);
Chris@0 319 paint.setBrush(brushColour);
Chris@0 320
Chris@28 321 QColor oddBrushColour(brushColour);
Chris@28 322 if (m_plotStyle == PlotSegmentation) {
Chris@1266 323 if (getBaseQColor() == Qt::black) {
Chris@1266 324 oddBrushColour = Qt::gray;
Chris@1266 325 } else if (getBaseQColor() == Qt::darkRed) {
Chris@1266 326 oddBrushColour = Qt::red;
Chris@1266 327 } else if (getBaseQColor() == Qt::darkBlue) {
Chris@1266 328 oddBrushColour = Qt::blue;
Chris@1266 329 } else if (getBaseQColor() == Qt::darkGreen) {
Chris@1266 330 oddBrushColour = Qt::green;
Chris@1266 331 } else {
Chris@1266 332 oddBrushColour = oddBrushColour.light(150);
Chris@1266 333 }
Chris@1266 334 oddBrushColour.setAlpha(100);
Chris@28 335 }
Chris@28 336
Chris@587 337 // SVDEBUG << "TimeInstantLayer::paint: resolution is "
Chris@1266 338 // << m_model->getResolution() << " frames" << endl;
Chris@0 339
Chris@0 340 QPoint localPos;
Chris@989 341 sv_frame_t illuminateFrame = -1;
Chris@0 342
Chris@44 343 if (v->shouldIlluminateLocalFeatures(this, localPos)) {
Chris@1433 344 EventVector localPoints = getLocalPoints(v, localPos.x());
Chris@1433 345 if (!localPoints.empty()) {
Chris@1433 346 illuminateFrame = localPoints.begin()->getFrame();
Chris@1433 347 }
Chris@0 348 }
Chris@1266 349
Chris@23 350 int prevX = -1;
Chris@79 351 int textY = v->getTextLabelHeight(this, paint);
Chris@79 352
Chris@1433 353 for (EventVector::const_iterator i = points.begin();
Chris@1266 354 i != points.end(); ++i) {
Chris@0 355
Chris@1433 356 Event p(*i);
Chris@1433 357 EventVector::const_iterator j = i;
Chris@1266 358 ++j;
Chris@0 359
Chris@1433 360 int x = v->getXForFrame(p.getFrame());
Chris@576 361 if (x == prevX && m_plotStyle == PlotInstants &&
Chris@1433 362 p.getFrame() != illuminateFrame) continue;
Chris@23 363
Chris@1433 364 int iw = v->getXForFrame(p.getFrame() + m_model->getResolution()) - x;
Chris@1266 365 if (iw < 2) {
Chris@1266 366 if (iw < 1) {
Chris@1266 367 iw = 2;
Chris@1266 368 if (j != points.end()) {
Chris@1433 369 int nx = v->getXForFrame(j->getFrame());
Chris@1266 370 if (nx < x + 3) iw = 1;
Chris@1266 371 }
Chris@1266 372 } else {
Chris@1266 373 iw = 2;
Chris@1266 374 }
Chris@1266 375 }
Chris@1266 376
Chris@1433 377 if (p.getFrame() == illuminateFrame) {
Chris@1266 378 paint.setPen(getForegroundQColor(v->getView()));
Chris@1266 379 } else {
Chris@1266 380 paint.setPen(brushColour);
Chris@1266 381 }
Chris@23 382
Chris@1266 383 if (m_plotStyle == PlotInstants) {
Chris@1266 384 if (iw > 1) {
Chris@1266 385 paint.drawRect(x, 0, iw - 1, v->getPaintHeight() - 1);
Chris@1266 386 } else {
Chris@1266 387 paint.drawLine(x, 0, x, v->getPaintHeight() - 1);
Chris@1266 388 }
Chris@1266 389 } else {
Chris@28 390
Chris@1266 391 if (odd) paint.setBrush(oddBrushColour);
Chris@1266 392 else paint.setBrush(brushColour);
Chris@1266 393
Chris@1266 394 int nx;
Chris@1266 395
Chris@1266 396 if (j != points.end()) {
Chris@1433 397 Event q(*j);
Chris@1433 398 nx = v->getXForFrame(q.getFrame());
Chris@1266 399 } else {
Chris@1266 400 nx = v->getXForFrame(m_model->getEndFrame());
Chris@1266 401 }
Chris@28 402
Chris@1266 403 if (nx >= x) {
Chris@1266 404
Chris@1433 405 if (illuminateFrame != p.getFrame() &&
Chris@1266 406 (nx < x + 5 || x >= v->getPaintWidth() - 1)) {
Chris@1266 407 paint.setPen(Qt::NoPen);
Chris@1266 408 }
Chris@28 409
Chris@918 410 paint.drawRect(x, -1, nx - x, v->getPaintHeight() + 1);
Chris@1266 411 }
Chris@28 412
Chris@1266 413 odd = !odd;
Chris@1266 414 }
Chris@28 415
Chris@1266 416 paint.setPen(getBaseQColor());
Chris@1266 417
Chris@1433 418 if (p.getLabel() != "") {
Chris@0 419
Chris@1266 420 // only draw if there's enough room from here to the next point
Chris@0 421
Chris@1433 422 int lw = paint.fontMetrics().width(p.getLabel());
Chris@1266 423 bool good = true;
Chris@0 424
Chris@1266 425 if (j != points.end()) {
Chris@1433 426 int nx = v->getXForFrame(j->getFrame());
Chris@1266 427 if (nx >= x && nx - x - iw - 3 <= lw) good = false;
Chris@1266 428 }
Chris@0 429
Chris@1266 430 if (good) {
Chris@1273 431 PaintAssistant::drawVisibleText(v, paint,
Chris@1273 432 x + iw + 2, textY,
Chris@1433 433 p.getLabel(),
Chris@1273 434 PaintAssistant::OutlinedText);
Chris@1266 435 }
Chris@1266 436 }
Chris@23 437
Chris@1266 438 prevX = x;
Chris@0 439 }
Chris@0 440 }
Chris@0 441
Chris@17 442 void
Chris@918 443 TimeInstantLayer::drawStart(LayerGeometryProvider *v, QMouseEvent *e)
Chris@17 444 {
Chris@387 445 #ifdef DEBUG_TIME_INSTANT_LAYER
Chris@682 446 cerr << "TimeInstantLayer::drawStart(" << e->x() << ")" << endl;
Chris@387 447 #endif
Chris@17 448
Chris@17 449 if (!m_model) return;
Chris@17 450
Chris@989 451 sv_frame_t frame = v->getFrameForX(e->x());
Chris@17 452 if (frame < 0) frame = 0;
Chris@21 453 frame = frame / m_model->getResolution() * m_model->getResolution();
Chris@22 454
Chris@1433 455 m_editingPoint = Event(frame, tr("New Point"));
Chris@22 456
Chris@376 457 if (m_editingCommand) finish(m_editingCommand);
Chris@1433 458 m_editingCommand = new ChangeEventsCommand(m_model, tr("Draw Point"));
Chris@1433 459 m_editingCommand->add(m_editingPoint);
Chris@22 460
Chris@18 461 m_editing = true;
Chris@17 462 }
Chris@17 463
Chris@17 464 void
Chris@918 465 TimeInstantLayer::drawDrag(LayerGeometryProvider *v, QMouseEvent *e)
Chris@17 466 {
Chris@387 467 #ifdef DEBUG_TIME_INSTANT_LAYER
Chris@682 468 cerr << "TimeInstantLayer::drawDrag(" << e->x() << ")" << endl;
Chris@387 469 #endif
Chris@17 470
Chris@18 471 if (!m_model || !m_editing) return;
Chris@17 472
Chris@989 473 sv_frame_t frame = v->getFrameForX(e->x());
Chris@17 474 if (frame < 0) frame = 0;
Chris@21 475 frame = frame / m_model->getResolution() * m_model->getResolution();
Chris@1433 476 m_editingCommand->remove(m_editingPoint);
Chris@1433 477 m_editingPoint = m_editingPoint.withFrame(frame);
Chris@1433 478 m_editingCommand->add(m_editingPoint);
Chris@17 479 }
Chris@17 480
Chris@17 481 void
Chris@918 482 TimeInstantLayer::drawEnd(LayerGeometryProvider *, QMouseEvent *)
Chris@17 483 {
Chris@387 484 #ifdef DEBUG_TIME_INSTANT_LAYER
Chris@682 485 cerr << "TimeInstantLayer::drawEnd(" << e->x() << ")" << endl;
Chris@387 486 #endif
Chris@18 487 if (!m_model || !m_editing) return;
Chris@23 488 QString newName = tr("Add Point at %1 s")
Chris@1433 489 .arg(RealTime::frame2RealTime(m_editingPoint.getFrame(),
Chris@1266 490 m_model->getSampleRate())
Chris@1266 491 .toText(false).c_str());
Chris@23 492 m_editingCommand->setName(newName);
Chris@376 493 finish(m_editingCommand);
Chris@1408 494 m_editingCommand = nullptr;
Chris@18 495 m_editing = false;
Chris@18 496 }
Chris@18 497
Chris@18 498 void
Chris@918 499 TimeInstantLayer::eraseStart(LayerGeometryProvider *v, QMouseEvent *e)
Chris@335 500 {
Chris@335 501 if (!m_model) return;
Chris@335 502
Chris@1433 503 EventVector points = getLocalPoints(v, e->x());
Chris@335 504 if (points.empty()) return;
Chris@335 505
Chris@335 506 m_editingPoint = *points.begin();
Chris@335 507
Chris@335 508 if (m_editingCommand) {
Chris@1266 509 finish(m_editingCommand);
Chris@1408 510 m_editingCommand = nullptr;
Chris@335 511 }
Chris@335 512
Chris@335 513 m_editing = true;
Chris@335 514 }
Chris@335 515
Chris@335 516 void
Chris@918 517 TimeInstantLayer::eraseDrag(LayerGeometryProvider *, QMouseEvent *)
Chris@335 518 {
Chris@335 519 }
Chris@335 520
Chris@335 521 void
Chris@918 522 TimeInstantLayer::eraseEnd(LayerGeometryProvider *v, QMouseEvent *e)
Chris@335 523 {
Chris@335 524 if (!m_model || !m_editing) return;
Chris@335 525
Chris@335 526 m_editing = false;
Chris@335 527
Chris@1433 528 EventVector points = getLocalPoints(v, e->x());
Chris@335 529 if (points.empty()) return;
Chris@1433 530 if (points.begin()->getFrame() != m_editingPoint.getFrame()) return;
Chris@335 531
Chris@1433 532 m_editingCommand = new ChangeEventsCommand(m_model, tr("Erase Point"));
Chris@1433 533 m_editingCommand->remove(m_editingPoint);
Chris@376 534 finish(m_editingCommand);
Chris@1408 535 m_editingCommand = nullptr;
Chris@335 536 m_editing = false;
Chris@335 537 }
Chris@335 538
Chris@335 539 void
Chris@918 540 TimeInstantLayer::editStart(LayerGeometryProvider *v, QMouseEvent *e)
Chris@18 541 {
Chris@387 542 #ifdef DEBUG_TIME_INSTANT_LAYER
Chris@682 543 cerr << "TimeInstantLayer::editStart(" << e->x() << ")" << endl;
Chris@387 544 #endif
Chris@18 545
Chris@17 546 if (!m_model) return;
Chris@18 547
Chris@1433 548 EventVector points = getLocalPoints(v, e->x());
Chris@18 549 if (points.empty()) return;
Chris@18 550
Chris@18 551 m_editingPoint = *points.begin();
Chris@22 552
Chris@22 553 if (m_editingCommand) {
Chris@1266 554 finish(m_editingCommand);
Chris@1408 555 m_editingCommand = nullptr;
Chris@22 556 }
Chris@22 557
Chris@18 558 m_editing = true;
Chris@18 559 }
Chris@18 560
Chris@18 561 void
Chris@918 562 TimeInstantLayer::editDrag(LayerGeometryProvider *v, QMouseEvent *e)
Chris@18 563 {
Chris@387 564 #ifdef DEBUG_TIME_INSTANT_LAYER
Chris@682 565 cerr << "TimeInstantLayer::editDrag(" << e->x() << ")" << endl;
Chris@387 566 #endif
Chris@18 567
Chris@18 568 if (!m_model || !m_editing) return;
Chris@18 569
Chris@989 570 sv_frame_t frame = v->getFrameForX(e->x());
Chris@18 571 if (frame < 0) frame = 0;
Chris@21 572 frame = frame / m_model->getResolution() * m_model->getResolution();
Chris@22 573
Chris@22 574 if (!m_editingCommand) {
Chris@1433 575 m_editingCommand = new ChangeEventsCommand(m_model, tr("Drag Point"));
Chris@22 576 }
Chris@22 577
Chris@1433 578 m_editingCommand->remove(m_editingPoint);
Chris@1433 579 m_editingPoint = m_editingPoint.withFrame(frame);
Chris@1433 580 m_editingCommand->add(m_editingPoint);
Chris@18 581 }
Chris@18 582
Chris@18 583 void
Chris@918 584 TimeInstantLayer::editEnd(LayerGeometryProvider *, QMouseEvent *)
Chris@18 585 {
Chris@387 586 #ifdef DEBUG_TIME_INSTANT_LAYER
Chris@682 587 cerr << "TimeInstantLayer::editEnd(" << e->x() << ")" << endl;
Chris@387 588 #endif
Chris@18 589 if (!m_model || !m_editing) return;
Chris@23 590 if (m_editingCommand) {
Chris@1266 591 QString newName = tr("Move Point to %1 s")
Chris@1433 592 .arg(RealTime::frame2RealTime(m_editingPoint.getFrame(),
Chris@1266 593 m_model->getSampleRate())
Chris@1266 594 .toText(false).c_str());
Chris@1266 595 m_editingCommand->setName(newName);
Chris@1266 596 finish(m_editingCommand);
Chris@23 597 }
Chris@1408 598 m_editingCommand = nullptr;
Chris@18 599 m_editing = false;
Chris@17 600 }
Chris@17 601
Chris@255 602 bool
Chris@918 603 TimeInstantLayer::editOpen(LayerGeometryProvider *v, QMouseEvent *e)
Chris@70 604 {
Chris@255 605 if (!m_model) return false;
Chris@70 606
Chris@1433 607 EventVector points = getLocalPoints(v, e->x());
Chris@255 608 if (points.empty()) return false;
Chris@70 609
Chris@1433 610 Event point = *points.begin();
Chris@70 611
Chris@70 612 ItemEditDialog *dialog = new ItemEditDialog
Chris@70 613 (m_model->getSampleRate(),
Chris@70 614 ItemEditDialog::ShowTime |
Chris@70 615 ItemEditDialog::ShowText);
Chris@70 616
Chris@1433 617 dialog->setFrameTime(point.getFrame());
Chris@1433 618 dialog->setText(point.getLabel());
Chris@70 619
Chris@70 620 if (dialog->exec() == QDialog::Accepted) {
Chris@70 621
Chris@1433 622 Event newPoint = point
Chris@1433 623 .withFrame(dialog->getFrameTime())
Chris@1433 624 .withLabel(dialog->getText());
Chris@70 625
Chris@1433 626 ChangeEventsCommand *command =
Chris@1433 627 new ChangeEventsCommand(m_model, tr("Edit Point"));
Chris@1433 628 command->remove(point);
Chris@1433 629 command->add(newPoint);
Chris@376 630 finish(command);
Chris@70 631 }
Chris@70 632
Chris@70 633 delete dialog;
Chris@255 634 return true;
Chris@70 635 }
Chris@70 636
Chris@70 637 void
Chris@908 638 TimeInstantLayer::moveSelection(Selection s, sv_frame_t newStartFrame)
Chris@43 639 {
Chris@99 640 if (!m_model) return;
Chris@99 641
Chris@1433 642 ChangeEventsCommand *command =
Chris@1433 643 new ChangeEventsCommand(m_model, tr("Drag Selection"));
Chris@43 644
Chris@1433 645 EventVector points =
Chris@1433 646 m_model->getEventsWithin(s.getStartFrame(), s.getDuration());
Chris@43 647
Chris@1433 648 for (auto p: points) {
Chris@1433 649 Event newPoint = p
Chris@1433 650 .withFrame(p.getFrame() + newStartFrame - s.getStartFrame());
Chris@1433 651 command->remove(p);
Chris@1433 652 command->add(newPoint);
Chris@43 653 }
Chris@43 654
Chris@376 655 finish(command);
Chris@43 656 }
Chris@43 657
Chris@43 658 void
Chris@43 659 TimeInstantLayer::resizeSelection(Selection s, Selection newSize)
Chris@43 660 {
Chris@99 661 if (!m_model) return;
Chris@99 662
Chris@1433 663 ChangeEventsCommand *command =
Chris@1433 664 new ChangeEventsCommand(m_model, tr("Resize Selection"));
Chris@43 665
Chris@1433 666 EventVector points =
Chris@1433 667 m_model->getEventsWithin(s.getStartFrame(), s.getDuration());
Chris@43 668
Chris@1433 669 double ratio = double(newSize.getDuration()) / double(s.getDuration());
Chris@1433 670 double oldStart = double(s.getStartFrame());
Chris@1433 671 double newStart = double(newSize.getStartFrame());
Chris@43 672
Chris@1433 673 for (auto p: points) {
Chris@43 674
Chris@1433 675 double newFrame = (double(p.getFrame()) - oldStart) * ratio + newStart;
Chris@43 676
Chris@1433 677 Event newPoint = p
Chris@1433 678 .withFrame(lrint(newFrame));
Chris@1433 679 command->remove(p);
Chris@1433 680 command->add(newPoint);
Chris@43 681 }
Chris@43 682
Chris@376 683 finish(command);
Chris@43 684 }
Chris@43 685
Chris@43 686 void
Chris@43 687 TimeInstantLayer::deleteSelection(Selection s)
Chris@43 688 {
Chris@99 689 if (!m_model) return;
Chris@99 690
Chris@1433 691 ChangeEventsCommand *command =
Chris@1433 692 new ChangeEventsCommand(m_model, tr("Delete Selection"));
Chris@43 693
Chris@1433 694 EventVector points =
Chris@1433 695 m_model->getEventsWithin(s.getStartFrame(), s.getDuration());
Chris@43 696
Chris@1433 697 for (auto p: points) {
Chris@1433 698 command->remove(p);
Chris@43 699 }
Chris@43 700
Chris@376 701 finish(command);
Chris@43 702 }
Chris@76 703
Chris@76 704 void
Chris@918 705 TimeInstantLayer::copy(LayerGeometryProvider *v, Selection s, Clipboard &to)
Chris@76 706 {
Chris@99 707 if (!m_model) return;
Chris@99 708
Chris@1433 709 EventVector points =
Chris@1433 710 m_model->getEventsWithin(s.getStartFrame(), s.getDuration());
Chris@76 711
Chris@1433 712 for (auto p: points) {
Chris@1433 713 to.addPoint(p.withReferenceFrame(alignToReference(v, p.getFrame())));
Chris@76 714 }
Chris@76 715 }
Chris@76 716
Chris@125 717 bool
Chris@918 718 TimeInstantLayer::paste(LayerGeometryProvider *v, const Clipboard &from, sv_frame_t frameOffset, bool)
Chris@76 719 {
Chris@125 720 if (!m_model) return false;
Chris@99 721
Chris@1433 722 EventVector points = from.getPoints();
Chris@76 723
Chris@358 724 bool realign = false;
Chris@358 725
Chris@360 726 if (clipboardHasDifferentAlignment(v, from)) {
Chris@358 727
Chris@360 728 QMessageBox::StandardButton button =
Chris@918 729 QMessageBox::question(v->getView(), tr("Re-align pasted instants?"),
Chris@360 730 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 731 QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel,
Chris@360 732 QMessageBox::Yes);
Chris@358 733
Chris@360 734 if (button == QMessageBox::Cancel) {
Chris@360 735 return false;
Chris@360 736 }
Chris@358 737
Chris@360 738 if (button == QMessageBox::Yes) {
Chris@360 739 realign = true;
Chris@360 740 }
Chris@358 741 }
Chris@358 742
Chris@1433 743 ChangeEventsCommand *command =
Chris@1433 744 new ChangeEventsCommand(m_model, tr("Paste"));
Chris@358 745
Chris@1423 746 for (EventVector::const_iterator i = points.begin();
Chris@76 747 i != points.end(); ++i) {
Chris@76 748
Chris@908 749 sv_frame_t frame = 0;
Chris@359 750
Chris@359 751 if (!realign) {
Chris@359 752
Chris@359 753 frame = i->getFrame();
Chris@359 754
Chris@359 755 } else {
Chris@359 756
Chris@1423 757 if (i->hasReferenceFrame()) {
Chris@359 758 frame = i->getReferenceFrame();
Chris@359 759 frame = alignFromReference(v, frame);
Chris@359 760 } else {
Chris@359 761 frame = i->getFrame();
Chris@359 762 }
Chris@76 763 }
Chris@359 764
Chris@359 765 if (frameOffset > 0) frame += frameOffset;
Chris@359 766 else if (frameOffset < 0) {
Chris@359 767 if (frame > -frameOffset) frame += frameOffset;
Chris@359 768 else frame = 0;
Chris@359 769 }
Chris@359 770
Chris@1433 771 Event newPoint = *i;
Chris@1433 772 if (!i->hasLabel() && i->hasValue()) {
Chris@1433 773 newPoint = newPoint.withLabel(QString("%1").arg(i->getValue()));
Chris@125 774 }
Chris@76 775
Chris@1433 776 command->add(newPoint);
Chris@76 777 }
Chris@76 778
Chris@376 779 finish(command);
Chris@125 780 return true;
Chris@76 781 }
Chris@43 782
Chris@287 783 int
Chris@287 784 TimeInstantLayer::getDefaultColourHint(bool darkbg, bool &impose)
Chris@287 785 {
Chris@287 786 impose = false;
Chris@287 787 return ColourDatabase::getInstance()->getColourIndex
Chris@287 788 (QString(darkbg ? "Bright Purple" : "Purple"));
Chris@287 789 }
Chris@287 790
Chris@316 791 void
Chris@316 792 TimeInstantLayer::toXml(QTextStream &stream,
Chris@316 793 QString indent, QString extraAttributes) const
Chris@6 794 {
Chris@316 795 SingleColourLayer::toXml(stream, indent,
Chris@316 796 extraAttributes +
Chris@316 797 QString(" plotStyle=\"%1\"")
Chris@316 798 .arg(m_plotStyle));
Chris@6 799 }
Chris@0 800
Chris@11 801 void
Chris@11 802 TimeInstantLayer::setProperties(const QXmlAttributes &attributes)
Chris@11 803 {
Chris@287 804 SingleColourLayer::setProperties(attributes);
Chris@28 805
Chris@28 806 bool ok;
Chris@28 807 PlotStyle style = (PlotStyle)
Chris@1266 808 attributes.value("plotStyle").toInt(&ok);
Chris@28 809 if (ok) setPlotStyle(style);
Chris@11 810 }
Chris@11 811