annotate layer/TimeInstantLayer.cpp @ 349:369a197737c7

* Various fixes to object lifetime management, particularly in the spectrum layer and for notification of main model deletion. The main purpose of this is to improve the behaviour of the spectrum, but I think it may also help with #1840922 Various crashes in Layer Summary window.
author Chris Cannam
date Wed, 23 Jan 2008 15:43:27 +0000
parents 2f83b6e3b8ca
children bff85425228c 0895517bb2d1
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@76 754 for (Clipboard::PointList::const_iterator i = points.begin();
Chris@76 755 i != points.end(); ++i) {
Chris@76 756
Chris@76 757 if (!i->haveFrame()) continue;
Chris@76 758 size_t frame = 0;
Chris@76 759 if (frameOffset > 0 || -frameOffset < i->getFrame()) {
Chris@76 760 frame = i->getFrame() + frameOffset;
Chris@76 761 }
Chris@76 762 SparseOneDimensionalModel::Point newPoint(frame);
Chris@125 763 if (i->haveLabel()) {
Chris@125 764 newPoint.label = i->getLabel();
Chris@125 765 } else if (i->haveValue()) {
Chris@125 766 newPoint.label = QString("%1").arg(i->getValue());
Chris@125 767 }
Chris@76 768
Chris@76 769 command->addPoint(newPoint);
Chris@76 770 }
Chris@76 771
Chris@76 772 command->finish();
Chris@125 773 return true;
Chris@76 774 }
Chris@43 775
Chris@287 776 int
Chris@287 777 TimeInstantLayer::getDefaultColourHint(bool darkbg, bool &impose)
Chris@287 778 {
Chris@287 779 impose = false;
Chris@287 780 return ColourDatabase::getInstance()->getColourIndex
Chris@287 781 (QString(darkbg ? "Bright Purple" : "Purple"));
Chris@287 782 }
Chris@287 783
Chris@316 784 void
Chris@316 785 TimeInstantLayer::toXml(QTextStream &stream,
Chris@316 786 QString indent, QString extraAttributes) const
Chris@6 787 {
Chris@316 788 SingleColourLayer::toXml(stream, indent,
Chris@316 789 extraAttributes +
Chris@316 790 QString(" plotStyle=\"%1\"")
Chris@316 791 .arg(m_plotStyle));
Chris@6 792 }
Chris@0 793
Chris@11 794 void
Chris@11 795 TimeInstantLayer::setProperties(const QXmlAttributes &attributes)
Chris@11 796 {
Chris@287 797 SingleColourLayer::setProperties(attributes);
Chris@28 798
Chris@28 799 bool ok;
Chris@28 800 PlotStyle style = (PlotStyle)
Chris@28 801 attributes.value("plotStyle").toInt(&ok);
Chris@28 802 if (ok) setPlotStyle(style);
Chris@11 803 }
Chris@11 804