annotate layer/TimeInstantLayer.cpp @ 429:427e5c58658e

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