annotate layer/RegionLayer.cpp @ 414:fc19435ac0f5

* Improve snap policy for selecting regions
author Chris Cannam
date Mon, 22 Sep 2008 12:20:34 +0000
parents f71752646f97
children 31f222929319
rev   line source
Chris@411 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@411 2
Chris@411 3 /*
Chris@411 4 Sonic Visualiser
Chris@411 5 An audio file viewer and annotation editor.
Chris@411 6 Centre for Digital Music, Queen Mary, University of London.
Chris@411 7 This file copyright 2006-2008 Chris Cannam and QMUL.
Chris@411 8
Chris@411 9 This program is free software; you can redistribute it and/or
Chris@411 10 modify it under the terms of the GNU General Public License as
Chris@411 11 published by the Free Software Foundation; either version 2 of the
Chris@411 12 License, or (at your option) any later version. See the file
Chris@411 13 COPYING included with this distribution for more information.
Chris@411 14 */
Chris@411 15
Chris@411 16 #include "RegionLayer.h"
Chris@411 17
Chris@411 18 #include "data/model/Model.h"
Chris@411 19 #include "base/RealTime.h"
Chris@411 20 #include "base/Profiler.h"
Chris@411 21 #include "base/LogRange.h"
Chris@411 22 #include "ColourDatabase.h"
Chris@411 23 #include "view/View.h"
Chris@411 24
Chris@411 25 #include "data/model/RegionModel.h"
Chris@411 26
Chris@411 27 #include "widgets/ItemEditDialog.h"
Chris@411 28
Chris@411 29 #include "SpectrogramLayer.h" // for optional frequency alignment
Chris@411 30
Chris@411 31 #include <QPainter>
Chris@411 32 #include <QPainterPath>
Chris@411 33 #include <QMouseEvent>
Chris@411 34 #include <QTextStream>
Chris@411 35 #include <QMessageBox>
Chris@411 36
Chris@411 37 #include <iostream>
Chris@411 38 #include <cmath>
Chris@411 39
Chris@411 40 RegionLayer::RegionLayer() :
Chris@411 41 SingleColourLayer(),
Chris@411 42 m_model(0),
Chris@411 43 m_editing(false),
Chris@411 44 m_originalPoint(0, 0.0, 0, tr("New Point")),
Chris@411 45 m_editingPoint(0, 0.0, 0, tr("New Point")),
Chris@411 46 m_editingCommand(0),
Chris@412 47 m_verticalScale(AutoAlignScale),
Chris@412 48 m_plotStyle(PlotLines)
Chris@411 49 {
Chris@411 50
Chris@411 51 }
Chris@411 52
Chris@411 53 void
Chris@411 54 RegionLayer::setModel(RegionModel *model)
Chris@411 55 {
Chris@411 56 if (m_model == model) return;
Chris@411 57 m_model = model;
Chris@411 58
Chris@411 59 connectSignals(m_model);
Chris@411 60
Chris@411 61 // std::cerr << "RegionLayer::setModel(" << model << ")" << std::endl;
Chris@411 62
Chris@411 63 emit modelReplaced();
Chris@411 64 }
Chris@411 65
Chris@411 66 Layer::PropertyList
Chris@411 67 RegionLayer::getProperties() const
Chris@411 68 {
Chris@411 69 PropertyList list = SingleColourLayer::getProperties();
Chris@411 70 list.push_back("Vertical Scale");
Chris@411 71 list.push_back("Scale Units");
Chris@412 72 list.push_back("Plot Type");
Chris@411 73 return list;
Chris@411 74 }
Chris@411 75
Chris@411 76 QString
Chris@411 77 RegionLayer::getPropertyLabel(const PropertyName &name) const
Chris@411 78 {
Chris@411 79 if (name == "Vertical Scale") return tr("Vertical Scale");
Chris@411 80 if (name == "Scale Units") return tr("Scale Units");
Chris@412 81 if (name == "Plot Type") return tr("Plot Type");
Chris@411 82 return SingleColourLayer::getPropertyLabel(name);
Chris@411 83 }
Chris@411 84
Chris@411 85 Layer::PropertyType
Chris@411 86 RegionLayer::getPropertyType(const PropertyName &name) const
Chris@411 87 {
Chris@411 88 if (name == "Scale Units") return UnitsProperty;
Chris@411 89 if (name == "Vertical Scale") return ValueProperty;
Chris@412 90 if (name == "Plot Type") return ValueProperty;
Chris@411 91 return SingleColourLayer::getPropertyType(name);
Chris@411 92 }
Chris@411 93
Chris@411 94 QString
Chris@411 95 RegionLayer::getPropertyGroupName(const PropertyName &name) const
Chris@411 96 {
Chris@411 97 if (name == "Vertical Scale" || name == "Scale Units") {
Chris@411 98 return tr("Scale");
Chris@411 99 }
Chris@411 100 return SingleColourLayer::getPropertyGroupName(name);
Chris@411 101 }
Chris@411 102
Chris@411 103 int
Chris@411 104 RegionLayer::getPropertyRangeAndValue(const PropertyName &name,
Chris@411 105 int *min, int *max, int *deflt) const
Chris@411 106 {
Chris@411 107 int val = 0;
Chris@411 108
Chris@412 109 if (name == "Plot Type") {
Chris@412 110
Chris@412 111 if (min) *min = 0;
Chris@412 112 if (max) *max = 1;
Chris@412 113 if (deflt) *deflt = 0;
Chris@412 114
Chris@412 115 val = int(m_plotStyle);
Chris@412 116
Chris@412 117 } else if (name == "Vertical Scale") {
Chris@411 118
Chris@411 119 if (min) *min = 0;
Chris@411 120 if (max) *max = 3;
Chris@411 121 if (deflt) *deflt = int(AutoAlignScale);
Chris@411 122
Chris@411 123 val = int(m_verticalScale);
Chris@411 124
Chris@411 125 } else if (name == "Scale Units") {
Chris@411 126
Chris@411 127 if (deflt) *deflt = 0;
Chris@411 128 if (m_model) {
Chris@411 129 val = UnitDatabase::getInstance()->getUnitId
Chris@411 130 (m_model->getScaleUnits());
Chris@411 131 }
Chris@411 132
Chris@411 133 } else {
Chris@411 134
Chris@411 135 val = SingleColourLayer::getPropertyRangeAndValue(name, min, max, deflt);
Chris@411 136 }
Chris@411 137
Chris@411 138 return val;
Chris@411 139 }
Chris@411 140
Chris@411 141 QString
Chris@411 142 RegionLayer::getPropertyValueLabel(const PropertyName &name,
Chris@411 143 int value) const
Chris@411 144 {
Chris@412 145 if (name == "Plot Type") {
Chris@412 146
Chris@412 147 switch (value) {
Chris@412 148 default:
Chris@412 149 case 0: return tr("Lines");
Chris@412 150 case 1: return tr("Segmentation");
Chris@412 151 }
Chris@412 152
Chris@412 153 } else if (name == "Vertical Scale") {
Chris@411 154 switch (value) {
Chris@411 155 default:
Chris@411 156 case 0: return tr("Auto-Align");
Chris@411 157 case 1: return tr("Linear");
Chris@411 158 case 2: return tr("Log");
Chris@411 159 }
Chris@411 160 }
Chris@411 161 return SingleColourLayer::getPropertyValueLabel(name, value);
Chris@411 162 }
Chris@411 163
Chris@411 164 void
Chris@411 165 RegionLayer::setProperty(const PropertyName &name, int value)
Chris@411 166 {
Chris@412 167 if (name == "Plot Type") {
Chris@412 168 setPlotStyle(PlotStyle(value));
Chris@412 169 } else if (name == "Vertical Scale") {
Chris@411 170 setVerticalScale(VerticalScale(value));
Chris@411 171 } else if (name == "Scale Units") {
Chris@411 172 if (m_model) {
Chris@411 173 m_model->setScaleUnits
Chris@411 174 (UnitDatabase::getInstance()->getUnitById(value));
Chris@411 175 emit modelChanged();
Chris@411 176 }
Chris@411 177 } else {
Chris@411 178 return SingleColourLayer::setProperty(name, value);
Chris@411 179 }
Chris@411 180 }
Chris@411 181
Chris@411 182 void
Chris@412 183 RegionLayer::setPlotStyle(PlotStyle style)
Chris@412 184 {
Chris@412 185 if (m_plotStyle == style) return;
Chris@412 186 m_plotStyle = style;
Chris@412 187 emit layerParametersChanged();
Chris@412 188 }
Chris@412 189
Chris@412 190 void
Chris@411 191 RegionLayer::setVerticalScale(VerticalScale scale)
Chris@411 192 {
Chris@411 193 if (m_verticalScale == scale) return;
Chris@411 194 m_verticalScale = scale;
Chris@411 195 emit layerParametersChanged();
Chris@411 196 }
Chris@411 197
Chris@411 198 bool
Chris@411 199 RegionLayer::isLayerScrollable(const View *v) const
Chris@411 200 {
Chris@411 201 QPoint discard;
Chris@411 202 return !v->shouldIlluminateLocalFeatures(this, discard);
Chris@411 203 }
Chris@411 204
Chris@411 205 bool
Chris@411 206 RegionLayer::getValueExtents(float &min, float &max,
Chris@411 207 bool &logarithmic, QString &unit) const
Chris@411 208 {
Chris@411 209 if (!m_model) return false;
Chris@411 210 min = m_model->getValueMinimum();
Chris@411 211 max = m_model->getValueMaximum();
Chris@411 212 unit = m_model->getScaleUnits();
Chris@411 213
Chris@411 214 if (m_verticalScale == LogScale) logarithmic = true;
Chris@411 215
Chris@411 216 return true;
Chris@411 217 }
Chris@411 218
Chris@411 219 bool
Chris@411 220 RegionLayer::getDisplayExtents(float &min, float &max) const
Chris@411 221 {
Chris@411 222 if (!m_model || m_verticalScale == AutoAlignScale) return false;
Chris@411 223
Chris@411 224 min = m_model->getValueMinimum();
Chris@411 225 max = m_model->getValueMaximum();
Chris@411 226
Chris@411 227 return true;
Chris@411 228 }
Chris@411 229
Chris@411 230 RegionModel::PointList
Chris@411 231 RegionLayer::getLocalPoints(View *v, int x) const
Chris@411 232 {
Chris@411 233 if (!m_model) return RegionModel::PointList();
Chris@411 234
Chris@411 235 long frame = v->getFrameForX(x);
Chris@411 236
Chris@411 237 RegionModel::PointList onPoints =
Chris@411 238 m_model->getPoints(frame);
Chris@411 239
Chris@411 240 if (!onPoints.empty()) {
Chris@411 241 return onPoints;
Chris@411 242 }
Chris@411 243
Chris@411 244 RegionModel::PointList prevPoints =
Chris@411 245 m_model->getPreviousPoints(frame);
Chris@411 246 RegionModel::PointList nextPoints =
Chris@411 247 m_model->getNextPoints(frame);
Chris@411 248
Chris@411 249 RegionModel::PointList usePoints = prevPoints;
Chris@411 250
Chris@411 251 if (prevPoints.empty()) {
Chris@411 252 usePoints = nextPoints;
Chris@411 253 } else if (long(prevPoints.begin()->frame) < v->getStartFrame() &&
Chris@411 254 !(nextPoints.begin()->frame > v->getEndFrame())) {
Chris@411 255 usePoints = nextPoints;
Chris@411 256 } else if (long(nextPoints.begin()->frame) - frame <
Chris@411 257 frame - long(prevPoints.begin()->frame)) {
Chris@411 258 usePoints = nextPoints;
Chris@411 259 }
Chris@411 260
Chris@411 261 if (!usePoints.empty()) {
Chris@411 262 int fuzz = 2;
Chris@411 263 int px = v->getXForFrame(usePoints.begin()->frame);
Chris@411 264 if ((px > x && px - x > fuzz) ||
Chris@411 265 (px < x && x - px > fuzz + 1)) {
Chris@411 266 usePoints.clear();
Chris@411 267 }
Chris@411 268 }
Chris@411 269
Chris@411 270 return usePoints;
Chris@411 271 }
Chris@411 272
Chris@411 273 QString
Chris@411 274 RegionLayer::getFeatureDescription(View *v, QPoint &pos) const
Chris@411 275 {
Chris@411 276 int x = pos.x();
Chris@411 277
Chris@411 278 if (!m_model || !m_model->getSampleRate()) return "";
Chris@411 279
Chris@411 280 RegionModel::PointList points = getLocalPoints(v, x);
Chris@411 281
Chris@411 282 if (points.empty()) {
Chris@411 283 if (!m_model->isReady()) {
Chris@411 284 return tr("In progress");
Chris@411 285 } else {
Chris@411 286 return tr("No local points");
Chris@411 287 }
Chris@411 288 }
Chris@411 289
Chris@411 290 RegionRec region(0);
Chris@411 291 RegionModel::PointList::iterator i;
Chris@411 292
Chris@413 293 //!!! harmonise with whatever decision is made about point y
Chris@413 294 //!!! coords in paint method
Chris@413 295
Chris@411 296 for (i = points.begin(); i != points.end(); ++i) {
Chris@411 297
Chris@411 298 int y = getYForValue(v, i->value);
Chris@411 299 int h = 3;
Chris@411 300
Chris@411 301 if (m_model->getValueQuantization() != 0.0) {
Chris@411 302 h = y - getYForValue(v, i->value + m_model->getValueQuantization());
Chris@411 303 if (h < 3) h = 3;
Chris@411 304 }
Chris@411 305
Chris@411 306 if (pos.y() >= y - h && pos.y() <= y) {
Chris@411 307 region = *i;
Chris@411 308 break;
Chris@411 309 }
Chris@411 310 }
Chris@411 311
Chris@411 312 if (i == points.end()) return tr("No local points");
Chris@411 313
Chris@411 314 RealTime rt = RealTime::frame2RealTime(region.frame,
Chris@411 315 m_model->getSampleRate());
Chris@411 316 RealTime rd = RealTime::frame2RealTime(region.duration,
Chris@411 317 m_model->getSampleRate());
Chris@411 318
Chris@411 319 QString valueText;
Chris@411 320
Chris@411 321 valueText = tr("%1 %2").arg(region.value).arg(m_model->getScaleUnits());
Chris@411 322
Chris@411 323 QString text;
Chris@411 324
Chris@411 325 if (region.label == "") {
Chris@411 326 text = QString(tr("Time:\t%1\nValue:\t%2\nDuration:\t%3\nNo label"))
Chris@411 327 .arg(rt.toText(true).c_str())
Chris@411 328 .arg(valueText)
Chris@411 329 .arg(rd.toText(true).c_str());
Chris@411 330 } else {
Chris@411 331 text = QString(tr("Time:\t%1\nValue:\t%2\nDuration:\t%3\nLabel:\t%4"))
Chris@411 332 .arg(rt.toText(true).c_str())
Chris@411 333 .arg(valueText)
Chris@411 334 .arg(rd.toText(true).c_str())
Chris@411 335 .arg(region.label);
Chris@411 336 }
Chris@411 337
Chris@411 338 pos = QPoint(v->getXForFrame(region.frame),
Chris@411 339 getYForValue(v, region.value));
Chris@411 340 return text;
Chris@411 341 }
Chris@411 342
Chris@411 343 bool
Chris@411 344 RegionLayer::snapToFeatureFrame(View *v, int &frame,
Chris@414 345 size_t &resolution,
Chris@414 346 SnapType snap) const
Chris@411 347 {
Chris@411 348 if (!m_model) {
Chris@411 349 return Layer::snapToFeatureFrame(v, frame, resolution, snap);
Chris@411 350 }
Chris@411 351
Chris@411 352 resolution = m_model->getResolution();
Chris@411 353 RegionModel::PointList points;
Chris@411 354
Chris@411 355 if (snap == SnapNeighbouring) {
Chris@411 356
Chris@411 357 points = getLocalPoints(v, v->getXForFrame(frame));
Chris@411 358 if (points.empty()) return false;
Chris@411 359 frame = points.begin()->frame;
Chris@411 360 return true;
Chris@411 361 }
Chris@411 362
Chris@411 363 points = m_model->getPoints(frame, frame);
Chris@411 364 int snapped = frame;
Chris@411 365 bool found = false;
Chris@411 366
Chris@411 367 for (RegionModel::PointList::const_iterator i = points.begin();
Chris@411 368 i != points.end(); ++i) {
Chris@411 369
Chris@411 370 if (snap == SnapRight) {
Chris@411 371
Chris@414 372 // The best frame to snap to is the end frame of whichever
Chris@414 373 // feature we would have snapped to the start frame of if
Chris@414 374 // we had been snapping left.
Chris@414 375
Chris@414 376 if (i->frame <= frame) {
Chris@414 377 if (i->frame + i->duration > frame) {
Chris@414 378 snapped = i->frame + i->duration;
Chris@414 379 found = true; // don't break, as the next may be better
Chris@414 380 }
Chris@414 381 } else {
Chris@414 382 if (!found) {
Chris@414 383 snapped = i->frame;
Chris@414 384 found = true;
Chris@414 385 }
Chris@414 386 break;
Chris@414 387 }
Chris@411 388
Chris@411 389 } else if (snap == SnapLeft) {
Chris@411 390
Chris@411 391 if (i->frame <= frame) {
Chris@411 392 snapped = i->frame;
Chris@411 393 found = true; // don't break, as the next may be better
Chris@411 394 } else {
Chris@411 395 break;
Chris@411 396 }
Chris@411 397
Chris@411 398 } else { // nearest
Chris@411 399
Chris@411 400 RegionModel::PointList::const_iterator j = i;
Chris@411 401 ++j;
Chris@411 402
Chris@411 403 if (j == points.end()) {
Chris@411 404
Chris@411 405 snapped = i->frame;
Chris@411 406 found = true;
Chris@411 407 break;
Chris@411 408
Chris@411 409 } else if (j->frame >= frame) {
Chris@411 410
Chris@411 411 if (j->frame - frame < frame - i->frame) {
Chris@411 412 snapped = j->frame;
Chris@411 413 } else {
Chris@411 414 snapped = i->frame;
Chris@411 415 }
Chris@411 416 found = true;
Chris@411 417 break;
Chris@411 418 }
Chris@411 419 }
Chris@411 420 }
Chris@411 421
Chris@411 422 frame = snapped;
Chris@411 423 return found;
Chris@411 424 }
Chris@411 425
Chris@411 426 void
Chris@411 427 RegionLayer::getScaleExtents(View *v, float &min, float &max, bool &log) const
Chris@411 428 {
Chris@411 429 min = 0.0;
Chris@411 430 max = 0.0;
Chris@411 431 log = false;
Chris@411 432
Chris@411 433 QString queryUnits;
Chris@411 434 queryUnits = m_model->getScaleUnits();
Chris@411 435
Chris@411 436 if (m_verticalScale == AutoAlignScale) {
Chris@411 437
Chris@411 438 if (!v->getValueExtents(queryUnits, min, max, log)) {
Chris@411 439
Chris@411 440 min = m_model->getValueMinimum();
Chris@411 441 max = m_model->getValueMaximum();
Chris@411 442
Chris@411 443 std::cerr << "RegionLayer[" << this << "]::getScaleExtents: min = " << min << ", max = " << max << ", log = " << log << std::endl;
Chris@411 444
Chris@411 445 } else if (log) {
Chris@411 446
Chris@411 447 LogRange::mapRange(min, max);
Chris@411 448
Chris@411 449 std::cerr << "RegionLayer[" << this << "]::getScaleExtents: min = " << min << ", max = " << max << ", log = " << log << std::endl;
Chris@411 450
Chris@411 451 }
Chris@411 452
Chris@411 453 } else {
Chris@411 454
Chris@411 455 min = m_model->getValueMinimum();
Chris@411 456 max = m_model->getValueMaximum();
Chris@411 457
Chris@411 458 if (m_verticalScale == LogScale) {
Chris@411 459 LogRange::mapRange(min, max);
Chris@411 460 log = true;
Chris@411 461 }
Chris@411 462 }
Chris@411 463
Chris@411 464 if (max == min) max = min + 1.0;
Chris@411 465 }
Chris@411 466
Chris@411 467 int
Chris@411 468 RegionLayer::getYForValue(View *v, float val) const
Chris@411 469 {
Chris@411 470 float min = 0.0, max = 0.0;
Chris@411 471 bool logarithmic = false;
Chris@411 472 int h = v->height();
Chris@413 473 int margin = 8;
Chris@413 474 if (h < margin * 8) margin = h / 8;
Chris@411 475
Chris@411 476 getScaleExtents(v, min, max, logarithmic);
Chris@411 477
Chris@413 478 std::cerr << "RegionLayer[" << this << "]::getYForValue(" << val << "): min = " << min << ", max = " << max << ", log = " << logarithmic << std::endl;
Chris@413 479 std::cerr << "h = " << h << ", margin = " << margin << std::endl;
Chris@411 480
Chris@411 481 if (logarithmic) {
Chris@411 482 val = LogRange::map(val);
Chris@413 483 std::cerr << "logarithmic true, val now = " << val << std::endl;
Chris@411 484 }
Chris@411 485
Chris@413 486 h -= margin * 2;
Chris@413 487 int y = margin + int(h - ((val - min) * h) / (max - min)) - 1;
Chris@413 488 std::cerr << "y = " << y << std::endl;
Chris@411 489 return y;
Chris@411 490 }
Chris@411 491
Chris@411 492 float
Chris@411 493 RegionLayer::getValueForY(View *v, int y) const
Chris@411 494 {
Chris@411 495 float min = 0.0, max = 0.0;
Chris@411 496 bool logarithmic = false;
Chris@411 497 int h = v->height();
Chris@411 498
Chris@411 499 getScaleExtents(v, min, max, logarithmic);
Chris@411 500
Chris@411 501 float val = min + (float(h - y) * float(max - min)) / h;
Chris@411 502
Chris@411 503 if (logarithmic) {
Chris@411 504 val = powf(10.f, val);
Chris@411 505 }
Chris@411 506
Chris@411 507 return val;
Chris@411 508 }
Chris@411 509
Chris@411 510 void
Chris@411 511 RegionLayer::paint(View *v, QPainter &paint, QRect rect) const
Chris@411 512 {
Chris@411 513 if (!m_model || !m_model->isOK()) return;
Chris@411 514
Chris@411 515 int sampleRate = m_model->getSampleRate();
Chris@411 516 if (!sampleRate) return;
Chris@411 517
Chris@411 518 // Profiler profiler("RegionLayer::paint", true);
Chris@411 519
Chris@411 520 int x0 = rect.left(), x1 = rect.right();
Chris@411 521 long frame0 = v->getFrameForX(x0);
Chris@411 522 long frame1 = v->getFrameForX(x1);
Chris@411 523
Chris@411 524 RegionModel::PointList points(m_model->getPoints(frame0, frame1));
Chris@411 525 if (points.empty()) return;
Chris@411 526
Chris@411 527 paint.setPen(getBaseQColor());
Chris@411 528
Chris@411 529 QColor brushColour(getBaseQColor());
Chris@411 530 brushColour.setAlpha(80);
Chris@411 531
Chris@411 532 // std::cerr << "RegionLayer::paint: resolution is "
Chris@411 533 // << m_model->getResolution() << " frames" << std::endl;
Chris@411 534
Chris@411 535 float min = m_model->getValueMinimum();
Chris@411 536 float max = m_model->getValueMaximum();
Chris@411 537 if (max == min) max = min + 1.0;
Chris@411 538
Chris@411 539 QPoint localPos;
Chris@411 540 long illuminateFrame = -1;
Chris@411 541
Chris@411 542 if (v->shouldIlluminateLocalFeatures(this, localPos)) {
Chris@411 543 RegionModel::PointList localPoints =
Chris@411 544 getLocalPoints(v, localPos.x());
Chris@411 545 if (!localPoints.empty()) illuminateFrame = localPoints.begin()->frame;
Chris@411 546 }
Chris@411 547
Chris@411 548 paint.save();
Chris@411 549 paint.setRenderHint(QPainter::Antialiasing, false);
Chris@411 550
Chris@413 551 //!!! point y coords if model does not haveDistinctValues() should
Chris@413 552 //!!! be assigned to avoid overlaps
Chris@413 553
Chris@413 554 //!!! if it does have distinct values, we should still ensure y
Chris@413 555 //!!! coord is never completely flat on the top or bottom
Chris@413 556
Chris@411 557 for (RegionModel::PointList::const_iterator i = points.begin();
Chris@411 558 i != points.end(); ++i) {
Chris@411 559
Chris@411 560 const RegionModel::Point &p(*i);
Chris@411 561
Chris@411 562 int x = v->getXForFrame(p.frame);
Chris@411 563 int y = getYForValue(v, p.value);
Chris@411 564 int w = v->getXForFrame(p.frame + p.duration) - x;
Chris@413 565 int h = 9;
Chris@411 566
Chris@411 567 if (m_model->getValueQuantization() != 0.0) {
Chris@411 568 h = y - getYForValue(v, p.value + m_model->getValueQuantization());
Chris@411 569 if (h < 3) h = 3;
Chris@411 570 }
Chris@411 571
Chris@411 572 if (w < 1) w = 1;
Chris@411 573 paint.setPen(getBaseQColor());
Chris@411 574 paint.setBrush(brushColour);
Chris@411 575
Chris@411 576 if (illuminateFrame == p.frame) {
Chris@411 577 if (localPos.y() >= y - h && localPos.y() < y) {
Chris@411 578 paint.setPen(v->getForeground());
Chris@411 579 paint.setBrush(v->getForeground());
Chris@411 580 }
Chris@411 581 }
Chris@411 582
Chris@413 583 paint.drawLine(x, y-1, x + w, y-1);
Chris@413 584 paint.drawLine(x, y+1, x + w, y+1);
Chris@411 585 paint.drawLine(x, y - h/2, x, y + h/2);
Chris@413 586 paint.drawLine(x+w, y - h/2, x + w, y + h/2);
Chris@411 587
Chris@411 588 /// if (p.label != "") {
Chris@411 589 /// paint.drawText(x + 5, y - paint.fontMetrics().height() + paint.fontMetrics().ascent(), p.label);
Chris@411 590 /// }
Chris@411 591 }
Chris@411 592
Chris@411 593 paint.restore();
Chris@411 594 }
Chris@411 595
Chris@411 596 void
Chris@411 597 RegionLayer::drawStart(View *v, QMouseEvent *e)
Chris@411 598 {
Chris@411 599 // std::cerr << "RegionLayer::drawStart(" << e->x() << "," << e->y() << ")" << std::endl;
Chris@411 600
Chris@411 601 if (!m_model) return;
Chris@411 602
Chris@411 603 long frame = v->getFrameForX(e->x());
Chris@411 604 if (frame < 0) frame = 0;
Chris@411 605 frame = frame / m_model->getResolution() * m_model->getResolution();
Chris@411 606
Chris@411 607 float value = getValueForY(v, e->y());
Chris@411 608
Chris@411 609 m_editingPoint = RegionModel::Point(frame, value, 0, tr("New Point"));
Chris@411 610 m_originalPoint = m_editingPoint;
Chris@411 611
Chris@411 612 if (m_editingCommand) finish(m_editingCommand);
Chris@411 613 m_editingCommand = new RegionModel::EditCommand(m_model,
Chris@411 614 tr("Draw Point"));
Chris@411 615 m_editingCommand->addPoint(m_editingPoint);
Chris@411 616
Chris@411 617 m_editing = true;
Chris@411 618 }
Chris@411 619
Chris@411 620 void
Chris@411 621 RegionLayer::drawDrag(View *v, QMouseEvent *e)
Chris@411 622 {
Chris@411 623 // std::cerr << "RegionLayer::drawDrag(" << e->x() << "," << e->y() << ")" << std::endl;
Chris@411 624
Chris@411 625 if (!m_model || !m_editing) return;
Chris@411 626
Chris@411 627 long frame = v->getFrameForX(e->x());
Chris@411 628 if (frame < 0) frame = 0;
Chris@411 629 frame = frame / m_model->getResolution() * m_model->getResolution();
Chris@411 630
Chris@411 631 float newValue = getValueForY(v, e->y());
Chris@411 632
Chris@411 633 long newFrame = m_editingPoint.frame;
Chris@411 634 long newDuration = frame - newFrame;
Chris@411 635 if (newDuration < 0) {
Chris@411 636 newFrame = frame;
Chris@411 637 newDuration = -newDuration;
Chris@411 638 } else if (newDuration == 0) {
Chris@411 639 newDuration = 1;
Chris@411 640 }
Chris@411 641
Chris@411 642 m_editingCommand->deletePoint(m_editingPoint);
Chris@411 643 m_editingPoint.frame = newFrame;
Chris@411 644 m_editingPoint.value = newValue;
Chris@411 645 m_editingPoint.duration = newDuration;
Chris@411 646 m_editingCommand->addPoint(m_editingPoint);
Chris@411 647 }
Chris@411 648
Chris@411 649 void
Chris@411 650 RegionLayer::drawEnd(View *, QMouseEvent *)
Chris@411 651 {
Chris@411 652 // std::cerr << "RegionLayer::drawEnd(" << e->x() << "," << e->y() << ")" << std::endl;
Chris@411 653 if (!m_model || !m_editing) return;
Chris@411 654 finish(m_editingCommand);
Chris@411 655 m_editingCommand = 0;
Chris@411 656 m_editing = false;
Chris@411 657 }
Chris@411 658
Chris@411 659 void
Chris@411 660 RegionLayer::eraseStart(View *v, QMouseEvent *e)
Chris@411 661 {
Chris@411 662 if (!m_model) return;
Chris@411 663
Chris@411 664 RegionModel::PointList points = getLocalPoints(v, e->x());
Chris@411 665 if (points.empty()) return;
Chris@411 666
Chris@411 667 m_editingPoint = *points.begin();
Chris@411 668
Chris@411 669 if (m_editingCommand) {
Chris@411 670 finish(m_editingCommand);
Chris@411 671 m_editingCommand = 0;
Chris@411 672 }
Chris@411 673
Chris@411 674 m_editing = true;
Chris@411 675 }
Chris@411 676
Chris@411 677 void
Chris@411 678 RegionLayer::eraseDrag(View *v, QMouseEvent *e)
Chris@411 679 {
Chris@411 680 }
Chris@411 681
Chris@411 682 void
Chris@411 683 RegionLayer::eraseEnd(View *v, QMouseEvent *e)
Chris@411 684 {
Chris@411 685 if (!m_model || !m_editing) return;
Chris@411 686
Chris@411 687 m_editing = false;
Chris@411 688
Chris@411 689 RegionModel::PointList points = getLocalPoints(v, e->x());
Chris@411 690 if (points.empty()) return;
Chris@411 691 if (points.begin()->frame != m_editingPoint.frame ||
Chris@411 692 points.begin()->value != m_editingPoint.value) return;
Chris@411 693
Chris@411 694 m_editingCommand = new RegionModel::EditCommand
Chris@411 695 (m_model, tr("Erase Point"));
Chris@411 696
Chris@411 697 m_editingCommand->deletePoint(m_editingPoint);
Chris@411 698
Chris@411 699 finish(m_editingCommand);
Chris@411 700 m_editingCommand = 0;
Chris@411 701 m_editing = false;
Chris@411 702 }
Chris@411 703
Chris@411 704 void
Chris@411 705 RegionLayer::editStart(View *v, QMouseEvent *e)
Chris@411 706 {
Chris@411 707 // std::cerr << "RegionLayer::editStart(" << e->x() << "," << e->y() << ")" << std::endl;
Chris@411 708
Chris@411 709 if (!m_model) return;
Chris@411 710
Chris@411 711 RegionModel::PointList points = getLocalPoints(v, e->x());
Chris@411 712 if (points.empty()) return;
Chris@411 713
Chris@411 714 m_editingPoint = *points.begin();
Chris@411 715 m_originalPoint = m_editingPoint;
Chris@411 716
Chris@411 717 if (m_editingCommand) {
Chris@411 718 finish(m_editingCommand);
Chris@411 719 m_editingCommand = 0;
Chris@411 720 }
Chris@411 721
Chris@411 722 m_editing = true;
Chris@411 723 }
Chris@411 724
Chris@411 725 void
Chris@411 726 RegionLayer::editDrag(View *v, QMouseEvent *e)
Chris@411 727 {
Chris@411 728 // std::cerr << "RegionLayer::editDrag(" << e->x() << "," << e->y() << ")" << std::endl;
Chris@411 729
Chris@411 730 if (!m_model || !m_editing) return;
Chris@411 731
Chris@411 732 long frame = v->getFrameForX(e->x());
Chris@411 733 if (frame < 0) frame = 0;
Chris@411 734 frame = frame / m_model->getResolution() * m_model->getResolution();
Chris@411 735
Chris@411 736 float value = getValueForY(v, e->y());
Chris@411 737
Chris@411 738 if (!m_editingCommand) {
Chris@411 739 m_editingCommand = new RegionModel::EditCommand(m_model,
Chris@411 740 tr("Drag Point"));
Chris@411 741 }
Chris@411 742
Chris@411 743 m_editingCommand->deletePoint(m_editingPoint);
Chris@411 744 m_editingPoint.frame = frame;
Chris@411 745 m_editingPoint.value = value;
Chris@411 746 m_editingCommand->addPoint(m_editingPoint);
Chris@411 747 }
Chris@411 748
Chris@411 749 void
Chris@411 750 RegionLayer::editEnd(View *, QMouseEvent *)
Chris@411 751 {
Chris@411 752 // std::cerr << "RegionLayer::editEnd(" << e->x() << "," << e->y() << ")" << std::endl;
Chris@411 753 if (!m_model || !m_editing) return;
Chris@411 754
Chris@411 755 if (m_editingCommand) {
Chris@411 756
Chris@411 757 QString newName = m_editingCommand->getName();
Chris@411 758
Chris@411 759 if (m_editingPoint.frame != m_originalPoint.frame) {
Chris@411 760 if (m_editingPoint.value != m_originalPoint.value) {
Chris@411 761 newName = tr("Edit Point");
Chris@411 762 } else {
Chris@411 763 newName = tr("Relocate Point");
Chris@411 764 }
Chris@411 765 } else {
Chris@411 766 newName = tr("Change Point Value");
Chris@411 767 }
Chris@411 768
Chris@411 769 m_editingCommand->setName(newName);
Chris@411 770 finish(m_editingCommand);
Chris@411 771 }
Chris@411 772
Chris@411 773 m_editingCommand = 0;
Chris@411 774 m_editing = false;
Chris@411 775 }
Chris@411 776
Chris@411 777 bool
Chris@411 778 RegionLayer::editOpen(View *v, QMouseEvent *e)
Chris@411 779 {
Chris@411 780 if (!m_model) return false;
Chris@411 781
Chris@411 782 RegionModel::PointList points = getLocalPoints(v, e->x());
Chris@411 783 if (points.empty()) return false;
Chris@411 784
Chris@411 785 RegionModel::Point region = *points.begin();
Chris@411 786
Chris@411 787 ItemEditDialog *dialog = new ItemEditDialog
Chris@411 788 (m_model->getSampleRate(),
Chris@411 789 ItemEditDialog::ShowTime |
Chris@411 790 ItemEditDialog::ShowDuration |
Chris@411 791 ItemEditDialog::ShowValue |
Chris@411 792 ItemEditDialog::ShowText,
Chris@411 793 m_model->getScaleUnits());
Chris@411 794
Chris@411 795 dialog->setFrameTime(region.frame);
Chris@411 796 dialog->setValue(region.value);
Chris@411 797 dialog->setFrameDuration(region.duration);
Chris@411 798 dialog->setText(region.label);
Chris@411 799
Chris@411 800 if (dialog->exec() == QDialog::Accepted) {
Chris@411 801
Chris@411 802 RegionModel::Point newRegion = region;
Chris@411 803 newRegion.frame = dialog->getFrameTime();
Chris@411 804 newRegion.value = dialog->getValue();
Chris@411 805 newRegion.duration = dialog->getFrameDuration();
Chris@411 806 newRegion.label = dialog->getText();
Chris@411 807
Chris@411 808 RegionModel::EditCommand *command = new RegionModel::EditCommand
Chris@411 809 (m_model, tr("Edit Point"));
Chris@411 810 command->deletePoint(region);
Chris@411 811 command->addPoint(newRegion);
Chris@411 812 finish(command);
Chris@411 813 }
Chris@411 814
Chris@411 815 delete dialog;
Chris@411 816 return true;
Chris@411 817 }
Chris@411 818
Chris@411 819 void
Chris@411 820 RegionLayer::moveSelection(Selection s, size_t newStartFrame)
Chris@411 821 {
Chris@411 822 if (!m_model) return;
Chris@411 823
Chris@411 824 RegionModel::EditCommand *command =
Chris@411 825 new RegionModel::EditCommand(m_model, tr("Drag Selection"));
Chris@411 826
Chris@411 827 RegionModel::PointList points =
Chris@411 828 m_model->getPoints(s.getStartFrame(), s.getEndFrame());
Chris@411 829
Chris@411 830 for (RegionModel::PointList::iterator i = points.begin();
Chris@411 831 i != points.end(); ++i) {
Chris@411 832
Chris@411 833 if (s.contains(i->frame)) {
Chris@411 834 RegionModel::Point newPoint(*i);
Chris@411 835 newPoint.frame = i->frame + newStartFrame - s.getStartFrame();
Chris@411 836 command->deletePoint(*i);
Chris@411 837 command->addPoint(newPoint);
Chris@411 838 }
Chris@411 839 }
Chris@411 840
Chris@411 841 finish(command);
Chris@411 842 }
Chris@411 843
Chris@411 844 void
Chris@411 845 RegionLayer::resizeSelection(Selection s, Selection newSize)
Chris@411 846 {
Chris@411 847 if (!m_model) return;
Chris@411 848
Chris@411 849 RegionModel::EditCommand *command =
Chris@411 850 new RegionModel::EditCommand(m_model, tr("Resize Selection"));
Chris@411 851
Chris@411 852 RegionModel::PointList points =
Chris@411 853 m_model->getPoints(s.getStartFrame(), s.getEndFrame());
Chris@411 854
Chris@411 855 double ratio =
Chris@411 856 double(newSize.getEndFrame() - newSize.getStartFrame()) /
Chris@411 857 double(s.getEndFrame() - s.getStartFrame());
Chris@411 858
Chris@411 859 for (RegionModel::PointList::iterator i = points.begin();
Chris@411 860 i != points.end(); ++i) {
Chris@411 861
Chris@411 862 if (s.contains(i->frame)) {
Chris@411 863
Chris@411 864 double targetStart = i->frame;
Chris@411 865 targetStart = newSize.getStartFrame() +
Chris@411 866 double(targetStart - s.getStartFrame()) * ratio;
Chris@411 867
Chris@411 868 double targetEnd = i->frame + i->duration;
Chris@411 869 targetEnd = newSize.getStartFrame() +
Chris@411 870 double(targetEnd - s.getStartFrame()) * ratio;
Chris@411 871
Chris@411 872 RegionModel::Point newPoint(*i);
Chris@411 873 newPoint.frame = lrint(targetStart);
Chris@411 874 newPoint.duration = lrint(targetEnd - targetStart);
Chris@411 875 command->deletePoint(*i);
Chris@411 876 command->addPoint(newPoint);
Chris@411 877 }
Chris@411 878 }
Chris@411 879
Chris@411 880 finish(command);
Chris@411 881 }
Chris@411 882
Chris@411 883 void
Chris@411 884 RegionLayer::deleteSelection(Selection s)
Chris@411 885 {
Chris@411 886 if (!m_model) return;
Chris@411 887
Chris@411 888 RegionModel::EditCommand *command =
Chris@411 889 new RegionModel::EditCommand(m_model, tr("Delete Selected Points"));
Chris@411 890
Chris@411 891 RegionModel::PointList points =
Chris@411 892 m_model->getPoints(s.getStartFrame(), s.getEndFrame());
Chris@411 893
Chris@411 894 for (RegionModel::PointList::iterator i = points.begin();
Chris@411 895 i != points.end(); ++i) {
Chris@411 896
Chris@411 897 if (s.contains(i->frame)) {
Chris@411 898 command->deletePoint(*i);
Chris@411 899 }
Chris@411 900 }
Chris@411 901
Chris@411 902 finish(command);
Chris@411 903 }
Chris@411 904
Chris@411 905 void
Chris@411 906 RegionLayer::copy(View *v, Selection s, Clipboard &to)
Chris@411 907 {
Chris@411 908 if (!m_model) return;
Chris@411 909
Chris@411 910 RegionModel::PointList points =
Chris@411 911 m_model->getPoints(s.getStartFrame(), s.getEndFrame());
Chris@411 912
Chris@411 913 for (RegionModel::PointList::iterator i = points.begin();
Chris@411 914 i != points.end(); ++i) {
Chris@411 915 if (s.contains(i->frame)) {
Chris@411 916 Clipboard::Point point(i->frame, i->value, i->duration, i->label);
Chris@411 917 point.setReferenceFrame(alignToReference(v, i->frame));
Chris@411 918 to.addPoint(point);
Chris@411 919 }
Chris@411 920 }
Chris@411 921 }
Chris@411 922
Chris@411 923 bool
Chris@411 924 RegionLayer::paste(View *v, const Clipboard &from, int frameOffset, bool /* interactive */)
Chris@411 925 {
Chris@411 926 if (!m_model) return false;
Chris@411 927
Chris@411 928 const Clipboard::PointList &points = from.getPoints();
Chris@411 929
Chris@411 930 bool realign = false;
Chris@411 931
Chris@411 932 if (clipboardHasDifferentAlignment(v, from)) {
Chris@411 933
Chris@411 934 QMessageBox::StandardButton button =
Chris@411 935 QMessageBox::question(v, tr("Re-align pasted items?"),
Chris@411 936 tr("The items 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@411 937 QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel,
Chris@411 938 QMessageBox::Yes);
Chris@411 939
Chris@411 940 if (button == QMessageBox::Cancel) {
Chris@411 941 return false;
Chris@411 942 }
Chris@411 943
Chris@411 944 if (button == QMessageBox::Yes) {
Chris@411 945 realign = true;
Chris@411 946 }
Chris@411 947 }
Chris@411 948
Chris@411 949 RegionModel::EditCommand *command =
Chris@411 950 new RegionModel::EditCommand(m_model, tr("Paste"));
Chris@411 951
Chris@411 952 for (Clipboard::PointList::const_iterator i = points.begin();
Chris@411 953 i != points.end(); ++i) {
Chris@411 954
Chris@411 955 if (!i->haveFrame()) continue;
Chris@411 956 size_t frame = 0;
Chris@411 957
Chris@411 958 if (!realign) {
Chris@411 959
Chris@411 960 frame = i->getFrame();
Chris@411 961
Chris@411 962 } else {
Chris@411 963
Chris@411 964 if (i->haveReferenceFrame()) {
Chris@411 965 frame = i->getReferenceFrame();
Chris@411 966 frame = alignFromReference(v, frame);
Chris@411 967 } else {
Chris@411 968 frame = i->getFrame();
Chris@411 969 }
Chris@411 970 }
Chris@411 971
Chris@411 972 RegionModel::Point newPoint(frame);
Chris@411 973
Chris@411 974 if (i->haveLabel()) newPoint.label = i->getLabel();
Chris@411 975 if (i->haveValue()) newPoint.value = i->getValue();
Chris@411 976 else newPoint.value = (m_model->getValueMinimum() +
Chris@411 977 m_model->getValueMaximum()) / 2;
Chris@411 978 if (i->haveDuration()) newPoint.duration = i->getDuration();
Chris@411 979 else {
Chris@411 980 size_t nextFrame = frame;
Chris@411 981 Clipboard::PointList::const_iterator j = i;
Chris@411 982 for (; j != points.end(); ++j) {
Chris@411 983 if (!j->haveFrame()) continue;
Chris@411 984 if (j != i) break;
Chris@411 985 }
Chris@411 986 if (j != points.end()) {
Chris@411 987 nextFrame = j->getFrame();
Chris@411 988 }
Chris@411 989 if (nextFrame == frame) {
Chris@411 990 newPoint.duration = m_model->getResolution();
Chris@411 991 } else {
Chris@411 992 newPoint.duration = nextFrame - frame;
Chris@411 993 }
Chris@411 994 }
Chris@411 995
Chris@411 996 command->addPoint(newPoint);
Chris@411 997 }
Chris@411 998
Chris@411 999 finish(command);
Chris@411 1000 return true;
Chris@411 1001 }
Chris@411 1002
Chris@411 1003 int
Chris@411 1004 RegionLayer::getDefaultColourHint(bool darkbg, bool &impose)
Chris@411 1005 {
Chris@411 1006 impose = false;
Chris@411 1007 return ColourDatabase::getInstance()->getColourIndex
Chris@411 1008 (QString(darkbg ? "White" : "Black"));
Chris@411 1009 }
Chris@411 1010
Chris@411 1011 void
Chris@411 1012 RegionLayer::toXml(QTextStream &stream,
Chris@411 1013 QString indent, QString extraAttributes) const
Chris@411 1014 {
Chris@411 1015 SingleColourLayer::toXml(stream, indent, extraAttributes +
Chris@412 1016 QString(" verticalScale=\"%1\" plotStyle=\"%2\"")
Chris@412 1017 .arg(m_verticalScale)
Chris@412 1018 .arg(m_plotStyle));
Chris@411 1019 }
Chris@411 1020
Chris@411 1021 void
Chris@411 1022 RegionLayer::setProperties(const QXmlAttributes &attributes)
Chris@411 1023 {
Chris@411 1024 SingleColourLayer::setProperties(attributes);
Chris@411 1025
Chris@411 1026 bool ok;
Chris@411 1027 VerticalScale scale = (VerticalScale)
Chris@411 1028 attributes.value("verticalScale").toInt(&ok);
Chris@411 1029 if (ok) setVerticalScale(scale);
Chris@412 1030 PlotStyle style = (PlotStyle)
Chris@412 1031 attributes.value("plotStyle").toInt(&ok);
Chris@412 1032 if (ok) setPlotStyle(style);
Chris@411 1033 }
Chris@411 1034
Chris@411 1035