annotate layer/RegionLayer.cpp @ 413:f71752646f97

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