annotate layer/TimeInstantLayer.cpp @ 1386:fc3d89f88690 spectrogramparam

Use log-frequency rather than log-bin for calculating x coord in spectrum. This has the advantage that frequency positions don't move when we change the window size or oversampling ratio, but it does give us an unhelpfully large amount of space for very low frequencies - to be considered
author Chris Cannam
date Mon, 12 Nov 2018 11:34:34 +0000
parents 61418c112281
children c39f2d439d59
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@1273 461 PaintAssistant::drawVisibleText(v, paint,
Chris@1273 462 x + iw + 2, textY,
Chris@1273 463 p.label,
Chris@1273 464 PaintAssistant::OutlinedText);
Chris@1266 465 }
Chris@1266 466 }
Chris@23 467
Chris@1266 468 prevX = x;
Chris@0 469 }
Chris@0 470 }
Chris@0 471
Chris@17 472 void
Chris@918 473 TimeInstantLayer::drawStart(LayerGeometryProvider *v, QMouseEvent *e)
Chris@17 474 {
Chris@387 475 #ifdef DEBUG_TIME_INSTANT_LAYER
Chris@682 476 cerr << "TimeInstantLayer::drawStart(" << e->x() << ")" << endl;
Chris@387 477 #endif
Chris@17 478
Chris@17 479 if (!m_model) return;
Chris@17 480
Chris@989 481 sv_frame_t frame = v->getFrameForX(e->x());
Chris@17 482 if (frame < 0) frame = 0;
Chris@21 483 frame = frame / m_model->getResolution() * m_model->getResolution();
Chris@22 484
Chris@17 485 m_editingPoint = SparseOneDimensionalModel::Point(frame, tr("New Point"));
Chris@22 486
Chris@376 487 if (m_editingCommand) finish(m_editingCommand);
Chris@22 488 m_editingCommand = new SparseOneDimensionalModel::EditCommand(m_model,
Chris@1266 489 tr("Draw Point"));
Chris@22 490 m_editingCommand->addPoint(m_editingPoint);
Chris@22 491
Chris@18 492 m_editing = true;
Chris@17 493 }
Chris@17 494
Chris@17 495 void
Chris@918 496 TimeInstantLayer::drawDrag(LayerGeometryProvider *v, QMouseEvent *e)
Chris@17 497 {
Chris@387 498 #ifdef DEBUG_TIME_INSTANT_LAYER
Chris@682 499 cerr << "TimeInstantLayer::drawDrag(" << e->x() << ")" << endl;
Chris@387 500 #endif
Chris@17 501
Chris@18 502 if (!m_model || !m_editing) return;
Chris@17 503
Chris@989 504 sv_frame_t frame = v->getFrameForX(e->x());
Chris@17 505 if (frame < 0) frame = 0;
Chris@21 506 frame = frame / m_model->getResolution() * m_model->getResolution();
Chris@22 507 m_editingCommand->deletePoint(m_editingPoint);
Chris@17 508 m_editingPoint.frame = frame;
Chris@22 509 m_editingCommand->addPoint(m_editingPoint);
Chris@17 510 }
Chris@17 511
Chris@17 512 void
Chris@918 513 TimeInstantLayer::drawEnd(LayerGeometryProvider *, QMouseEvent *)
Chris@17 514 {
Chris@387 515 #ifdef DEBUG_TIME_INSTANT_LAYER
Chris@682 516 cerr << "TimeInstantLayer::drawEnd(" << e->x() << ")" << endl;
Chris@387 517 #endif
Chris@18 518 if (!m_model || !m_editing) return;
Chris@23 519 QString newName = tr("Add Point at %1 s")
Chris@1266 520 .arg(RealTime::frame2RealTime(m_editingPoint.frame,
Chris@1266 521 m_model->getSampleRate())
Chris@1266 522 .toText(false).c_str());
Chris@23 523 m_editingCommand->setName(newName);
Chris@376 524 finish(m_editingCommand);
Chris@22 525 m_editingCommand = 0;
Chris@18 526 m_editing = false;
Chris@18 527 }
Chris@18 528
Chris@18 529 void
Chris@918 530 TimeInstantLayer::eraseStart(LayerGeometryProvider *v, QMouseEvent *e)
Chris@335 531 {
Chris@335 532 if (!m_model) return;
Chris@335 533
Chris@335 534 SparseOneDimensionalModel::PointList points = getLocalPoints(v, e->x());
Chris@335 535 if (points.empty()) return;
Chris@335 536
Chris@335 537 m_editingPoint = *points.begin();
Chris@335 538
Chris@335 539 if (m_editingCommand) {
Chris@1266 540 finish(m_editingCommand);
Chris@1266 541 m_editingCommand = 0;
Chris@335 542 }
Chris@335 543
Chris@335 544 m_editing = true;
Chris@335 545 }
Chris@335 546
Chris@335 547 void
Chris@918 548 TimeInstantLayer::eraseDrag(LayerGeometryProvider *, QMouseEvent *)
Chris@335 549 {
Chris@335 550 }
Chris@335 551
Chris@335 552 void
Chris@918 553 TimeInstantLayer::eraseEnd(LayerGeometryProvider *v, QMouseEvent *e)
Chris@335 554 {
Chris@335 555 if (!m_model || !m_editing) return;
Chris@335 556
Chris@335 557 m_editing = false;
Chris@335 558
Chris@335 559 SparseOneDimensionalModel::PointList points = getLocalPoints(v, e->x());
Chris@335 560 if (points.empty()) return;
Chris@335 561 if (points.begin()->frame != m_editingPoint.frame) return;
Chris@335 562
Chris@335 563 m_editingCommand = new SparseOneDimensionalModel::EditCommand
Chris@335 564 (m_model, tr("Erase Point"));
Chris@335 565
Chris@335 566 m_editingCommand->deletePoint(m_editingPoint);
Chris@335 567
Chris@376 568 finish(m_editingCommand);
Chris@335 569 m_editingCommand = 0;
Chris@335 570 m_editing = false;
Chris@335 571 }
Chris@335 572
Chris@335 573 void
Chris@918 574 TimeInstantLayer::editStart(LayerGeometryProvider *v, QMouseEvent *e)
Chris@18 575 {
Chris@387 576 #ifdef DEBUG_TIME_INSTANT_LAYER
Chris@682 577 cerr << "TimeInstantLayer::editStart(" << e->x() << ")" << endl;
Chris@387 578 #endif
Chris@18 579
Chris@17 580 if (!m_model) return;
Chris@18 581
Chris@44 582 SparseOneDimensionalModel::PointList points = getLocalPoints(v, e->x());
Chris@18 583 if (points.empty()) return;
Chris@18 584
Chris@18 585 m_editingPoint = *points.begin();
Chris@22 586
Chris@22 587 if (m_editingCommand) {
Chris@1266 588 finish(m_editingCommand);
Chris@1266 589 m_editingCommand = 0;
Chris@22 590 }
Chris@22 591
Chris@18 592 m_editing = true;
Chris@18 593 }
Chris@18 594
Chris@18 595 void
Chris@918 596 TimeInstantLayer::editDrag(LayerGeometryProvider *v, QMouseEvent *e)
Chris@18 597 {
Chris@387 598 #ifdef DEBUG_TIME_INSTANT_LAYER
Chris@682 599 cerr << "TimeInstantLayer::editDrag(" << e->x() << ")" << endl;
Chris@387 600 #endif
Chris@18 601
Chris@18 602 if (!m_model || !m_editing) return;
Chris@18 603
Chris@989 604 sv_frame_t frame = v->getFrameForX(e->x());
Chris@18 605 if (frame < 0) frame = 0;
Chris@21 606 frame = frame / m_model->getResolution() * m_model->getResolution();
Chris@22 607
Chris@22 608 if (!m_editingCommand) {
Chris@1266 609 m_editingCommand = new SparseOneDimensionalModel::EditCommand(m_model,
Chris@1266 610 tr("Drag Point"));
Chris@22 611 }
Chris@22 612
Chris@22 613 m_editingCommand->deletePoint(m_editingPoint);
Chris@18 614 m_editingPoint.frame = frame;
Chris@22 615 m_editingCommand->addPoint(m_editingPoint);
Chris@18 616 }
Chris@18 617
Chris@18 618 void
Chris@918 619 TimeInstantLayer::editEnd(LayerGeometryProvider *, QMouseEvent *)
Chris@18 620 {
Chris@387 621 #ifdef DEBUG_TIME_INSTANT_LAYER
Chris@682 622 cerr << "TimeInstantLayer::editEnd(" << e->x() << ")" << endl;
Chris@387 623 #endif
Chris@18 624 if (!m_model || !m_editing) return;
Chris@23 625 if (m_editingCommand) {
Chris@1266 626 QString newName = tr("Move Point to %1 s")
Chris@1266 627 .arg(RealTime::frame2RealTime(m_editingPoint.frame,
Chris@1266 628 m_model->getSampleRate())
Chris@1266 629 .toText(false).c_str());
Chris@1266 630 m_editingCommand->setName(newName);
Chris@1266 631 finish(m_editingCommand);
Chris@23 632 }
Chris@22 633 m_editingCommand = 0;
Chris@18 634 m_editing = false;
Chris@17 635 }
Chris@17 636
Chris@255 637 bool
Chris@918 638 TimeInstantLayer::editOpen(LayerGeometryProvider *v, QMouseEvent *e)
Chris@70 639 {
Chris@255 640 if (!m_model) return false;
Chris@70 641
Chris@70 642 SparseOneDimensionalModel::PointList points = getLocalPoints(v, e->x());
Chris@255 643 if (points.empty()) return false;
Chris@70 644
Chris@70 645 SparseOneDimensionalModel::Point point = *points.begin();
Chris@70 646
Chris@70 647 ItemEditDialog *dialog = new ItemEditDialog
Chris@70 648 (m_model->getSampleRate(),
Chris@70 649 ItemEditDialog::ShowTime |
Chris@70 650 ItemEditDialog::ShowText);
Chris@70 651
Chris@70 652 dialog->setFrameTime(point.frame);
Chris@70 653 dialog->setText(point.label);
Chris@70 654
Chris@70 655 if (dialog->exec() == QDialog::Accepted) {
Chris@70 656
Chris@70 657 SparseOneDimensionalModel::Point newPoint = point;
Chris@70 658 newPoint.frame = dialog->getFrameTime();
Chris@70 659 newPoint.label = dialog->getText();
Chris@70 660
Chris@70 661 SparseOneDimensionalModel::EditCommand *command =
Chris@70 662 new SparseOneDimensionalModel::EditCommand(m_model, tr("Edit Point"));
Chris@70 663 command->deletePoint(point);
Chris@70 664 command->addPoint(newPoint);
Chris@376 665 finish(command);
Chris@70 666 }
Chris@70 667
Chris@70 668 delete dialog;
Chris@255 669 return true;
Chris@70 670 }
Chris@70 671
Chris@70 672 void
Chris@908 673 TimeInstantLayer::moveSelection(Selection s, sv_frame_t newStartFrame)
Chris@43 674 {
Chris@99 675 if (!m_model) return;
Chris@99 676
Chris@43 677 SparseOneDimensionalModel::EditCommand *command =
Chris@1266 678 new SparseOneDimensionalModel::EditCommand(m_model,
Chris@1266 679 tr("Drag Selection"));
Chris@43 680
Chris@43 681 SparseOneDimensionalModel::PointList points =
Chris@1266 682 m_model->getPoints(s.getStartFrame(), s.getEndFrame());
Chris@43 683
Chris@43 684 for (SparseOneDimensionalModel::PointList::iterator i = points.begin();
Chris@1266 685 i != points.end(); ++i) {
Chris@43 686
Chris@1266 687 if (s.contains(i->frame)) {
Chris@1266 688 SparseOneDimensionalModel::Point newPoint(*i);
Chris@1266 689 newPoint.frame = i->frame + newStartFrame - s.getStartFrame();
Chris@1266 690 command->deletePoint(*i);
Chris@1266 691 command->addPoint(newPoint);
Chris@1266 692 }
Chris@43 693 }
Chris@43 694
Chris@376 695 finish(command);
Chris@43 696 }
Chris@43 697
Chris@43 698 void
Chris@43 699 TimeInstantLayer::resizeSelection(Selection s, Selection newSize)
Chris@43 700 {
Chris@99 701 if (!m_model) return;
Chris@99 702
Chris@43 703 SparseOneDimensionalModel::EditCommand *command =
Chris@1266 704 new SparseOneDimensionalModel::EditCommand(m_model,
Chris@1266 705 tr("Resize Selection"));
Chris@43 706
Chris@43 707 SparseOneDimensionalModel::PointList points =
Chris@1266 708 m_model->getPoints(s.getStartFrame(), s.getEndFrame());
Chris@43 709
Chris@43 710 double ratio =
Chris@1266 711 double(newSize.getEndFrame() - newSize.getStartFrame()) /
Chris@1266 712 double(s.getEndFrame() - s.getStartFrame());
Chris@43 713
Chris@43 714 for (SparseOneDimensionalModel::PointList::iterator i = points.begin();
Chris@1266 715 i != points.end(); ++i) {
Chris@43 716
Chris@1266 717 if (s.contains(i->frame)) {
Chris@43 718
Chris@1266 719 double target = double(i->frame);
Chris@1266 720 target = double(newSize.getStartFrame()) +
Chris@1266 721 target - double(s.getStartFrame()) * ratio;
Chris@43 722
Chris@1266 723 SparseOneDimensionalModel::Point newPoint(*i);
Chris@1266 724 newPoint.frame = lrint(target);
Chris@1266 725 command->deletePoint(*i);
Chris@1266 726 command->addPoint(newPoint);
Chris@1266 727 }
Chris@43 728 }
Chris@43 729
Chris@376 730 finish(command);
Chris@43 731 }
Chris@43 732
Chris@43 733 void
Chris@43 734 TimeInstantLayer::deleteSelection(Selection s)
Chris@43 735 {
Chris@99 736 if (!m_model) return;
Chris@99 737
Chris@43 738 SparseOneDimensionalModel::EditCommand *command =
Chris@1266 739 new SparseOneDimensionalModel::EditCommand(m_model,
Chris@1266 740 tr("Delete Selection"));
Chris@43 741
Chris@43 742 SparseOneDimensionalModel::PointList points =
Chris@1266 743 m_model->getPoints(s.getStartFrame(), s.getEndFrame());
Chris@43 744
Chris@43 745 for (SparseOneDimensionalModel::PointList::iterator i = points.begin();
Chris@1266 746 i != points.end(); ++i) {
Chris@1266 747 if (s.contains(i->frame)) command->deletePoint(*i);
Chris@43 748 }
Chris@43 749
Chris@376 750 finish(command);
Chris@43 751 }
Chris@76 752
Chris@76 753 void
Chris@918 754 TimeInstantLayer::copy(LayerGeometryProvider *v, Selection s, Clipboard &to)
Chris@76 755 {
Chris@99 756 if (!m_model) return;
Chris@99 757
Chris@76 758 SparseOneDimensionalModel::PointList points =
Chris@1266 759 m_model->getPoints(s.getStartFrame(), s.getEndFrame());
Chris@76 760
Chris@76 761 for (SparseOneDimensionalModel::PointList::iterator i = points.begin();
Chris@1266 762 i != points.end(); ++i) {
Chris@1266 763 if (s.contains(i->frame)) {
Chris@76 764 Clipboard::Point point(i->frame, i->label);
Chris@359 765 point.setReferenceFrame(alignToReference(v, i->frame));
Chris@76 766 to.addPoint(point);
Chris@76 767 }
Chris@76 768 }
Chris@76 769 }
Chris@76 770
Chris@125 771 bool
Chris@918 772 TimeInstantLayer::paste(LayerGeometryProvider *v, const Clipboard &from, sv_frame_t frameOffset, bool)
Chris@76 773 {
Chris@125 774 if (!m_model) return false;
Chris@99 775
Chris@76 776 const Clipboard::PointList &points = from.getPoints();
Chris@76 777
Chris@358 778 bool realign = false;
Chris@358 779
Chris@360 780 if (clipboardHasDifferentAlignment(v, from)) {
Chris@358 781
Chris@360 782 QMessageBox::StandardButton button =
Chris@918 783 QMessageBox::question(v->getView(), tr("Re-align pasted instants?"),
Chris@360 784 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 785 QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel,
Chris@360 786 QMessageBox::Yes);
Chris@358 787
Chris@360 788 if (button == QMessageBox::Cancel) {
Chris@360 789 return false;
Chris@360 790 }
Chris@358 791
Chris@360 792 if (button == QMessageBox::Yes) {
Chris@360 793 realign = true;
Chris@360 794 }
Chris@358 795 }
Chris@358 796
Chris@358 797 SparseOneDimensionalModel::EditCommand *command =
Chris@1266 798 new SparseOneDimensionalModel::EditCommand(m_model, tr("Paste"));
Chris@358 799
Chris@76 800 for (Clipboard::PointList::const_iterator i = points.begin();
Chris@76 801 i != points.end(); ++i) {
Chris@76 802
Chris@76 803 if (!i->haveFrame()) continue;
Chris@359 804
Chris@908 805 sv_frame_t frame = 0;
Chris@359 806
Chris@359 807 if (!realign) {
Chris@359 808
Chris@359 809 frame = i->getFrame();
Chris@359 810
Chris@359 811 } else {
Chris@359 812
Chris@359 813 if (i->haveReferenceFrame()) {
Chris@359 814 frame = i->getReferenceFrame();
Chris@359 815 frame = alignFromReference(v, frame);
Chris@359 816 } else {
Chris@359 817 frame = i->getFrame();
Chris@359 818 }
Chris@76 819 }
Chris@359 820
Chris@359 821 if (frameOffset > 0) frame += frameOffset;
Chris@359 822 else if (frameOffset < 0) {
Chris@359 823 if (frame > -frameOffset) frame += frameOffset;
Chris@359 824 else frame = 0;
Chris@359 825 }
Chris@359 826
Chris@76 827 SparseOneDimensionalModel::Point newPoint(frame);
Chris@125 828 if (i->haveLabel()) {
Chris@125 829 newPoint.label = i->getLabel();
Chris@125 830 } else if (i->haveValue()) {
Chris@125 831 newPoint.label = QString("%1").arg(i->getValue());
Chris@125 832 }
Chris@76 833
Chris@76 834 command->addPoint(newPoint);
Chris@76 835 }
Chris@76 836
Chris@376 837 finish(command);
Chris@125 838 return true;
Chris@76 839 }
Chris@43 840
Chris@287 841 int
Chris@287 842 TimeInstantLayer::getDefaultColourHint(bool darkbg, bool &impose)
Chris@287 843 {
Chris@287 844 impose = false;
Chris@287 845 return ColourDatabase::getInstance()->getColourIndex
Chris@287 846 (QString(darkbg ? "Bright Purple" : "Purple"));
Chris@287 847 }
Chris@287 848
Chris@316 849 void
Chris@316 850 TimeInstantLayer::toXml(QTextStream &stream,
Chris@316 851 QString indent, QString extraAttributes) const
Chris@6 852 {
Chris@316 853 SingleColourLayer::toXml(stream, indent,
Chris@316 854 extraAttributes +
Chris@316 855 QString(" plotStyle=\"%1\"")
Chris@316 856 .arg(m_plotStyle));
Chris@6 857 }
Chris@0 858
Chris@11 859 void
Chris@11 860 TimeInstantLayer::setProperties(const QXmlAttributes &attributes)
Chris@11 861 {
Chris@287 862 SingleColourLayer::setProperties(attributes);
Chris@28 863
Chris@28 864 bool ok;
Chris@28 865 PlotStyle style = (PlotStyle)
Chris@1266 866 attributes.value("plotStyle").toInt(&ok);
Chris@28 867 if (ok) setPlotStyle(style);
Chris@11 868 }
Chris@11 869