annotate layer/TimeInstantLayer.cpp @ 357:3e538a90e9b8

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