annotate layer/TimeInstantLayer.cpp @ 576:a4ba6c96b66d

* Make text labels in time instant layer more apparent (use drawVisibleText) * Fix bug that caused time-instants in segmentation mode to come up white when zoomed far out
author Chris Cannam
date Mon, 25 Oct 2010 21:52:38 +0200
parents 2e8194a30f40
children f4960f8ce798
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@552 204 TimeInstantLayer::getLabelPreceding(size_t frame) const
Chris@552 205 {
Chris@552 206 if (!m_model) return "";
Chris@552 207 SparseOneDimensionalModel::PointList points = m_model->getPreviousPoints(frame);
Chris@552 208 for (SparseOneDimensionalModel::PointList::const_iterator i = points.begin();
Chris@552 209 i != points.end(); ++i) {
Chris@552 210 if (i->label != "") return i->label;
Chris@552 211 }
Chris@552 212 return "";
Chris@552 213 }
Chris@552 214
Chris@552 215 QString
Chris@44 216 TimeInstantLayer::getFeatureDescription(View *v, QPoint &pos) const
Chris@0 217 {
Chris@25 218 int x = pos.x();
Chris@0 219
Chris@25 220 if (!m_model || !m_model->getSampleRate()) return "";
Chris@0 221
Chris@44 222 SparseOneDimensionalModel::PointList points = getLocalPoints(v, x);
Chris@0 223
Chris@0 224 if (points.empty()) {
Chris@0 225 if (!m_model->isReady()) {
Chris@25 226 return tr("In progress");
Chris@25 227 } else {
Chris@25 228 return tr("No local points");
Chris@0 229 }
Chris@0 230 }
Chris@0 231
Chris@0 232 long useFrame = points.begin()->frame;
Chris@0 233
Chris@0 234 RealTime rt = RealTime::frame2RealTime(useFrame, m_model->getSampleRate());
Chris@25 235
Chris@25 236 QString text;
Chris@0 237
Chris@25 238 if (points.begin()->label == "") {
Chris@25 239 text = QString(tr("Time:\t%1\nNo label"))
Chris@25 240 .arg(rt.toText(true).c_str());
Chris@25 241 } else {
Chris@25 242 text = QString(tr("Time:\t%1\nLabel:\t%2"))
Chris@25 243 .arg(rt.toText(true).c_str())
Chris@25 244 .arg(points.begin()->label);
Chris@25 245 }
Chris@0 246
Chris@44 247 pos = QPoint(v->getXForFrame(useFrame), pos.y());
Chris@25 248 return text;
Chris@0 249 }
Chris@0 250
Chris@28 251 bool
Chris@44 252 TimeInstantLayer::snapToFeatureFrame(View *v, int &frame,
Chris@28 253 size_t &resolution,
Chris@28 254 SnapType snap) const
Chris@13 255 {
Chris@13 256 if (!m_model) {
Chris@44 257 return Layer::snapToFeatureFrame(v, frame, resolution, snap);
Chris@13 258 }
Chris@13 259
Chris@13 260 resolution = m_model->getResolution();
Chris@28 261 SparseOneDimensionalModel::PointList points;
Chris@13 262
Chris@28 263 if (snap == SnapNeighbouring) {
Chris@28 264
Chris@44 265 points = getLocalPoints(v, v->getXForFrame(frame));
Chris@28 266 if (points.empty()) return false;
Chris@28 267 frame = points.begin()->frame;
Chris@28 268 return true;
Chris@28 269 }
Chris@28 270
Chris@28 271 points = m_model->getPoints(frame, frame);
Chris@28 272 int snapped = frame;
Chris@28 273 bool found = false;
Chris@13 274
Chris@13 275 for (SparseOneDimensionalModel::PointList::const_iterator i = points.begin();
Chris@13 276 i != points.end(); ++i) {
Chris@13 277
Chris@28 278 if (snap == SnapRight) {
Chris@28 279
Chris@28 280 if (i->frame >= frame) {
Chris@28 281 snapped = i->frame;
Chris@28 282 found = true;
Chris@13 283 break;
Chris@13 284 }
Chris@28 285
Chris@28 286 } else if (snap == SnapLeft) {
Chris@28 287
Chris@13 288 if (i->frame <= frame) {
Chris@28 289 snapped = i->frame;
Chris@28 290 found = true; // don't break, as the next may be better
Chris@28 291 } else {
Chris@28 292 break;
Chris@28 293 }
Chris@28 294
Chris@28 295 } else { // nearest
Chris@28 296
Chris@28 297 SparseOneDimensionalModel::PointList::const_iterator j = i;
Chris@28 298 ++j;
Chris@28 299
Chris@28 300 if (j == points.end()) {
Chris@28 301
Chris@28 302 snapped = i->frame;
Chris@28 303 found = true;
Chris@28 304 break;
Chris@28 305
Chris@28 306 } else if (j->frame >= frame) {
Chris@28 307
Chris@28 308 if (j->frame - frame < frame - i->frame) {
Chris@28 309 snapped = j->frame;
Chris@28 310 } else {
Chris@28 311 snapped = i->frame;
Chris@28 312 }
Chris@28 313 found = true;
Chris@28 314 break;
Chris@13 315 }
Chris@13 316 }
Chris@13 317 }
Chris@13 318
Chris@28 319 frame = snapped;
Chris@28 320 return found;
Chris@13 321 }
Chris@13 322
Chris@0 323 void
Chris@44 324 TimeInstantLayer::paint(View *v, QPainter &paint, QRect rect) const
Chris@0 325 {
Chris@0 326 if (!m_model || !m_model->isOK()) return;
Chris@0 327
Chris@0 328 // Profiler profiler("TimeInstantLayer::paint", true);
Chris@0 329
Chris@20 330 int x0 = rect.left(), x1 = rect.right();
Chris@0 331
Chris@44 332 long frame0 = v->getFrameForX(x0);
Chris@44 333 long frame1 = v->getFrameForX(x1);
Chris@0 334
Chris@0 335 SparseOneDimensionalModel::PointList points(m_model->getPoints
Chris@0 336 (frame0, frame1));
Chris@0 337
Chris@28 338 bool odd = false;
Chris@28 339 if (m_plotStyle == PlotSegmentation && !points.empty()) {
Chris@28 340 int index = m_model->getIndexOf(*points.begin());
Chris@28 341 odd = ((index % 2) == 1);
Chris@28 342 }
Chris@28 343
Chris@287 344 paint.setPen(getBaseQColor());
Chris@0 345
Chris@287 346 QColor brushColour(getBaseQColor());
Chris@0 347 brushColour.setAlpha(100);
Chris@0 348 paint.setBrush(brushColour);
Chris@0 349
Chris@28 350 QColor oddBrushColour(brushColour);
Chris@28 351 if (m_plotStyle == PlotSegmentation) {
Chris@287 352 if (getBaseQColor() == Qt::black) {
Chris@28 353 oddBrushColour = Qt::gray;
Chris@287 354 } else if (getBaseQColor() == Qt::darkRed) {
Chris@28 355 oddBrushColour = Qt::red;
Chris@287 356 } else if (getBaseQColor() == Qt::darkBlue) {
Chris@28 357 oddBrushColour = Qt::blue;
Chris@287 358 } else if (getBaseQColor() == Qt::darkGreen) {
Chris@28 359 oddBrushColour = Qt::green;
Chris@28 360 } else {
Chris@28 361 oddBrushColour = oddBrushColour.light(150);
Chris@28 362 }
Chris@28 363 oddBrushColour.setAlpha(100);
Chris@28 364 }
Chris@28 365
Chris@0 366 // std::cerr << "TimeInstantLayer::paint: resolution is "
Chris@0 367 // << m_model->getResolution() << " frames" << std::endl;
Chris@0 368
Chris@0 369 QPoint localPos;
Chris@0 370 long illuminateFrame = -1;
Chris@0 371
Chris@44 372 if (v->shouldIlluminateLocalFeatures(this, localPos)) {
Chris@0 373 SparseOneDimensionalModel::PointList localPoints =
Chris@44 374 getLocalPoints(v, localPos.x());
Chris@0 375 if (!localPoints.empty()) illuminateFrame = localPoints.begin()->frame;
Chris@0 376 }
Chris@0 377
Chris@23 378 int prevX = -1;
Chris@79 379 int textY = v->getTextLabelHeight(this, paint);
Chris@79 380
Chris@0 381 for (SparseOneDimensionalModel::PointList::const_iterator i = points.begin();
Chris@0 382 i != points.end(); ++i) {
Chris@0 383
Chris@0 384 const SparseOneDimensionalModel::Point &p(*i);
Chris@17 385 SparseOneDimensionalModel::PointList::const_iterator j = i;
Chris@17 386 ++j;
Chris@0 387
Chris@44 388 int x = v->getXForFrame(p.frame);
Chris@576 389 if (x == prevX && m_plotStyle == PlotInstants &&
Chris@576 390 p.frame != illuminateFrame) continue;
Chris@23 391
Chris@44 392 int iw = v->getXForFrame(p.frame + m_model->getResolution()) - x;
Chris@16 393 if (iw < 2) {
Chris@17 394 if (iw < 1) {
Chris@17 395 iw = 2;
Chris@17 396 if (j != points.end()) {
Chris@44 397 int nx = v->getXForFrame(j->frame);
Chris@17 398 if (nx < x + 3) iw = 1;
Chris@17 399 }
Chris@17 400 } else {
Chris@17 401 iw = 2;
Chris@17 402 }
Chris@16 403 }
Chris@20 404
Chris@0 405 if (p.frame == illuminateFrame) {
Chris@287 406 paint.setPen(getForegroundQColor(v));
Chris@0 407 } else {
Chris@0 408 paint.setPen(brushColour);
Chris@0 409 }
Chris@23 410
Chris@28 411 if (m_plotStyle == PlotInstants) {
Chris@28 412 if (iw > 1) {
Chris@44 413 paint.drawRect(x, 0, iw - 1, v->height() - 1);
Chris@28 414 } else {
Chris@44 415 paint.drawLine(x, 0, x, v->height() - 1);
Chris@28 416 }
Chris@23 417 } else {
Chris@28 418
Chris@28 419 if (odd) paint.setBrush(oddBrushColour);
Chris@28 420 else paint.setBrush(brushColour);
Chris@28 421
Chris@28 422 int nx;
Chris@28 423
Chris@28 424 if (j != points.end()) {
Chris@28 425 const SparseOneDimensionalModel::Point &q(*j);
Chris@44 426 nx = v->getXForFrame(q.frame);
Chris@28 427 } else {
Chris@44 428 nx = v->getXForFrame(m_model->getEndFrame());
Chris@28 429 }
Chris@28 430
Chris@28 431 if (nx >= x) {
Chris@28 432
Chris@28 433 if (illuminateFrame != p.frame &&
Chris@44 434 (nx < x + 5 || x >= v->width() - 1)) {
Chris@28 435 paint.setPen(Qt::NoPen);
Chris@28 436 }
Chris@28 437
Chris@576 438 paint.drawRect(x, -1, nx - x, v->height() + 1);
Chris@28 439 }
Chris@28 440
Chris@28 441 odd = !odd;
Chris@23 442 }
Chris@28 443
Chris@287 444 paint.setPen(getBaseQColor());
Chris@0 445
Chris@0 446 if (p.label != "") {
Chris@0 447
Chris@0 448 // only draw if there's enough room from here to the next point
Chris@0 449
Chris@0 450 int lw = paint.fontMetrics().width(p.label);
Chris@0 451 bool good = true;
Chris@0 452
Chris@17 453 if (j != points.end()) {
Chris@44 454 int nx = v->getXForFrame(j->frame);
Chris@20 455 if (nx >= x && nx - x - iw - 3 <= lw) good = false;
Chris@0 456 }
Chris@0 457
Chris@0 458 if (good) {
Chris@576 459 v->drawVisibleText(paint, x + iw + 2, textY, p.label, View::OutlinedText);
Chris@576 460 // paint.drawText(x + iw + 2, textY, p.label);
Chris@0 461 }
Chris@0 462 }
Chris@23 463
Chris@23 464 prevX = x;
Chris@0 465 }
Chris@0 466 }
Chris@0 467
Chris@17 468 void
Chris@44 469 TimeInstantLayer::drawStart(View *v, QMouseEvent *e)
Chris@17 470 {
Chris@387 471 #ifdef DEBUG_TIME_INSTANT_LAYER
Chris@17 472 std::cerr << "TimeInstantLayer::drawStart(" << e->x() << ")" << std::endl;
Chris@387 473 #endif
Chris@17 474
Chris@17 475 if (!m_model) return;
Chris@17 476
Chris@44 477 long frame = v->getFrameForX(e->x());
Chris@17 478 if (frame < 0) frame = 0;
Chris@21 479 frame = frame / m_model->getResolution() * m_model->getResolution();
Chris@22 480
Chris@17 481 m_editingPoint = SparseOneDimensionalModel::Point(frame, tr("New Point"));
Chris@22 482
Chris@376 483 if (m_editingCommand) finish(m_editingCommand);
Chris@22 484 m_editingCommand = new SparseOneDimensionalModel::EditCommand(m_model,
Chris@22 485 tr("Draw Point"));
Chris@22 486 m_editingCommand->addPoint(m_editingPoint);
Chris@22 487
Chris@18 488 m_editing = true;
Chris@17 489 }
Chris@17 490
Chris@17 491 void
Chris@44 492 TimeInstantLayer::drawDrag(View *v, QMouseEvent *e)
Chris@17 493 {
Chris@387 494 #ifdef DEBUG_TIME_INSTANT_LAYER
Chris@17 495 std::cerr << "TimeInstantLayer::drawDrag(" << e->x() << ")" << std::endl;
Chris@387 496 #endif
Chris@17 497
Chris@18 498 if (!m_model || !m_editing) return;
Chris@17 499
Chris@44 500 long frame = v->getFrameForX(e->x());
Chris@17 501 if (frame < 0) frame = 0;
Chris@21 502 frame = frame / m_model->getResolution() * m_model->getResolution();
Chris@22 503 m_editingCommand->deletePoint(m_editingPoint);
Chris@17 504 m_editingPoint.frame = frame;
Chris@22 505 m_editingCommand->addPoint(m_editingPoint);
Chris@17 506 }
Chris@17 507
Chris@17 508 void
Chris@248 509 TimeInstantLayer::drawEnd(View *, QMouseEvent *e)
Chris@17 510 {
Chris@387 511 #ifdef DEBUG_TIME_INSTANT_LAYER
Chris@17 512 std::cerr << "TimeInstantLayer::drawEnd(" << e->x() << ")" << std::endl;
Chris@387 513 #endif
Chris@18 514 if (!m_model || !m_editing) return;
Chris@23 515 QString newName = tr("Add Point at %1 s")
Chris@23 516 .arg(RealTime::frame2RealTime(m_editingPoint.frame,
Chris@23 517 m_model->getSampleRate())
Chris@23 518 .toText(false).c_str());
Chris@23 519 m_editingCommand->setName(newName);
Chris@376 520 finish(m_editingCommand);
Chris@22 521 m_editingCommand = 0;
Chris@18 522 m_editing = false;
Chris@18 523 }
Chris@18 524
Chris@18 525 void
Chris@335 526 TimeInstantLayer::eraseStart(View *v, QMouseEvent *e)
Chris@335 527 {
Chris@335 528 if (!m_model) return;
Chris@335 529
Chris@335 530 SparseOneDimensionalModel::PointList points = getLocalPoints(v, e->x());
Chris@335 531 if (points.empty()) return;
Chris@335 532
Chris@335 533 m_editingPoint = *points.begin();
Chris@335 534
Chris@335 535 if (m_editingCommand) {
Chris@376 536 finish(m_editingCommand);
Chris@335 537 m_editingCommand = 0;
Chris@335 538 }
Chris@335 539
Chris@335 540 m_editing = true;
Chris@335 541 }
Chris@335 542
Chris@335 543 void
Chris@335 544 TimeInstantLayer::eraseDrag(View *v, QMouseEvent *e)
Chris@335 545 {
Chris@335 546 }
Chris@335 547
Chris@335 548 void
Chris@335 549 TimeInstantLayer::eraseEnd(View *v, QMouseEvent *e)
Chris@335 550 {
Chris@335 551 if (!m_model || !m_editing) return;
Chris@335 552
Chris@335 553 m_editing = false;
Chris@335 554
Chris@335 555 SparseOneDimensionalModel::PointList points = getLocalPoints(v, e->x());
Chris@335 556 if (points.empty()) return;
Chris@335 557 if (points.begin()->frame != m_editingPoint.frame) return;
Chris@335 558
Chris@335 559 m_editingCommand = new SparseOneDimensionalModel::EditCommand
Chris@335 560 (m_model, tr("Erase Point"));
Chris@335 561
Chris@335 562 m_editingCommand->deletePoint(m_editingPoint);
Chris@335 563
Chris@376 564 finish(m_editingCommand);
Chris@335 565 m_editingCommand = 0;
Chris@335 566 m_editing = false;
Chris@335 567 }
Chris@335 568
Chris@335 569 void
Chris@44 570 TimeInstantLayer::editStart(View *v, QMouseEvent *e)
Chris@18 571 {
Chris@387 572 #ifdef DEBUG_TIME_INSTANT_LAYER
Chris@18 573 std::cerr << "TimeInstantLayer::editStart(" << e->x() << ")" << std::endl;
Chris@387 574 #endif
Chris@18 575
Chris@17 576 if (!m_model) return;
Chris@18 577
Chris@44 578 SparseOneDimensionalModel::PointList points = getLocalPoints(v, e->x());
Chris@18 579 if (points.empty()) return;
Chris@18 580
Chris@18 581 m_editingPoint = *points.begin();
Chris@22 582
Chris@22 583 if (m_editingCommand) {
Chris@376 584 finish(m_editingCommand);
Chris@22 585 m_editingCommand = 0;
Chris@22 586 }
Chris@22 587
Chris@18 588 m_editing = true;
Chris@18 589 }
Chris@18 590
Chris@18 591 void
Chris@44 592 TimeInstantLayer::editDrag(View *v, QMouseEvent *e)
Chris@18 593 {
Chris@387 594 #ifdef DEBUG_TIME_INSTANT_LAYER
Chris@18 595 std::cerr << "TimeInstantLayer::editDrag(" << e->x() << ")" << std::endl;
Chris@387 596 #endif
Chris@18 597
Chris@18 598 if (!m_model || !m_editing) return;
Chris@18 599
Chris@44 600 long frame = v->getFrameForX(e->x());
Chris@18 601 if (frame < 0) frame = 0;
Chris@21 602 frame = frame / m_model->getResolution() * m_model->getResolution();
Chris@22 603
Chris@22 604 if (!m_editingCommand) {
Chris@22 605 m_editingCommand = new SparseOneDimensionalModel::EditCommand(m_model,
Chris@22 606 tr("Drag Point"));
Chris@22 607 }
Chris@22 608
Chris@22 609 m_editingCommand->deletePoint(m_editingPoint);
Chris@18 610 m_editingPoint.frame = frame;
Chris@22 611 m_editingCommand->addPoint(m_editingPoint);
Chris@18 612 }
Chris@18 613
Chris@18 614 void
Chris@248 615 TimeInstantLayer::editEnd(View *, QMouseEvent *e)
Chris@18 616 {
Chris@387 617 #ifdef DEBUG_TIME_INSTANT_LAYER
Chris@18 618 std::cerr << "TimeInstantLayer::editEnd(" << e->x() << ")" << std::endl;
Chris@387 619 #endif
Chris@18 620 if (!m_model || !m_editing) return;
Chris@23 621 if (m_editingCommand) {
Chris@23 622 QString newName = tr("Move Point to %1 s")
Chris@23 623 .arg(RealTime::frame2RealTime(m_editingPoint.frame,
Chris@23 624 m_model->getSampleRate())
Chris@23 625 .toText(false).c_str());
Chris@23 626 m_editingCommand->setName(newName);
Chris@376 627 finish(m_editingCommand);
Chris@23 628 }
Chris@22 629 m_editingCommand = 0;
Chris@18 630 m_editing = false;
Chris@17 631 }
Chris@17 632
Chris@255 633 bool
Chris@70 634 TimeInstantLayer::editOpen(View *v, QMouseEvent *e)
Chris@70 635 {
Chris@255 636 if (!m_model) return false;
Chris@70 637
Chris@70 638 SparseOneDimensionalModel::PointList points = getLocalPoints(v, e->x());
Chris@255 639 if (points.empty()) return false;
Chris@70 640
Chris@70 641 SparseOneDimensionalModel::Point point = *points.begin();
Chris@70 642
Chris@70 643 ItemEditDialog *dialog = new ItemEditDialog
Chris@70 644 (m_model->getSampleRate(),
Chris@70 645 ItemEditDialog::ShowTime |
Chris@70 646 ItemEditDialog::ShowText);
Chris@70 647
Chris@70 648 dialog->setFrameTime(point.frame);
Chris@70 649 dialog->setText(point.label);
Chris@70 650
Chris@70 651 if (dialog->exec() == QDialog::Accepted) {
Chris@70 652
Chris@70 653 SparseOneDimensionalModel::Point newPoint = point;
Chris@70 654 newPoint.frame = dialog->getFrameTime();
Chris@70 655 newPoint.label = dialog->getText();
Chris@70 656
Chris@70 657 SparseOneDimensionalModel::EditCommand *command =
Chris@70 658 new SparseOneDimensionalModel::EditCommand(m_model, tr("Edit Point"));
Chris@70 659 command->deletePoint(point);
Chris@70 660 command->addPoint(newPoint);
Chris@376 661 finish(command);
Chris@70 662 }
Chris@70 663
Chris@70 664 delete dialog;
Chris@255 665 return true;
Chris@70 666 }
Chris@70 667
Chris@70 668 void
Chris@43 669 TimeInstantLayer::moveSelection(Selection s, size_t newStartFrame)
Chris@43 670 {
Chris@99 671 if (!m_model) return;
Chris@99 672
Chris@43 673 SparseOneDimensionalModel::EditCommand *command =
Chris@43 674 new SparseOneDimensionalModel::EditCommand(m_model,
Chris@43 675 tr("Drag Selection"));
Chris@43 676
Chris@43 677 SparseOneDimensionalModel::PointList points =
Chris@43 678 m_model->getPoints(s.getStartFrame(), s.getEndFrame());
Chris@43 679
Chris@43 680 for (SparseOneDimensionalModel::PointList::iterator i = points.begin();
Chris@43 681 i != points.end(); ++i) {
Chris@43 682
Chris@43 683 if (s.contains(i->frame)) {
Chris@43 684 SparseOneDimensionalModel::Point newPoint(*i);
Chris@43 685 newPoint.frame = i->frame + newStartFrame - s.getStartFrame();
Chris@43 686 command->deletePoint(*i);
Chris@43 687 command->addPoint(newPoint);
Chris@43 688 }
Chris@43 689 }
Chris@43 690
Chris@376 691 finish(command);
Chris@43 692 }
Chris@43 693
Chris@43 694 void
Chris@43 695 TimeInstantLayer::resizeSelection(Selection s, Selection newSize)
Chris@43 696 {
Chris@99 697 if (!m_model) return;
Chris@99 698
Chris@43 699 SparseOneDimensionalModel::EditCommand *command =
Chris@43 700 new SparseOneDimensionalModel::EditCommand(m_model,
Chris@43 701 tr("Resize Selection"));
Chris@43 702
Chris@43 703 SparseOneDimensionalModel::PointList points =
Chris@43 704 m_model->getPoints(s.getStartFrame(), s.getEndFrame());
Chris@43 705
Chris@43 706 double ratio =
Chris@43 707 double(newSize.getEndFrame() - newSize.getStartFrame()) /
Chris@43 708 double(s.getEndFrame() - s.getStartFrame());
Chris@43 709
Chris@43 710 for (SparseOneDimensionalModel::PointList::iterator i = points.begin();
Chris@43 711 i != points.end(); ++i) {
Chris@43 712
Chris@43 713 if (s.contains(i->frame)) {
Chris@43 714
Chris@43 715 double target = i->frame;
Chris@43 716 target = newSize.getStartFrame() +
Chris@43 717 double(target - s.getStartFrame()) * ratio;
Chris@43 718
Chris@43 719 SparseOneDimensionalModel::Point newPoint(*i);
Chris@43 720 newPoint.frame = lrint(target);
Chris@43 721 command->deletePoint(*i);
Chris@43 722 command->addPoint(newPoint);
Chris@43 723 }
Chris@43 724 }
Chris@43 725
Chris@376 726 finish(command);
Chris@43 727 }
Chris@43 728
Chris@43 729 void
Chris@43 730 TimeInstantLayer::deleteSelection(Selection s)
Chris@43 731 {
Chris@99 732 if (!m_model) return;
Chris@99 733
Chris@43 734 SparseOneDimensionalModel::EditCommand *command =
Chris@43 735 new SparseOneDimensionalModel::EditCommand(m_model,
Chris@43 736 tr("Delete Selection"));
Chris@43 737
Chris@43 738 SparseOneDimensionalModel::PointList points =
Chris@43 739 m_model->getPoints(s.getStartFrame(), s.getEndFrame());
Chris@43 740
Chris@43 741 for (SparseOneDimensionalModel::PointList::iterator i = points.begin();
Chris@43 742 i != points.end(); ++i) {
Chris@43 743 if (s.contains(i->frame)) command->deletePoint(*i);
Chris@43 744 }
Chris@43 745
Chris@376 746 finish(command);
Chris@43 747 }
Chris@76 748
Chris@76 749 void
Chris@359 750 TimeInstantLayer::copy(View *v, Selection s, Clipboard &to)
Chris@76 751 {
Chris@99 752 if (!m_model) return;
Chris@99 753
Chris@76 754 SparseOneDimensionalModel::PointList points =
Chris@76 755 m_model->getPoints(s.getStartFrame(), s.getEndFrame());
Chris@76 756
Chris@76 757 for (SparseOneDimensionalModel::PointList::iterator i = points.begin();
Chris@76 758 i != points.end(); ++i) {
Chris@76 759 if (s.contains(i->frame)) {
Chris@76 760 Clipboard::Point point(i->frame, i->label);
Chris@359 761 point.setReferenceFrame(alignToReference(v, i->frame));
Chris@76 762 to.addPoint(point);
Chris@76 763 }
Chris@76 764 }
Chris@76 765 }
Chris@76 766
Chris@125 767 bool
Chris@359 768 TimeInstantLayer::paste(View *v, const Clipboard &from, int frameOffset, bool)
Chris@76 769 {
Chris@125 770 if (!m_model) return false;
Chris@99 771
Chris@76 772 const Clipboard::PointList &points = from.getPoints();
Chris@76 773
Chris@358 774 bool realign = false;
Chris@358 775
Chris@360 776 if (clipboardHasDifferentAlignment(v, from)) {
Chris@358 777
Chris@360 778 QMessageBox::StandardButton button =
Chris@360 779 QMessageBox::question(v, tr("Re-align pasted instants?"),
Chris@360 780 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 781 QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel,
Chris@360 782 QMessageBox::Yes);
Chris@358 783
Chris@360 784 if (button == QMessageBox::Cancel) {
Chris@360 785 return false;
Chris@360 786 }
Chris@358 787
Chris@360 788 if (button == QMessageBox::Yes) {
Chris@360 789 realign = true;
Chris@360 790 }
Chris@358 791 }
Chris@358 792
Chris@358 793 SparseOneDimensionalModel::EditCommand *command =
Chris@358 794 new SparseOneDimensionalModel::EditCommand(m_model, tr("Paste"));
Chris@358 795
Chris@76 796 for (Clipboard::PointList::const_iterator i = points.begin();
Chris@76 797 i != points.end(); ++i) {
Chris@76 798
Chris@76 799 if (!i->haveFrame()) continue;
Chris@359 800
Chris@76 801 size_t frame = 0;
Chris@359 802
Chris@359 803 if (!realign) {
Chris@359 804
Chris@359 805 frame = i->getFrame();
Chris@359 806
Chris@359 807 } else {
Chris@359 808
Chris@359 809 if (i->haveReferenceFrame()) {
Chris@359 810 frame = i->getReferenceFrame();
Chris@359 811 frame = alignFromReference(v, frame);
Chris@359 812 } else {
Chris@359 813 frame = i->getFrame();
Chris@359 814 }
Chris@76 815 }
Chris@359 816
Chris@359 817 if (frameOffset > 0) frame += frameOffset;
Chris@359 818 else if (frameOffset < 0) {
Chris@359 819 if (frame > -frameOffset) frame += frameOffset;
Chris@359 820 else frame = 0;
Chris@359 821 }
Chris@359 822
Chris@76 823 SparseOneDimensionalModel::Point newPoint(frame);
Chris@125 824 if (i->haveLabel()) {
Chris@125 825 newPoint.label = i->getLabel();
Chris@125 826 } else if (i->haveValue()) {
Chris@125 827 newPoint.label = QString("%1").arg(i->getValue());
Chris@125 828 }
Chris@76 829
Chris@76 830 command->addPoint(newPoint);
Chris@76 831 }
Chris@76 832
Chris@376 833 finish(command);
Chris@125 834 return true;
Chris@76 835 }
Chris@43 836
Chris@287 837 int
Chris@287 838 TimeInstantLayer::getDefaultColourHint(bool darkbg, bool &impose)
Chris@287 839 {
Chris@287 840 impose = false;
Chris@287 841 return ColourDatabase::getInstance()->getColourIndex
Chris@287 842 (QString(darkbg ? "Bright Purple" : "Purple"));
Chris@287 843 }
Chris@287 844
Chris@316 845 void
Chris@316 846 TimeInstantLayer::toXml(QTextStream &stream,
Chris@316 847 QString indent, QString extraAttributes) const
Chris@6 848 {
Chris@316 849 SingleColourLayer::toXml(stream, indent,
Chris@316 850 extraAttributes +
Chris@316 851 QString(" plotStyle=\"%1\"")
Chris@316 852 .arg(m_plotStyle));
Chris@6 853 }
Chris@0 854
Chris@11 855 void
Chris@11 856 TimeInstantLayer::setProperties(const QXmlAttributes &attributes)
Chris@11 857 {
Chris@287 858 SingleColourLayer::setProperties(attributes);
Chris@28 859
Chris@28 860 bool ok;
Chris@28 861 PlotStyle style = (PlotStyle)
Chris@28 862 attributes.value("plotStyle").toInt(&ok);
Chris@28 863 if (ok) setPlotStyle(style);
Chris@11 864 }
Chris@11 865