annotate layer/TimeInstantLayer.cpp @ 1269:f2894944c6b8

Make the overlays at either end translucent, so they don't completely crop out any underlying text or necessary info (e.g. selection extents)
author Chris Cannam
date Thu, 19 Apr 2018 14:35:59 +0100
parents a34a2a25907c
children 61418c112281
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@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@1266 194 int fuzz = 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@1078 461 PaintAssistant::drawVisibleText(v, paint, x + iw + 2, textY, p.label, PaintAssistant::OutlinedText);
Chris@1266 462 // paint.drawText(x + iw + 2, textY, p.label);
Chris@1266 463 }
Chris@1266 464 }
Chris@23 465
Chris@1266 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@1266 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@1266 518 .arg(RealTime::frame2RealTime(m_editingPoint.frame,
Chris@1266 519 m_model->getSampleRate())
Chris@1266 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@1266 538 finish(m_editingCommand);
Chris@1266 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@1266 586 finish(m_editingCommand);
Chris@1266 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@1266 607 m_editingCommand = new SparseOneDimensionalModel::EditCommand(m_model,
Chris@1266 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@1266 624 QString newName = tr("Move Point to %1 s")
Chris@1266 625 .arg(RealTime::frame2RealTime(m_editingPoint.frame,
Chris@1266 626 m_model->getSampleRate())
Chris@1266 627 .toText(false).c_str());
Chris@1266 628 m_editingCommand->setName(newName);
Chris@1266 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@1266 676 new SparseOneDimensionalModel::EditCommand(m_model,
Chris@1266 677 tr("Drag Selection"));
Chris@43 678
Chris@43 679 SparseOneDimensionalModel::PointList points =
Chris@1266 680 m_model->getPoints(s.getStartFrame(), s.getEndFrame());
Chris@43 681
Chris@43 682 for (SparseOneDimensionalModel::PointList::iterator i = points.begin();
Chris@1266 683 i != points.end(); ++i) {
Chris@43 684
Chris@1266 685 if (s.contains(i->frame)) {
Chris@1266 686 SparseOneDimensionalModel::Point newPoint(*i);
Chris@1266 687 newPoint.frame = i->frame + newStartFrame - s.getStartFrame();
Chris@1266 688 command->deletePoint(*i);
Chris@1266 689 command->addPoint(newPoint);
Chris@1266 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@1266 702 new SparseOneDimensionalModel::EditCommand(m_model,
Chris@1266 703 tr("Resize Selection"));
Chris@43 704
Chris@43 705 SparseOneDimensionalModel::PointList points =
Chris@1266 706 m_model->getPoints(s.getStartFrame(), s.getEndFrame());
Chris@43 707
Chris@43 708 double ratio =
Chris@1266 709 double(newSize.getEndFrame() - newSize.getStartFrame()) /
Chris@1266 710 double(s.getEndFrame() - s.getStartFrame());
Chris@43 711
Chris@43 712 for (SparseOneDimensionalModel::PointList::iterator i = points.begin();
Chris@1266 713 i != points.end(); ++i) {
Chris@43 714
Chris@1266 715 if (s.contains(i->frame)) {
Chris@43 716
Chris@1266 717 double target = double(i->frame);
Chris@1266 718 target = double(newSize.getStartFrame()) +
Chris@1266 719 target - double(s.getStartFrame()) * ratio;
Chris@43 720
Chris@1266 721 SparseOneDimensionalModel::Point newPoint(*i);
Chris@1266 722 newPoint.frame = lrint(target);
Chris@1266 723 command->deletePoint(*i);
Chris@1266 724 command->addPoint(newPoint);
Chris@1266 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@1266 737 new SparseOneDimensionalModel::EditCommand(m_model,
Chris@1266 738 tr("Delete Selection"));
Chris@43 739
Chris@43 740 SparseOneDimensionalModel::PointList points =
Chris@1266 741 m_model->getPoints(s.getStartFrame(), s.getEndFrame());
Chris@43 742
Chris@43 743 for (SparseOneDimensionalModel::PointList::iterator i = points.begin();
Chris@1266 744 i != points.end(); ++i) {
Chris@1266 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@1266 757 m_model->getPoints(s.getStartFrame(), s.getEndFrame());
Chris@76 758
Chris@76 759 for (SparseOneDimensionalModel::PointList::iterator i = points.begin();
Chris@1266 760 i != points.end(); ++i) {
Chris@1266 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@1266 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@1266 864 attributes.value("plotStyle").toInt(&ok);
Chris@28 865 if (ok) setPlotStyle(style);
Chris@11 866 }
Chris@11 867