annotate layer/RegionLayer.cpp @ 412:d332ad1ca66b

* Add segmentation plot type to region layer (plotting not implemented yet)
author Chris Cannam
date Fri, 19 Sep 2008 12:55:35 +0000
parents 96e4d7b9e165
children f71752646f97
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@411 293 for (i = points.begin(); i != points.end(); ++i) {
Chris@411 294
Chris@411 295 int y = getYForValue(v, i->value);
Chris@411 296 int h = 3;
Chris@411 297
Chris@411 298 if (m_model->getValueQuantization() != 0.0) {
Chris@411 299 h = y - getYForValue(v, i->value + m_model->getValueQuantization());
Chris@411 300 if (h < 3) h = 3;
Chris@411 301 }
Chris@411 302
Chris@411 303 if (pos.y() >= y - h && pos.y() <= y) {
Chris@411 304 region = *i;
Chris@411 305 break;
Chris@411 306 }
Chris@411 307 }
Chris@411 308
Chris@411 309 if (i == points.end()) return tr("No local points");
Chris@411 310
Chris@411 311 RealTime rt = RealTime::frame2RealTime(region.frame,
Chris@411 312 m_model->getSampleRate());
Chris@411 313 RealTime rd = RealTime::frame2RealTime(region.duration,
Chris@411 314 m_model->getSampleRate());
Chris@411 315
Chris@411 316 QString valueText;
Chris@411 317
Chris@411 318 valueText = tr("%1 %2").arg(region.value).arg(m_model->getScaleUnits());
Chris@411 319
Chris@411 320 QString text;
Chris@411 321
Chris@411 322 if (region.label == "") {
Chris@411 323 text = QString(tr("Time:\t%1\nValue:\t%2\nDuration:\t%3\nNo label"))
Chris@411 324 .arg(rt.toText(true).c_str())
Chris@411 325 .arg(valueText)
Chris@411 326 .arg(rd.toText(true).c_str());
Chris@411 327 } else {
Chris@411 328 text = QString(tr("Time:\t%1\nValue:\t%2\nDuration:\t%3\nLabel:\t%4"))
Chris@411 329 .arg(rt.toText(true).c_str())
Chris@411 330 .arg(valueText)
Chris@411 331 .arg(rd.toText(true).c_str())
Chris@411 332 .arg(region.label);
Chris@411 333 }
Chris@411 334
Chris@411 335 pos = QPoint(v->getXForFrame(region.frame),
Chris@411 336 getYForValue(v, region.value));
Chris@411 337 return text;
Chris@411 338 }
Chris@411 339
Chris@411 340 bool
Chris@411 341 RegionLayer::snapToFeatureFrame(View *v, int &frame,
Chris@411 342 size_t &resolution,
Chris@411 343 SnapType snap) const
Chris@411 344 {
Chris@411 345 if (!m_model) {
Chris@411 346 return Layer::snapToFeatureFrame(v, frame, resolution, snap);
Chris@411 347 }
Chris@411 348
Chris@411 349 resolution = m_model->getResolution();
Chris@411 350 RegionModel::PointList points;
Chris@411 351
Chris@411 352 if (snap == SnapNeighbouring) {
Chris@411 353
Chris@411 354 points = getLocalPoints(v, v->getXForFrame(frame));
Chris@411 355 if (points.empty()) return false;
Chris@411 356 frame = points.begin()->frame;
Chris@411 357 return true;
Chris@411 358 }
Chris@411 359
Chris@411 360 points = m_model->getPoints(frame, frame);
Chris@411 361 int snapped = frame;
Chris@411 362 bool found = false;
Chris@411 363
Chris@411 364 for (RegionModel::PointList::const_iterator i = points.begin();
Chris@411 365 i != points.end(); ++i) {
Chris@411 366
Chris@411 367 if (snap == SnapRight) {
Chris@411 368
Chris@411 369 if (i->frame > frame) {
Chris@411 370 snapped = i->frame;
Chris@411 371 found = true;
Chris@411 372 break;
Chris@411 373 }
Chris@411 374
Chris@411 375 } else if (snap == SnapLeft) {
Chris@411 376
Chris@411 377 if (i->frame <= frame) {
Chris@411 378 snapped = i->frame;
Chris@411 379 found = true; // don't break, as the next may be better
Chris@411 380 } else {
Chris@411 381 break;
Chris@411 382 }
Chris@411 383
Chris@411 384 } else { // nearest
Chris@411 385
Chris@411 386 RegionModel::PointList::const_iterator j = i;
Chris@411 387 ++j;
Chris@411 388
Chris@411 389 if (j == points.end()) {
Chris@411 390
Chris@411 391 snapped = i->frame;
Chris@411 392 found = true;
Chris@411 393 break;
Chris@411 394
Chris@411 395 } else if (j->frame >= frame) {
Chris@411 396
Chris@411 397 if (j->frame - frame < frame - i->frame) {
Chris@411 398 snapped = j->frame;
Chris@411 399 } else {
Chris@411 400 snapped = i->frame;
Chris@411 401 }
Chris@411 402 found = true;
Chris@411 403 break;
Chris@411 404 }
Chris@411 405 }
Chris@411 406 }
Chris@411 407
Chris@411 408 frame = snapped;
Chris@411 409 return found;
Chris@411 410 }
Chris@411 411
Chris@411 412 void
Chris@411 413 RegionLayer::getScaleExtents(View *v, float &min, float &max, bool &log) const
Chris@411 414 {
Chris@411 415 min = 0.0;
Chris@411 416 max = 0.0;
Chris@411 417 log = false;
Chris@411 418
Chris@411 419 QString queryUnits;
Chris@411 420 queryUnits = m_model->getScaleUnits();
Chris@411 421
Chris@411 422 if (m_verticalScale == AutoAlignScale) {
Chris@411 423
Chris@411 424 if (!v->getValueExtents(queryUnits, min, max, log)) {
Chris@411 425
Chris@411 426 min = m_model->getValueMinimum();
Chris@411 427 max = m_model->getValueMaximum();
Chris@411 428
Chris@411 429 std::cerr << "RegionLayer[" << this << "]::getScaleExtents: min = " << min << ", max = " << max << ", log = " << log << std::endl;
Chris@411 430
Chris@411 431 } else if (log) {
Chris@411 432
Chris@411 433 LogRange::mapRange(min, max);
Chris@411 434
Chris@411 435 std::cerr << "RegionLayer[" << this << "]::getScaleExtents: min = " << min << ", max = " << max << ", log = " << log << std::endl;
Chris@411 436
Chris@411 437 }
Chris@411 438
Chris@411 439 } else {
Chris@411 440
Chris@411 441 min = m_model->getValueMinimum();
Chris@411 442 max = m_model->getValueMaximum();
Chris@411 443
Chris@411 444 if (m_verticalScale == LogScale) {
Chris@411 445 LogRange::mapRange(min, max);
Chris@411 446 log = true;
Chris@411 447 }
Chris@411 448 }
Chris@411 449
Chris@411 450 if (max == min) max = min + 1.0;
Chris@411 451 }
Chris@411 452
Chris@411 453 int
Chris@411 454 RegionLayer::getYForValue(View *v, float val) const
Chris@411 455 {
Chris@411 456 float min = 0.0, max = 0.0;
Chris@411 457 bool logarithmic = false;
Chris@411 458 int h = v->height();
Chris@411 459
Chris@411 460 getScaleExtents(v, min, max, logarithmic);
Chris@411 461
Chris@411 462 // std::cerr << "RegionLayer[" << this << "]::getYForValue(" << val << "): min = " << min << ", max = " << max << ", log = " << logarithmic << std::endl;
Chris@411 463
Chris@411 464 if (logarithmic) {
Chris@411 465 val = LogRange::map(val);
Chris@411 466 // std::cerr << "logarithmic true, val now = " << val << std::endl;
Chris@411 467 }
Chris@411 468
Chris@411 469 int y = int(h - ((val - min) * h) / (max - min)) - 1;
Chris@411 470 // std::cerr << "y = " << y << std::endl;
Chris@411 471 return y;
Chris@411 472 }
Chris@411 473
Chris@411 474 float
Chris@411 475 RegionLayer::getValueForY(View *v, int y) const
Chris@411 476 {
Chris@411 477 float min = 0.0, max = 0.0;
Chris@411 478 bool logarithmic = false;
Chris@411 479 int h = v->height();
Chris@411 480
Chris@411 481 getScaleExtents(v, min, max, logarithmic);
Chris@411 482
Chris@411 483 float val = min + (float(h - y) * float(max - min)) / h;
Chris@411 484
Chris@411 485 if (logarithmic) {
Chris@411 486 val = powf(10.f, val);
Chris@411 487 }
Chris@411 488
Chris@411 489 return val;
Chris@411 490 }
Chris@411 491
Chris@411 492 void
Chris@411 493 RegionLayer::paint(View *v, QPainter &paint, QRect rect) const
Chris@411 494 {
Chris@411 495 if (!m_model || !m_model->isOK()) return;
Chris@411 496
Chris@411 497 int sampleRate = m_model->getSampleRate();
Chris@411 498 if (!sampleRate) return;
Chris@411 499
Chris@411 500 // Profiler profiler("RegionLayer::paint", true);
Chris@411 501
Chris@411 502 int x0 = rect.left(), x1 = rect.right();
Chris@411 503 long frame0 = v->getFrameForX(x0);
Chris@411 504 long frame1 = v->getFrameForX(x1);
Chris@411 505
Chris@411 506 RegionModel::PointList points(m_model->getPoints(frame0, frame1));
Chris@411 507 if (points.empty()) return;
Chris@411 508
Chris@411 509 paint.setPen(getBaseQColor());
Chris@411 510
Chris@411 511 QColor brushColour(getBaseQColor());
Chris@411 512 brushColour.setAlpha(80);
Chris@411 513
Chris@411 514 // std::cerr << "RegionLayer::paint: resolution is "
Chris@411 515 // << m_model->getResolution() << " frames" << std::endl;
Chris@411 516
Chris@411 517 float min = m_model->getValueMinimum();
Chris@411 518 float max = m_model->getValueMaximum();
Chris@411 519 if (max == min) max = min + 1.0;
Chris@411 520
Chris@411 521 QPoint localPos;
Chris@411 522 long illuminateFrame = -1;
Chris@411 523
Chris@411 524 if (v->shouldIlluminateLocalFeatures(this, localPos)) {
Chris@411 525 RegionModel::PointList localPoints =
Chris@411 526 getLocalPoints(v, localPos.x());
Chris@411 527 if (!localPoints.empty()) illuminateFrame = localPoints.begin()->frame;
Chris@411 528 }
Chris@411 529
Chris@411 530 paint.save();
Chris@411 531 paint.setRenderHint(QPainter::Antialiasing, false);
Chris@411 532
Chris@411 533 for (RegionModel::PointList::const_iterator i = points.begin();
Chris@411 534 i != points.end(); ++i) {
Chris@411 535
Chris@411 536 const RegionModel::Point &p(*i);
Chris@411 537
Chris@411 538 int x = v->getXForFrame(p.frame);
Chris@411 539 int y = getYForValue(v, p.value);
Chris@411 540 int w = v->getXForFrame(p.frame + p.duration) - x;
Chris@411 541 int h = 3;
Chris@411 542
Chris@411 543 if (m_model->getValueQuantization() != 0.0) {
Chris@411 544 h = y - getYForValue(v, p.value + m_model->getValueQuantization());
Chris@411 545 if (h < 3) h = 3;
Chris@411 546 }
Chris@411 547
Chris@411 548 if (w < 1) w = 1;
Chris@411 549 paint.setPen(getBaseQColor());
Chris@411 550 paint.setBrush(brushColour);
Chris@411 551
Chris@411 552 if (illuminateFrame == p.frame) {
Chris@411 553 if (localPos.y() >= y - h && localPos.y() < y) {
Chris@411 554 paint.setPen(v->getForeground());
Chris@411 555 paint.setBrush(v->getForeground());
Chris@411 556 }
Chris@411 557 }
Chris@411 558
Chris@411 559 paint.drawLine(x, y, x + w, y);
Chris@411 560 paint.drawLine(x, y - h/2, x, y + h/2);
Chris@411 561 paint.drawLine(x + w, y - h/2, x + w, y + h/2);
Chris@411 562
Chris@411 563 /// if (p.label != "") {
Chris@411 564 /// paint.drawText(x + 5, y - paint.fontMetrics().height() + paint.fontMetrics().ascent(), p.label);
Chris@411 565 /// }
Chris@411 566 }
Chris@411 567
Chris@411 568 paint.restore();
Chris@411 569 }
Chris@411 570
Chris@411 571 void
Chris@411 572 RegionLayer::drawStart(View *v, QMouseEvent *e)
Chris@411 573 {
Chris@411 574 // std::cerr << "RegionLayer::drawStart(" << e->x() << "," << e->y() << ")" << std::endl;
Chris@411 575
Chris@411 576 if (!m_model) return;
Chris@411 577
Chris@411 578 long frame = v->getFrameForX(e->x());
Chris@411 579 if (frame < 0) frame = 0;
Chris@411 580 frame = frame / m_model->getResolution() * m_model->getResolution();
Chris@411 581
Chris@411 582 float value = getValueForY(v, e->y());
Chris@411 583
Chris@411 584 m_editingPoint = RegionModel::Point(frame, value, 0, tr("New Point"));
Chris@411 585 m_originalPoint = m_editingPoint;
Chris@411 586
Chris@411 587 if (m_editingCommand) finish(m_editingCommand);
Chris@411 588 m_editingCommand = new RegionModel::EditCommand(m_model,
Chris@411 589 tr("Draw Point"));
Chris@411 590 m_editingCommand->addPoint(m_editingPoint);
Chris@411 591
Chris@411 592 m_editing = true;
Chris@411 593 }
Chris@411 594
Chris@411 595 void
Chris@411 596 RegionLayer::drawDrag(View *v, QMouseEvent *e)
Chris@411 597 {
Chris@411 598 // std::cerr << "RegionLayer::drawDrag(" << e->x() << "," << e->y() << ")" << std::endl;
Chris@411 599
Chris@411 600 if (!m_model || !m_editing) return;
Chris@411 601
Chris@411 602 long frame = v->getFrameForX(e->x());
Chris@411 603 if (frame < 0) frame = 0;
Chris@411 604 frame = frame / m_model->getResolution() * m_model->getResolution();
Chris@411 605
Chris@411 606 float newValue = getValueForY(v, e->y());
Chris@411 607
Chris@411 608 long newFrame = m_editingPoint.frame;
Chris@411 609 long newDuration = frame - newFrame;
Chris@411 610 if (newDuration < 0) {
Chris@411 611 newFrame = frame;
Chris@411 612 newDuration = -newDuration;
Chris@411 613 } else if (newDuration == 0) {
Chris@411 614 newDuration = 1;
Chris@411 615 }
Chris@411 616
Chris@411 617 m_editingCommand->deletePoint(m_editingPoint);
Chris@411 618 m_editingPoint.frame = newFrame;
Chris@411 619 m_editingPoint.value = newValue;
Chris@411 620 m_editingPoint.duration = newDuration;
Chris@411 621 m_editingCommand->addPoint(m_editingPoint);
Chris@411 622 }
Chris@411 623
Chris@411 624 void
Chris@411 625 RegionLayer::drawEnd(View *, QMouseEvent *)
Chris@411 626 {
Chris@411 627 // std::cerr << "RegionLayer::drawEnd(" << e->x() << "," << e->y() << ")" << std::endl;
Chris@411 628 if (!m_model || !m_editing) return;
Chris@411 629 finish(m_editingCommand);
Chris@411 630 m_editingCommand = 0;
Chris@411 631 m_editing = false;
Chris@411 632 }
Chris@411 633
Chris@411 634 void
Chris@411 635 RegionLayer::eraseStart(View *v, QMouseEvent *e)
Chris@411 636 {
Chris@411 637 if (!m_model) return;
Chris@411 638
Chris@411 639 RegionModel::PointList points = getLocalPoints(v, e->x());
Chris@411 640 if (points.empty()) return;
Chris@411 641
Chris@411 642 m_editingPoint = *points.begin();
Chris@411 643
Chris@411 644 if (m_editingCommand) {
Chris@411 645 finish(m_editingCommand);
Chris@411 646 m_editingCommand = 0;
Chris@411 647 }
Chris@411 648
Chris@411 649 m_editing = true;
Chris@411 650 }
Chris@411 651
Chris@411 652 void
Chris@411 653 RegionLayer::eraseDrag(View *v, QMouseEvent *e)
Chris@411 654 {
Chris@411 655 }
Chris@411 656
Chris@411 657 void
Chris@411 658 RegionLayer::eraseEnd(View *v, QMouseEvent *e)
Chris@411 659 {
Chris@411 660 if (!m_model || !m_editing) return;
Chris@411 661
Chris@411 662 m_editing = false;
Chris@411 663
Chris@411 664 RegionModel::PointList points = getLocalPoints(v, e->x());
Chris@411 665 if (points.empty()) return;
Chris@411 666 if (points.begin()->frame != m_editingPoint.frame ||
Chris@411 667 points.begin()->value != m_editingPoint.value) return;
Chris@411 668
Chris@411 669 m_editingCommand = new RegionModel::EditCommand
Chris@411 670 (m_model, tr("Erase Point"));
Chris@411 671
Chris@411 672 m_editingCommand->deletePoint(m_editingPoint);
Chris@411 673
Chris@411 674 finish(m_editingCommand);
Chris@411 675 m_editingCommand = 0;
Chris@411 676 m_editing = false;
Chris@411 677 }
Chris@411 678
Chris@411 679 void
Chris@411 680 RegionLayer::editStart(View *v, QMouseEvent *e)
Chris@411 681 {
Chris@411 682 // std::cerr << "RegionLayer::editStart(" << e->x() << "," << e->y() << ")" << std::endl;
Chris@411 683
Chris@411 684 if (!m_model) return;
Chris@411 685
Chris@411 686 RegionModel::PointList points = getLocalPoints(v, e->x());
Chris@411 687 if (points.empty()) return;
Chris@411 688
Chris@411 689 m_editingPoint = *points.begin();
Chris@411 690 m_originalPoint = m_editingPoint;
Chris@411 691
Chris@411 692 if (m_editingCommand) {
Chris@411 693 finish(m_editingCommand);
Chris@411 694 m_editingCommand = 0;
Chris@411 695 }
Chris@411 696
Chris@411 697 m_editing = true;
Chris@411 698 }
Chris@411 699
Chris@411 700 void
Chris@411 701 RegionLayer::editDrag(View *v, QMouseEvent *e)
Chris@411 702 {
Chris@411 703 // std::cerr << "RegionLayer::editDrag(" << e->x() << "," << e->y() << ")" << std::endl;
Chris@411 704
Chris@411 705 if (!m_model || !m_editing) return;
Chris@411 706
Chris@411 707 long frame = v->getFrameForX(e->x());
Chris@411 708 if (frame < 0) frame = 0;
Chris@411 709 frame = frame / m_model->getResolution() * m_model->getResolution();
Chris@411 710
Chris@411 711 float value = getValueForY(v, e->y());
Chris@411 712
Chris@411 713 if (!m_editingCommand) {
Chris@411 714 m_editingCommand = new RegionModel::EditCommand(m_model,
Chris@411 715 tr("Drag Point"));
Chris@411 716 }
Chris@411 717
Chris@411 718 m_editingCommand->deletePoint(m_editingPoint);
Chris@411 719 m_editingPoint.frame = frame;
Chris@411 720 m_editingPoint.value = value;
Chris@411 721 m_editingCommand->addPoint(m_editingPoint);
Chris@411 722 }
Chris@411 723
Chris@411 724 void
Chris@411 725 RegionLayer::editEnd(View *, QMouseEvent *)
Chris@411 726 {
Chris@411 727 // std::cerr << "RegionLayer::editEnd(" << e->x() << "," << e->y() << ")" << std::endl;
Chris@411 728 if (!m_model || !m_editing) return;
Chris@411 729
Chris@411 730 if (m_editingCommand) {
Chris@411 731
Chris@411 732 QString newName = m_editingCommand->getName();
Chris@411 733
Chris@411 734 if (m_editingPoint.frame != m_originalPoint.frame) {
Chris@411 735 if (m_editingPoint.value != m_originalPoint.value) {
Chris@411 736 newName = tr("Edit Point");
Chris@411 737 } else {
Chris@411 738 newName = tr("Relocate Point");
Chris@411 739 }
Chris@411 740 } else {
Chris@411 741 newName = tr("Change Point Value");
Chris@411 742 }
Chris@411 743
Chris@411 744 m_editingCommand->setName(newName);
Chris@411 745 finish(m_editingCommand);
Chris@411 746 }
Chris@411 747
Chris@411 748 m_editingCommand = 0;
Chris@411 749 m_editing = false;
Chris@411 750 }
Chris@411 751
Chris@411 752 bool
Chris@411 753 RegionLayer::editOpen(View *v, QMouseEvent *e)
Chris@411 754 {
Chris@411 755 if (!m_model) return false;
Chris@411 756
Chris@411 757 RegionModel::PointList points = getLocalPoints(v, e->x());
Chris@411 758 if (points.empty()) return false;
Chris@411 759
Chris@411 760 RegionModel::Point region = *points.begin();
Chris@411 761
Chris@411 762 ItemEditDialog *dialog = new ItemEditDialog
Chris@411 763 (m_model->getSampleRate(),
Chris@411 764 ItemEditDialog::ShowTime |
Chris@411 765 ItemEditDialog::ShowDuration |
Chris@411 766 ItemEditDialog::ShowValue |
Chris@411 767 ItemEditDialog::ShowText,
Chris@411 768 m_model->getScaleUnits());
Chris@411 769
Chris@411 770 dialog->setFrameTime(region.frame);
Chris@411 771 dialog->setValue(region.value);
Chris@411 772 dialog->setFrameDuration(region.duration);
Chris@411 773 dialog->setText(region.label);
Chris@411 774
Chris@411 775 if (dialog->exec() == QDialog::Accepted) {
Chris@411 776
Chris@411 777 RegionModel::Point newRegion = region;
Chris@411 778 newRegion.frame = dialog->getFrameTime();
Chris@411 779 newRegion.value = dialog->getValue();
Chris@411 780 newRegion.duration = dialog->getFrameDuration();
Chris@411 781 newRegion.label = dialog->getText();
Chris@411 782
Chris@411 783 RegionModel::EditCommand *command = new RegionModel::EditCommand
Chris@411 784 (m_model, tr("Edit Point"));
Chris@411 785 command->deletePoint(region);
Chris@411 786 command->addPoint(newRegion);
Chris@411 787 finish(command);
Chris@411 788 }
Chris@411 789
Chris@411 790 delete dialog;
Chris@411 791 return true;
Chris@411 792 }
Chris@411 793
Chris@411 794 void
Chris@411 795 RegionLayer::moveSelection(Selection s, size_t newStartFrame)
Chris@411 796 {
Chris@411 797 if (!m_model) return;
Chris@411 798
Chris@411 799 RegionModel::EditCommand *command =
Chris@411 800 new RegionModel::EditCommand(m_model, tr("Drag Selection"));
Chris@411 801
Chris@411 802 RegionModel::PointList points =
Chris@411 803 m_model->getPoints(s.getStartFrame(), s.getEndFrame());
Chris@411 804
Chris@411 805 for (RegionModel::PointList::iterator i = points.begin();
Chris@411 806 i != points.end(); ++i) {
Chris@411 807
Chris@411 808 if (s.contains(i->frame)) {
Chris@411 809 RegionModel::Point newPoint(*i);
Chris@411 810 newPoint.frame = i->frame + newStartFrame - s.getStartFrame();
Chris@411 811 command->deletePoint(*i);
Chris@411 812 command->addPoint(newPoint);
Chris@411 813 }
Chris@411 814 }
Chris@411 815
Chris@411 816 finish(command);
Chris@411 817 }
Chris@411 818
Chris@411 819 void
Chris@411 820 RegionLayer::resizeSelection(Selection s, Selection newSize)
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("Resize 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 double ratio =
Chris@411 831 double(newSize.getEndFrame() - newSize.getStartFrame()) /
Chris@411 832 double(s.getEndFrame() - s.getStartFrame());
Chris@411 833
Chris@411 834 for (RegionModel::PointList::iterator i = points.begin();
Chris@411 835 i != points.end(); ++i) {
Chris@411 836
Chris@411 837 if (s.contains(i->frame)) {
Chris@411 838
Chris@411 839 double targetStart = i->frame;
Chris@411 840 targetStart = newSize.getStartFrame() +
Chris@411 841 double(targetStart - s.getStartFrame()) * ratio;
Chris@411 842
Chris@411 843 double targetEnd = i->frame + i->duration;
Chris@411 844 targetEnd = newSize.getStartFrame() +
Chris@411 845 double(targetEnd - s.getStartFrame()) * ratio;
Chris@411 846
Chris@411 847 RegionModel::Point newPoint(*i);
Chris@411 848 newPoint.frame = lrint(targetStart);
Chris@411 849 newPoint.duration = lrint(targetEnd - targetStart);
Chris@411 850 command->deletePoint(*i);
Chris@411 851 command->addPoint(newPoint);
Chris@411 852 }
Chris@411 853 }
Chris@411 854
Chris@411 855 finish(command);
Chris@411 856 }
Chris@411 857
Chris@411 858 void
Chris@411 859 RegionLayer::deleteSelection(Selection s)
Chris@411 860 {
Chris@411 861 if (!m_model) return;
Chris@411 862
Chris@411 863 RegionModel::EditCommand *command =
Chris@411 864 new RegionModel::EditCommand(m_model, tr("Delete Selected Points"));
Chris@411 865
Chris@411 866 RegionModel::PointList points =
Chris@411 867 m_model->getPoints(s.getStartFrame(), s.getEndFrame());
Chris@411 868
Chris@411 869 for (RegionModel::PointList::iterator i = points.begin();
Chris@411 870 i != points.end(); ++i) {
Chris@411 871
Chris@411 872 if (s.contains(i->frame)) {
Chris@411 873 command->deletePoint(*i);
Chris@411 874 }
Chris@411 875 }
Chris@411 876
Chris@411 877 finish(command);
Chris@411 878 }
Chris@411 879
Chris@411 880 void
Chris@411 881 RegionLayer::copy(View *v, Selection s, Clipboard &to)
Chris@411 882 {
Chris@411 883 if (!m_model) return;
Chris@411 884
Chris@411 885 RegionModel::PointList points =
Chris@411 886 m_model->getPoints(s.getStartFrame(), s.getEndFrame());
Chris@411 887
Chris@411 888 for (RegionModel::PointList::iterator i = points.begin();
Chris@411 889 i != points.end(); ++i) {
Chris@411 890 if (s.contains(i->frame)) {
Chris@411 891 Clipboard::Point point(i->frame, i->value, i->duration, i->label);
Chris@411 892 point.setReferenceFrame(alignToReference(v, i->frame));
Chris@411 893 to.addPoint(point);
Chris@411 894 }
Chris@411 895 }
Chris@411 896 }
Chris@411 897
Chris@411 898 bool
Chris@411 899 RegionLayer::paste(View *v, const Clipboard &from, int frameOffset, bool /* interactive */)
Chris@411 900 {
Chris@411 901 if (!m_model) return false;
Chris@411 902
Chris@411 903 const Clipboard::PointList &points = from.getPoints();
Chris@411 904
Chris@411 905 bool realign = false;
Chris@411 906
Chris@411 907 if (clipboardHasDifferentAlignment(v, from)) {
Chris@411 908
Chris@411 909 QMessageBox::StandardButton button =
Chris@411 910 QMessageBox::question(v, tr("Re-align pasted items?"),
Chris@411 911 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 912 QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel,
Chris@411 913 QMessageBox::Yes);
Chris@411 914
Chris@411 915 if (button == QMessageBox::Cancel) {
Chris@411 916 return false;
Chris@411 917 }
Chris@411 918
Chris@411 919 if (button == QMessageBox::Yes) {
Chris@411 920 realign = true;
Chris@411 921 }
Chris@411 922 }
Chris@411 923
Chris@411 924 RegionModel::EditCommand *command =
Chris@411 925 new RegionModel::EditCommand(m_model, tr("Paste"));
Chris@411 926
Chris@411 927 for (Clipboard::PointList::const_iterator i = points.begin();
Chris@411 928 i != points.end(); ++i) {
Chris@411 929
Chris@411 930 if (!i->haveFrame()) continue;
Chris@411 931 size_t frame = 0;
Chris@411 932
Chris@411 933 if (!realign) {
Chris@411 934
Chris@411 935 frame = i->getFrame();
Chris@411 936
Chris@411 937 } else {
Chris@411 938
Chris@411 939 if (i->haveReferenceFrame()) {
Chris@411 940 frame = i->getReferenceFrame();
Chris@411 941 frame = alignFromReference(v, frame);
Chris@411 942 } else {
Chris@411 943 frame = i->getFrame();
Chris@411 944 }
Chris@411 945 }
Chris@411 946
Chris@411 947 RegionModel::Point newPoint(frame);
Chris@411 948
Chris@411 949 if (i->haveLabel()) newPoint.label = i->getLabel();
Chris@411 950 if (i->haveValue()) newPoint.value = i->getValue();
Chris@411 951 else newPoint.value = (m_model->getValueMinimum() +
Chris@411 952 m_model->getValueMaximum()) / 2;
Chris@411 953 if (i->haveDuration()) newPoint.duration = i->getDuration();
Chris@411 954 else {
Chris@411 955 size_t nextFrame = frame;
Chris@411 956 Clipboard::PointList::const_iterator j = i;
Chris@411 957 for (; j != points.end(); ++j) {
Chris@411 958 if (!j->haveFrame()) continue;
Chris@411 959 if (j != i) break;
Chris@411 960 }
Chris@411 961 if (j != points.end()) {
Chris@411 962 nextFrame = j->getFrame();
Chris@411 963 }
Chris@411 964 if (nextFrame == frame) {
Chris@411 965 newPoint.duration = m_model->getResolution();
Chris@411 966 } else {
Chris@411 967 newPoint.duration = nextFrame - frame;
Chris@411 968 }
Chris@411 969 }
Chris@411 970
Chris@411 971 command->addPoint(newPoint);
Chris@411 972 }
Chris@411 973
Chris@411 974 finish(command);
Chris@411 975 return true;
Chris@411 976 }
Chris@411 977
Chris@411 978 int
Chris@411 979 RegionLayer::getDefaultColourHint(bool darkbg, bool &impose)
Chris@411 980 {
Chris@411 981 impose = false;
Chris@411 982 return ColourDatabase::getInstance()->getColourIndex
Chris@411 983 (QString(darkbg ? "White" : "Black"));
Chris@411 984 }
Chris@411 985
Chris@411 986 void
Chris@411 987 RegionLayer::toXml(QTextStream &stream,
Chris@411 988 QString indent, QString extraAttributes) const
Chris@411 989 {
Chris@411 990 SingleColourLayer::toXml(stream, indent, extraAttributes +
Chris@412 991 QString(" verticalScale=\"%1\" plotStyle=\"%2\"")
Chris@412 992 .arg(m_verticalScale)
Chris@412 993 .arg(m_plotStyle));
Chris@411 994 }
Chris@411 995
Chris@411 996 void
Chris@411 997 RegionLayer::setProperties(const QXmlAttributes &attributes)
Chris@411 998 {
Chris@411 999 SingleColourLayer::setProperties(attributes);
Chris@411 1000
Chris@411 1001 bool ok;
Chris@411 1002 VerticalScale scale = (VerticalScale)
Chris@411 1003 attributes.value("verticalScale").toInt(&ok);
Chris@411 1004 if (ok) setVerticalScale(scale);
Chris@412 1005 PlotStyle style = (PlotStyle)
Chris@412 1006 attributes.value("plotStyle").toInt(&ok);
Chris@412 1007 if (ok) setPlotStyle(style);
Chris@411 1008 }
Chris@411 1009
Chris@411 1010