annotate layer/TimeInstantLayer.cpp @ 494:b3140e9e0665

* Some fairly simplistic code to set up layer type properties based on RDF data about feature types (both when running transforms and when importing features from RDF files).
author Chris Cannam
date Thu, 12 Feb 2009 15:26:43 +0000
parents 427e5c58658e
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