annotate layer/RegionLayer.cpp @ 423:5a55d0683ee5

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