annotate layer/RegionLayer.cpp @ 411:96e4d7b9e165

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