annotate layer/TimeInstantLayer.cpp @ 1088:c520f90bbf2e spectrogram-minor-refactor

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