annotate layer/TimeInstantLayer.cpp @ 546:24d1d1528717

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