annotate layer/TimeInstantLayer.cpp @ 454:e2a40fdadd8c

Various fixes: * Fix handling of HTTP redirects (avoiding crashes... I hope) * Fix failure to delete FFT models when a feature extraction model transformer was abandoned (also a cause of crashes in the past) * Fix deadlock when said transform was abandoned before its source model was ready because the session was being cleared (and so the source model would never be ready)
author Chris Cannam
date Fri, 28 Nov 2008 13:36:13 +0000
parents 427e5c58658e
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