annotate layer/TimeInstantLayer.cpp @ 789:9fd1bdf214dd tonioni

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