annotate layer/RegionLayer.cpp @ 590:241929c5d57c sonification

Check the scale width in the View (which has access to it); ask the layer to do something when the user clicks in the scale regardless of the edit mode, and continue with normal processing if the layer has nothing interesting to do
author Chris Cannam
date Fri, 24 Jun 2011 14:27:32 +0100
parents 4806715f7a19
children ece1af8cfffa
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@427 23 #include "ColourMapper.h"
Chris@411 24 #include "view/View.h"
Chris@411 25
Chris@411 26 #include "data/model/RegionModel.h"
Chris@411 27
Chris@411 28 #include "widgets/ItemEditDialog.h"
Chris@411 29
Chris@411 30 #include <QPainter>
Chris@411 31 #include <QPainterPath>
Chris@411 32 #include <QMouseEvent>
Chris@411 33 #include <QTextStream>
Chris@411 34 #include <QMessageBox>
Chris@411 35
Chris@411 36 #include <iostream>
Chris@411 37 #include <cmath>
Chris@411 38
Chris@411 39 RegionLayer::RegionLayer() :
Chris@411 40 SingleColourLayer(),
Chris@411 41 m_model(0),
Chris@411 42 m_editing(false),
Chris@543 43 m_originalPoint(0, 0.0, 0, tr("New Region")),
Chris@543 44 m_editingPoint(0, 0.0, 0, tr("New Region")),
Chris@411 45 m_editingCommand(0),
Chris@433 46 m_verticalScale(EqualSpaced),
Chris@427 47 m_colourMap(0),
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@433 61 connect(m_model, SIGNAL(modelChanged()), this, SLOT(recalcSpacing()));
Chris@433 62 recalcSpacing();
Chris@433 63
Chris@587 64 // SVDEBUG << "RegionLayer::setModel(" << model << ")" << endl;
Chris@411 65
Chris@411 66 emit modelReplaced();
Chris@411 67 }
Chris@411 68
Chris@411 69 Layer::PropertyList
Chris@411 70 RegionLayer::getProperties() const
Chris@411 71 {
Chris@411 72 PropertyList list = SingleColourLayer::getProperties();
Chris@411 73 list.push_back("Vertical Scale");
Chris@411 74 list.push_back("Scale Units");
Chris@412 75 list.push_back("Plot Type");
Chris@411 76 return list;
Chris@411 77 }
Chris@411 78
Chris@411 79 QString
Chris@411 80 RegionLayer::getPropertyLabel(const PropertyName &name) const
Chris@411 81 {
Chris@411 82 if (name == "Vertical Scale") return tr("Vertical Scale");
Chris@411 83 if (name == "Scale Units") return tr("Scale Units");
Chris@412 84 if (name == "Plot Type") return tr("Plot Type");
Chris@411 85 return SingleColourLayer::getPropertyLabel(name);
Chris@411 86 }
Chris@411 87
Chris@411 88 Layer::PropertyType
Chris@411 89 RegionLayer::getPropertyType(const PropertyName &name) const
Chris@411 90 {
Chris@411 91 if (name == "Scale Units") return UnitsProperty;
Chris@411 92 if (name == "Vertical Scale") return ValueProperty;
Chris@412 93 if (name == "Plot Type") return ValueProperty;
Chris@427 94 if (name == "Colour" && m_plotStyle == PlotSegmentation) return ValueProperty;
Chris@411 95 return SingleColourLayer::getPropertyType(name);
Chris@411 96 }
Chris@411 97
Chris@411 98 QString
Chris@411 99 RegionLayer::getPropertyGroupName(const PropertyName &name) const
Chris@411 100 {
Chris@411 101 if (name == "Vertical Scale" || name == "Scale Units") {
Chris@411 102 return tr("Scale");
Chris@411 103 }
Chris@411 104 return SingleColourLayer::getPropertyGroupName(name);
Chris@411 105 }
Chris@411 106
Chris@411 107 int
Chris@411 108 RegionLayer::getPropertyRangeAndValue(const PropertyName &name,
Chris@411 109 int *min, int *max, int *deflt) const
Chris@411 110 {
Chris@411 111 int val = 0;
Chris@411 112
Chris@427 113 if (name == "Colour" && m_plotStyle == PlotSegmentation) {
Chris@427 114
Chris@427 115 if (min) *min = 0;
Chris@427 116 if (max) *max = ColourMapper::getColourMapCount() - 1;
Chris@427 117 if (deflt) *deflt = 0;
Chris@427 118
Chris@427 119 val = m_colourMap;
Chris@427 120
Chris@427 121 } else if (name == "Plot Type") {
Chris@412 122
Chris@412 123 if (min) *min = 0;
Chris@412 124 if (max) *max = 1;
Chris@412 125 if (deflt) *deflt = 0;
Chris@412 126
Chris@412 127 val = int(m_plotStyle);
Chris@412 128
Chris@412 129 } else if (name == "Vertical Scale") {
Chris@411 130
Chris@411 131 if (min) *min = 0;
Chris@411 132 if (max) *max = 3;
Chris@433 133 if (deflt) *deflt = int(EqualSpaced);
Chris@411 134
Chris@411 135 val = int(m_verticalScale);
Chris@411 136
Chris@411 137 } else if (name == "Scale Units") {
Chris@411 138
Chris@411 139 if (deflt) *deflt = 0;
Chris@411 140 if (m_model) {
Chris@411 141 val = UnitDatabase::getInstance()->getUnitId
Chris@411 142 (m_model->getScaleUnits());
Chris@411 143 }
Chris@411 144
Chris@411 145 } else {
Chris@411 146
Chris@411 147 val = SingleColourLayer::getPropertyRangeAndValue(name, min, max, deflt);
Chris@411 148 }
Chris@411 149
Chris@411 150 return val;
Chris@411 151 }
Chris@411 152
Chris@411 153 QString
Chris@411 154 RegionLayer::getPropertyValueLabel(const PropertyName &name,
Chris@427 155 int value) const
Chris@411 156 {
Chris@427 157 if (name == "Colour" && m_plotStyle == PlotSegmentation) {
Chris@427 158 return ColourMapper::getColourMapName(value);
Chris@427 159 } else if (name == "Plot Type") {
Chris@412 160
Chris@412 161 switch (value) {
Chris@412 162 default:
Chris@427 163 case 0: return tr("Bars");
Chris@412 164 case 1: return tr("Segmentation");
Chris@412 165 }
Chris@412 166
Chris@412 167 } else if (name == "Vertical Scale") {
Chris@411 168 switch (value) {
Chris@411 169 default:
Chris@411 170 case 0: return tr("Auto-Align");
Chris@433 171 case 1: return tr("Equal Spaced");
Chris@433 172 case 2: return tr("Linear");
Chris@433 173 case 3: return tr("Log");
Chris@411 174 }
Chris@411 175 }
Chris@411 176 return SingleColourLayer::getPropertyValueLabel(name, value);
Chris@411 177 }
Chris@411 178
Chris@411 179 void
Chris@411 180 RegionLayer::setProperty(const PropertyName &name, int value)
Chris@411 181 {
Chris@427 182 if (name == "Colour" && m_plotStyle == PlotSegmentation) {
Chris@427 183 setFillColourMap(value);
Chris@427 184 } else if (name == "Plot Type") {
Chris@412 185 setPlotStyle(PlotStyle(value));
Chris@412 186 } else if (name == "Vertical Scale") {
Chris@411 187 setVerticalScale(VerticalScale(value));
Chris@411 188 } else if (name == "Scale Units") {
Chris@411 189 if (m_model) {
Chris@411 190 m_model->setScaleUnits
Chris@411 191 (UnitDatabase::getInstance()->getUnitById(value));
Chris@411 192 emit modelChanged();
Chris@411 193 }
Chris@411 194 } else {
Chris@411 195 return SingleColourLayer::setProperty(name, value);
Chris@411 196 }
Chris@411 197 }
Chris@411 198
Chris@411 199 void
Chris@427 200 RegionLayer::setFillColourMap(int map)
Chris@427 201 {
Chris@427 202 if (m_colourMap == map) return;
Chris@427 203 m_colourMap = map;
Chris@427 204 emit layerParametersChanged();
Chris@427 205 }
Chris@427 206
Chris@427 207 void
Chris@412 208 RegionLayer::setPlotStyle(PlotStyle style)
Chris@412 209 {
Chris@412 210 if (m_plotStyle == style) return;
Chris@427 211 bool colourTypeChanged = (style == PlotSegmentation ||
Chris@427 212 m_plotStyle == PlotSegmentation);
Chris@412 213 m_plotStyle = style;
Chris@427 214 if (colourTypeChanged) {
Chris@427 215 emit layerParameterRangesChanged();
Chris@427 216 }
Chris@412 217 emit layerParametersChanged();
Chris@412 218 }
Chris@412 219
Chris@412 220 void
Chris@411 221 RegionLayer::setVerticalScale(VerticalScale scale)
Chris@411 222 {
Chris@411 223 if (m_verticalScale == scale) return;
Chris@411 224 m_verticalScale = scale;
Chris@411 225 emit layerParametersChanged();
Chris@411 226 }
Chris@411 227
Chris@411 228 bool
Chris@411 229 RegionLayer::isLayerScrollable(const View *v) const
Chris@411 230 {
Chris@411 231 QPoint discard;
Chris@411 232 return !v->shouldIlluminateLocalFeatures(this, discard);
Chris@411 233 }
Chris@411 234
Chris@433 235 void
Chris@433 236 RegionLayer::recalcSpacing()
Chris@433 237 {
Chris@433 238 m_spacingMap.clear();
Chris@551 239 m_distributionMap.clear();
Chris@433 240 if (!m_model) return;
Chris@433 241
Chris@587 242 // SVDEBUG << "RegionLayer::recalcSpacing" << endl;
Chris@433 243
Chris@433 244 for (RegionModel::PointList::const_iterator i = m_model->getPoints().begin();
Chris@433 245 i != m_model->getPoints().end(); ++i) {
Chris@551 246 m_distributionMap[i->value]++;
Chris@587 247 // SVDEBUG << "RegionLayer::recalcSpacing: value found: " << i->value << " (now have " << m_distributionMap[i->value] << " of this value)" << endl;
Chris@433 248 }
Chris@433 249
Chris@433 250 int n = 0;
Chris@433 251
Chris@551 252 for (SpacingMap::const_iterator i = m_distributionMap.begin();
Chris@551 253 i != m_distributionMap.end(); ++i) {
Chris@551 254 m_spacingMap[i->first] = n++;
Chris@587 255 // SVDEBUG << "RegionLayer::recalcSpacing: " << i->first << " -> " << m_spacingMap[i->first] << endl;
Chris@433 256 }
Chris@433 257 }
Chris@433 258
Chris@411 259 bool
Chris@411 260 RegionLayer::getValueExtents(float &min, float &max,
Chris@411 261 bool &logarithmic, QString &unit) const
Chris@411 262 {
Chris@411 263 if (!m_model) return false;
Chris@411 264 min = m_model->getValueMinimum();
Chris@411 265 max = m_model->getValueMaximum();
Chris@411 266 unit = m_model->getScaleUnits();
Chris@411 267
Chris@411 268 if (m_verticalScale == LogScale) logarithmic = true;
Chris@411 269
Chris@411 270 return true;
Chris@411 271 }
Chris@411 272
Chris@411 273 bool
Chris@411 274 RegionLayer::getDisplayExtents(float &min, float &max) const
Chris@411 275 {
Chris@433 276 if (!m_model ||
Chris@433 277 m_verticalScale == AutoAlignScale ||
Chris@433 278 m_verticalScale == EqualSpaced) return false;
Chris@411 279
Chris@411 280 min = m_model->getValueMinimum();
Chris@411 281 max = m_model->getValueMaximum();
Chris@411 282
Chris@411 283 return true;
Chris@411 284 }
Chris@411 285
Chris@411 286 RegionModel::PointList
Chris@411 287 RegionLayer::getLocalPoints(View *v, int x) const
Chris@411 288 {
Chris@411 289 if (!m_model) return RegionModel::PointList();
Chris@411 290
Chris@411 291 long frame = v->getFrameForX(x);
Chris@411 292
Chris@411 293 RegionModel::PointList onPoints =
Chris@411 294 m_model->getPoints(frame);
Chris@411 295
Chris@411 296 if (!onPoints.empty()) {
Chris@411 297 return onPoints;
Chris@411 298 }
Chris@411 299
Chris@411 300 RegionModel::PointList prevPoints =
Chris@411 301 m_model->getPreviousPoints(frame);
Chris@411 302 RegionModel::PointList nextPoints =
Chris@411 303 m_model->getNextPoints(frame);
Chris@411 304
Chris@411 305 RegionModel::PointList usePoints = prevPoints;
Chris@411 306
Chris@411 307 if (prevPoints.empty()) {
Chris@411 308 usePoints = nextPoints;
Chris@411 309 } else if (long(prevPoints.begin()->frame) < v->getStartFrame() &&
Chris@411 310 !(nextPoints.begin()->frame > v->getEndFrame())) {
Chris@411 311 usePoints = nextPoints;
Chris@411 312 } else if (long(nextPoints.begin()->frame) - frame <
Chris@411 313 frame - long(prevPoints.begin()->frame)) {
Chris@411 314 usePoints = nextPoints;
Chris@411 315 }
Chris@411 316
Chris@411 317 if (!usePoints.empty()) {
Chris@411 318 int fuzz = 2;
Chris@411 319 int px = v->getXForFrame(usePoints.begin()->frame);
Chris@411 320 if ((px > x && px - x > fuzz) ||
Chris@411 321 (px < x && x - px > fuzz + 1)) {
Chris@411 322 usePoints.clear();
Chris@411 323 }
Chris@411 324 }
Chris@411 325
Chris@411 326 return usePoints;
Chris@411 327 }
Chris@411 328
Chris@550 329 bool
Chris@550 330 RegionLayer::getPointToDrag(View *v, int x, int y, RegionModel::Point &p) const
Chris@550 331 {
Chris@550 332 if (!m_model) return false;
Chris@550 333
Chris@550 334 long frame = v->getFrameForX(x);
Chris@550 335
Chris@550 336 RegionModel::PointList onPoints = m_model->getPoints(frame);
Chris@550 337 if (onPoints.empty()) return false;
Chris@550 338
Chris@550 339 int nearestDistance = -1;
Chris@550 340
Chris@550 341 for (RegionModel::PointList::const_iterator i = onPoints.begin();
Chris@550 342 i != onPoints.end(); ++i) {
Chris@550 343
Chris@550 344 int distance = getYForValue(v, (*i).value) - y;
Chris@550 345 if (distance < 0) distance = -distance;
Chris@550 346 if (nearestDistance == -1 || distance < nearestDistance) {
Chris@550 347 nearestDistance = distance;
Chris@550 348 p = *i;
Chris@550 349 }
Chris@550 350 }
Chris@550 351
Chris@550 352 return true;
Chris@550 353 }
Chris@550 354
Chris@411 355 QString
Chris@552 356 RegionLayer::getLabelPreceding(size_t frame) const
Chris@552 357 {
Chris@552 358 if (!m_model) return "";
Chris@552 359 RegionModel::PointList points = m_model->getPreviousPoints(frame);
Chris@552 360 for (RegionModel::PointList::const_iterator i = points.begin();
Chris@552 361 i != points.end(); ++i) {
Chris@552 362 if (i->label != "") return i->label;
Chris@552 363 }
Chris@552 364 return "";
Chris@552 365 }
Chris@552 366
Chris@552 367 QString
Chris@411 368 RegionLayer::getFeatureDescription(View *v, QPoint &pos) const
Chris@411 369 {
Chris@411 370 int x = pos.x();
Chris@411 371
Chris@411 372 if (!m_model || !m_model->getSampleRate()) return "";
Chris@411 373
Chris@411 374 RegionModel::PointList points = getLocalPoints(v, x);
Chris@411 375
Chris@411 376 if (points.empty()) {
Chris@411 377 if (!m_model->isReady()) {
Chris@411 378 return tr("In progress");
Chris@411 379 } else {
Chris@411 380 return tr("No local points");
Chris@411 381 }
Chris@411 382 }
Chris@411 383
Chris@411 384 RegionRec region(0);
Chris@411 385 RegionModel::PointList::iterator i;
Chris@411 386
Chris@413 387 //!!! harmonise with whatever decision is made about point y
Chris@413 388 //!!! coords in paint method
Chris@413 389
Chris@411 390 for (i = points.begin(); i != points.end(); ++i) {
Chris@411 391
Chris@411 392 int y = getYForValue(v, i->value);
Chris@411 393 int h = 3;
Chris@411 394
Chris@411 395 if (m_model->getValueQuantization() != 0.0) {
Chris@411 396 h = y - getYForValue(v, i->value + m_model->getValueQuantization());
Chris@411 397 if (h < 3) h = 3;
Chris@411 398 }
Chris@411 399
Chris@411 400 if (pos.y() >= y - h && pos.y() <= y) {
Chris@411 401 region = *i;
Chris@411 402 break;
Chris@411 403 }
Chris@411 404 }
Chris@411 405
Chris@411 406 if (i == points.end()) return tr("No local points");
Chris@411 407
Chris@411 408 RealTime rt = RealTime::frame2RealTime(region.frame,
Chris@411 409 m_model->getSampleRate());
Chris@411 410 RealTime rd = RealTime::frame2RealTime(region.duration,
Chris@411 411 m_model->getSampleRate());
Chris@411 412
Chris@411 413 QString valueText;
Chris@411 414
Chris@411 415 valueText = tr("%1 %2").arg(region.value).arg(m_model->getScaleUnits());
Chris@411 416
Chris@411 417 QString text;
Chris@411 418
Chris@411 419 if (region.label == "") {
Chris@411 420 text = QString(tr("Time:\t%1\nValue:\t%2\nDuration:\t%3\nNo label"))
Chris@411 421 .arg(rt.toText(true).c_str())
Chris@411 422 .arg(valueText)
Chris@411 423 .arg(rd.toText(true).c_str());
Chris@411 424 } else {
Chris@411 425 text = QString(tr("Time:\t%1\nValue:\t%2\nDuration:\t%3\nLabel:\t%4"))
Chris@411 426 .arg(rt.toText(true).c_str())
Chris@411 427 .arg(valueText)
Chris@411 428 .arg(rd.toText(true).c_str())
Chris@411 429 .arg(region.label);
Chris@411 430 }
Chris@411 431
Chris@411 432 pos = QPoint(v->getXForFrame(region.frame),
Chris@411 433 getYForValue(v, region.value));
Chris@411 434 return text;
Chris@411 435 }
Chris@411 436
Chris@411 437 bool
Chris@411 438 RegionLayer::snapToFeatureFrame(View *v, int &frame,
Chris@414 439 size_t &resolution,
Chris@414 440 SnapType snap) const
Chris@411 441 {
Chris@411 442 if (!m_model) {
Chris@411 443 return Layer::snapToFeatureFrame(v, frame, resolution, snap);
Chris@411 444 }
Chris@411 445
Chris@411 446 resolution = m_model->getResolution();
Chris@411 447 RegionModel::PointList points;
Chris@411 448
Chris@411 449 if (snap == SnapNeighbouring) {
Chris@411 450
Chris@411 451 points = getLocalPoints(v, v->getXForFrame(frame));
Chris@411 452 if (points.empty()) return false;
Chris@411 453 frame = points.begin()->frame;
Chris@411 454 return true;
Chris@411 455 }
Chris@411 456
Chris@411 457 points = m_model->getPoints(frame, frame);
Chris@411 458 int snapped = frame;
Chris@411 459 bool found = false;
Chris@411 460
Chris@411 461 for (RegionModel::PointList::const_iterator i = points.begin();
Chris@411 462 i != points.end(); ++i) {
Chris@411 463
Chris@411 464 if (snap == SnapRight) {
Chris@411 465
Chris@414 466 // The best frame to snap to is the end frame of whichever
Chris@414 467 // feature we would have snapped to the start frame of if
Chris@414 468 // we had been snapping left.
Chris@414 469
Chris@414 470 if (i->frame <= frame) {
Chris@414 471 if (i->frame + i->duration > frame) {
Chris@414 472 snapped = i->frame + i->duration;
Chris@414 473 found = true; // don't break, as the next may be better
Chris@414 474 }
Chris@414 475 } else {
Chris@414 476 if (!found) {
Chris@414 477 snapped = i->frame;
Chris@414 478 found = true;
Chris@414 479 }
Chris@414 480 break;
Chris@414 481 }
Chris@411 482
Chris@411 483 } else if (snap == SnapLeft) {
Chris@411 484
Chris@411 485 if (i->frame <= frame) {
Chris@411 486 snapped = i->frame;
Chris@411 487 found = true; // don't break, as the next may be better
Chris@411 488 } else {
Chris@411 489 break;
Chris@411 490 }
Chris@411 491
Chris@411 492 } else { // nearest
Chris@411 493
Chris@411 494 RegionModel::PointList::const_iterator j = i;
Chris@411 495 ++j;
Chris@411 496
Chris@411 497 if (j == points.end()) {
Chris@411 498
Chris@411 499 snapped = i->frame;
Chris@411 500 found = true;
Chris@411 501 break;
Chris@411 502
Chris@411 503 } else if (j->frame >= frame) {
Chris@411 504
Chris@411 505 if (j->frame - frame < frame - i->frame) {
Chris@411 506 snapped = j->frame;
Chris@411 507 } else {
Chris@411 508 snapped = i->frame;
Chris@411 509 }
Chris@411 510 found = true;
Chris@411 511 break;
Chris@411 512 }
Chris@411 513 }
Chris@411 514 }
Chris@411 515
Chris@411 516 frame = snapped;
Chris@411 517 return found;
Chris@411 518 }
Chris@411 519
Chris@559 520 bool
Chris@559 521 RegionLayer::snapToSimilarFeature(View *v, int &frame,
Chris@559 522 size_t &resolution,
Chris@559 523 SnapType snap) const
Chris@559 524 {
Chris@559 525 if (!m_model) {
Chris@559 526 return Layer::snapToSimilarFeature(v, frame, resolution, snap);
Chris@559 527 }
Chris@559 528
Chris@559 529 resolution = m_model->getResolution();
Chris@559 530
Chris@559 531 const RegionModel::PointList &points = m_model->getPoints();
Chris@559 532 RegionModel::PointList close = m_model->getPoints(frame, frame);
Chris@559 533
Chris@559 534 RegionModel::PointList::const_iterator i;
Chris@559 535
Chris@559 536 int matchframe = frame;
Chris@559 537 float matchvalue = 0.f;
Chris@559 538
Chris@559 539 for (i = close.begin(); i != close.end(); ++i) {
Chris@559 540 if (i->frame > frame) break;
Chris@559 541 matchvalue = i->value;
Chris@559 542 matchframe = i->frame;
Chris@559 543 }
Chris@559 544
Chris@559 545 int snapped = frame;
Chris@559 546 bool found = false;
Chris@559 547 bool distant = false;
Chris@559 548 float epsilon = 0.0001;
Chris@559 549
Chris@559 550 i = close.begin();
Chris@559 551
Chris@559 552 // Scan through the close points first, then the more distant ones
Chris@559 553 // if no suitable close one is found
Chris@559 554
Chris@559 555 while (i != points.end()) {
Chris@559 556
Chris@559 557 if (i == close.end()) {
Chris@559 558 i = points.begin();
Chris@559 559 distant = true;
Chris@559 560 }
Chris@559 561
Chris@559 562 if (snap == SnapRight) {
Chris@559 563
Chris@559 564 if (i->frame > matchframe &&
Chris@559 565 fabsf(i->value - matchvalue) < epsilon) {
Chris@559 566 snapped = i->frame;
Chris@559 567 found = true;
Chris@559 568 break;
Chris@559 569 }
Chris@559 570
Chris@559 571 } else if (snap == SnapLeft) {
Chris@559 572
Chris@559 573 if (i->frame < matchframe) {
Chris@559 574 if (fabsf(i->value - matchvalue) < epsilon) {
Chris@559 575 snapped = i->frame;
Chris@559 576 found = true; // don't break, as the next may be better
Chris@559 577 }
Chris@559 578 } else if (found || distant) {
Chris@559 579 break;
Chris@559 580 }
Chris@559 581
Chris@559 582 } else {
Chris@559 583 // no other snap types supported
Chris@559 584 }
Chris@559 585
Chris@559 586 ++i;
Chris@559 587 }
Chris@559 588
Chris@559 589 frame = snapped;
Chris@559 590 return found;
Chris@559 591 }
Chris@559 592
Chris@411 593 void
Chris@411 594 RegionLayer::getScaleExtents(View *v, float &min, float &max, bool &log) const
Chris@411 595 {
Chris@411 596 min = 0.0;
Chris@411 597 max = 0.0;
Chris@411 598 log = false;
Chris@411 599
Chris@411 600 QString queryUnits;
Chris@411 601 queryUnits = m_model->getScaleUnits();
Chris@411 602
Chris@411 603 if (m_verticalScale == AutoAlignScale) {
Chris@411 604
Chris@411 605 if (!v->getValueExtents(queryUnits, min, max, log)) {
Chris@411 606
Chris@411 607 min = m_model->getValueMinimum();
Chris@411 608 max = m_model->getValueMaximum();
Chris@411 609
Chris@542 610 // std::cerr << "RegionLayer[" << this << "]::getScaleExtents: min = " << min << ", max = " << max << ", log = " << log << std::endl;
Chris@411 611
Chris@411 612 } else if (log) {
Chris@411 613
Chris@411 614 LogRange::mapRange(min, max);
Chris@411 615
Chris@542 616 // std::cerr << "RegionLayer[" << this << "]::getScaleExtents: min = " << min << ", max = " << max << ", log = " << log << std::endl;
Chris@411 617
Chris@411 618 }
Chris@411 619
Chris@433 620 } else if (m_verticalScale == EqualSpaced) {
Chris@433 621
Chris@433 622 if (!m_spacingMap.empty()) {
Chris@433 623 SpacingMap::const_iterator i = m_spacingMap.begin();
Chris@433 624 min = i->second;
Chris@433 625 i = m_spacingMap.end();
Chris@433 626 --i;
Chris@433 627 max = i->second;
Chris@542 628 // std::cerr << "RegionLayer[" << this << "]::getScaleExtents: equal spaced; min = " << min << ", max = " << max << ", log = " << log << std::endl;
Chris@433 629 }
Chris@433 630
Chris@411 631 } else {
Chris@411 632
Chris@411 633 min = m_model->getValueMinimum();
Chris@411 634 max = m_model->getValueMaximum();
Chris@411 635
Chris@411 636 if (m_verticalScale == LogScale) {
Chris@411 637 LogRange::mapRange(min, max);
Chris@411 638 log = true;
Chris@411 639 }
Chris@411 640 }
Chris@411 641
Chris@411 642 if (max == min) max = min + 1.0;
Chris@411 643 }
Chris@411 644
Chris@411 645 int
Chris@542 646 RegionLayer::spacingIndexToY(View *v, int i) const
Chris@542 647 {
Chris@542 648 int h = v->height();
Chris@542 649 int n = m_spacingMap.size();
Chris@542 650 // this maps from i (spacing of the value from the spacing
Chris@542 651 // map) and n (number of region types) to y
Chris@542 652 int y = h - (((h * i) / n) + (h / (2 * n)));
Chris@542 653 return y;
Chris@542 654 }
Chris@542 655
Chris@542 656 float
Chris@542 657 RegionLayer::yToSpacingIndex(View *v, int y) const
Chris@542 658 {
Chris@542 659 // we return an inexact result here (float rather than int)
Chris@542 660 int h = v->height();
Chris@542 661 int n = m_spacingMap.size();
Chris@551 662 // from y = h - ((h * i) / n) + (h / (2 * n)) as above (vh taking place of i)
Chris@551 663 float vh = float(2*h*n - h - 2*n*y) / float(2*h);
Chris@542 664 return vh;
Chris@542 665 }
Chris@542 666
Chris@542 667 int
Chris@411 668 RegionLayer::getYForValue(View *v, float val) const
Chris@411 669 {
Chris@411 670 float min = 0.0, max = 0.0;
Chris@411 671 bool logarithmic = false;
Chris@411 672 int h = v->height();
Chris@411 673
Chris@433 674 if (m_verticalScale == EqualSpaced) {
Chris@433 675
Chris@433 676 if (m_spacingMap.empty()) return h/2;
Chris@433 677
Chris@433 678 SpacingMap::const_iterator i = m_spacingMap.lower_bound(val);
Chris@433 679 //!!! what now, if i->first != v?
Chris@433 680
Chris@542 681 int y = spacingIndexToY(v, i->second);
Chris@433 682
Chris@587 683 // SVDEBUG << "RegionLayer::getYForValue: value " << val << " -> i->second " << i->second << " -> y " << y << endl;
Chris@542 684 return y;
Chris@433 685
Chris@433 686
Chris@433 687 } else {
Chris@433 688
Chris@433 689 getScaleExtents(v, min, max, logarithmic);
Chris@411 690
Chris@432 691 // std::cerr << "RegionLayer[" << this << "]::getYForValue(" << val << "): min = " << min << ", max = " << max << ", log = " << logarithmic << std::endl;
Chris@432 692 // std::cerr << "h = " << h << ", margin = " << margin << std::endl;
Chris@411 693
Chris@433 694 if (logarithmic) {
Chris@433 695 val = LogRange::map(val);
Chris@433 696 }
Chris@433 697
Chris@433 698 return int(h - ((val - min) * h) / (max - min));
Chris@411 699 }
Chris@411 700 }
Chris@411 701
Chris@542 702 float
Chris@551 703 RegionLayer::getValueForY(View *v, int y, int avoid) const
Chris@542 704 {
Chris@542 705 float min = 0.0, max = 0.0;
Chris@542 706 bool logarithmic = false;
Chris@542 707 int h = v->height();
Chris@542 708
Chris@542 709 if (m_verticalScale == EqualSpaced) {
Chris@542 710
Chris@542 711 // if we're equal spaced, we probably want to snap to the
Chris@542 712 // nearest item when close to it, and give some notification
Chris@542 713 // that we're doing so
Chris@542 714
Chris@542 715 if (m_spacingMap.empty()) return 1.f;
Chris@542 716
Chris@542 717 // n is the number of distinct regions. if we are close to
Chris@542 718 // one of the m/n divisions in the y scale, we should snap to
Chris@542 719 // the value of the mth region.
Chris@542 720
Chris@542 721 float vh = yToSpacingIndex(v, y);
Chris@542 722
Chris@542 723 // spacings in the map are integral, so find the closest one,
Chris@542 724 // map it back to its y coordinate, and see how far we are
Chris@542 725 // from it
Chris@542 726
Chris@542 727 int n = m_spacingMap.size();
Chris@542 728 int ivh = lrintf(vh);
Chris@542 729 if (ivh < 0) ivh = 0;
Chris@542 730 if (ivh > n-1) ivh = n-1;
Chris@542 731 int iy = spacingIndexToY(v, ivh);
Chris@542 732
Chris@542 733 int dist = iy - y;
Chris@542 734 int gap = h / n; // between region lines
Chris@542 735
Chris@551 736 // std::cerr << "getValueForY: y = " << y << ", vh = " << vh << ", ivh = " << ivh << " of " << n << ", iy = " << iy << ", dist = " << dist << ", gap = " << gap << std::endl;
Chris@542 737
Chris@542 738 SpacingMap::const_iterator i = m_spacingMap.begin();
Chris@542 739 while (i != m_spacingMap.end()) {
Chris@542 740 if (i->second == ivh) break;
Chris@542 741 ++i;
Chris@542 742 }
Chris@542 743 if (i == m_spacingMap.end()) i = m_spacingMap.begin();
Chris@542 744
Chris@551 745 // std::cerr << "nearest existing value = " << i->first << " at " << iy << std::endl;
Chris@551 746
Chris@542 747 float val = 0;
Chris@542 748
Chris@551 749 // std::cerr << "note: avoid = " << avoid << ", i->second = " << i->second << std::endl;
Chris@551 750
Chris@551 751 if (dist < -gap/3 &&
Chris@551 752 ((avoid == -1) ||
Chris@551 753 (avoid != i->second && avoid != i->second - 1))) {
Chris@542 754 // bisect gap to prior
Chris@542 755 if (i == m_spacingMap.begin()) {
Chris@542 756 val = i->first - 1.f;
Chris@551 757 // std::cerr << "extended down to " << val << std::endl;
Chris@542 758 } else {
Chris@542 759 SpacingMap::const_iterator j = i;
Chris@542 760 --j;
Chris@542 761 val = (i->first + j->first) / 2;
Chris@551 762 // std::cerr << "bisected down to " << val << std::endl;
Chris@542 763 }
Chris@551 764 } else if (dist > gap/3 &&
Chris@551 765 ((avoid == -1) ||
Chris@551 766 (avoid != i->second && avoid != i->second + 1))) {
Chris@542 767 // bisect gap to following
Chris@542 768 SpacingMap::const_iterator j = i;
Chris@542 769 ++j;
Chris@542 770 if (j == m_spacingMap.end()) {
Chris@542 771 val = i->first + 1.f;
Chris@551 772 // std::cerr << "extended up to " << val << std::endl;
Chris@542 773 } else {
Chris@542 774 val = (i->first + j->first) / 2;
Chris@551 775 // std::cerr << "bisected up to " << val << std::endl;
Chris@542 776 }
Chris@551 777 } else {
Chris@551 778 // snap
Chris@551 779 val = i->first;
Chris@551 780 // std::cerr << "snapped to " << val << std::endl;
Chris@542 781 }
Chris@542 782
Chris@542 783 return val;
Chris@542 784
Chris@542 785 } else {
Chris@542 786
Chris@542 787 getScaleExtents(v, min, max, logarithmic);
Chris@542 788
Chris@542 789 float val = min + (float(h - y) * float(max - min)) / h;
Chris@542 790
Chris@542 791 if (logarithmic) {
Chris@542 792 val = powf(10.f, val);
Chris@542 793 }
Chris@542 794
Chris@542 795 return val;
Chris@542 796 }
Chris@542 797 }
Chris@542 798
Chris@427 799 QColor
Chris@427 800 RegionLayer::getColourForValue(View *v, float val) const
Chris@427 801 {
Chris@427 802 float min, max;
Chris@427 803 bool log;
Chris@427 804 getScaleExtents(v, min, max, log);
Chris@427 805
Chris@427 806 if (min > max) std::swap(min, max);
Chris@427 807 if (max == min) max = min + 1;
Chris@427 808
Chris@427 809 if (log) {
Chris@427 810 LogRange::mapRange(min, max);
Chris@427 811 val = LogRange::map(val);
Chris@427 812 }
Chris@427 813
Chris@587 814 // SVDEBUG << "RegionLayer::getColourForValue: min " << min << ", max "
Chris@585 815 // << max << ", log " << log << ", value " << val << endl;
Chris@427 816
Chris@427 817 QColor solid = ColourMapper(m_colourMap, min, max).map(val);
Chris@427 818 return QColor(solid.red(), solid.green(), solid.blue(), 120);
Chris@427 819 }
Chris@427 820
Chris@427 821 int
Chris@427 822 RegionLayer::getDefaultColourHint(bool darkbg, bool &impose)
Chris@427 823 {
Chris@427 824 impose = false;
Chris@427 825 return ColourDatabase::getInstance()->getColourIndex
Chris@427 826 (QString(darkbg ? "Bright Blue" : "Blue"));
Chris@427 827 }
Chris@427 828
Chris@411 829 void
Chris@411 830 RegionLayer::paint(View *v, QPainter &paint, QRect rect) const
Chris@411 831 {
Chris@411 832 if (!m_model || !m_model->isOK()) return;
Chris@411 833
Chris@411 834 int sampleRate = m_model->getSampleRate();
Chris@411 835 if (!sampleRate) return;
Chris@411 836
Chris@411 837 // Profiler profiler("RegionLayer::paint", true);
Chris@411 838
Chris@552 839 int x0 = rect.left() - 40, x1 = rect.right();
Chris@411 840 long frame0 = v->getFrameForX(x0);
Chris@411 841 long frame1 = v->getFrameForX(x1);
Chris@411 842
Chris@411 843 RegionModel::PointList points(m_model->getPoints(frame0, frame1));
Chris@411 844 if (points.empty()) return;
Chris@411 845
Chris@411 846 paint.setPen(getBaseQColor());
Chris@411 847
Chris@411 848 QColor brushColour(getBaseQColor());
Chris@411 849 brushColour.setAlpha(80);
Chris@411 850
Chris@587 851 // SVDEBUG << "RegionLayer::paint: resolution is "
Chris@585 852 // << m_model->getResolution() << " frames" << endl;
Chris@411 853
Chris@411 854 float min = m_model->getValueMinimum();
Chris@411 855 float max = m_model->getValueMaximum();
Chris@411 856 if (max == min) max = min + 1.0;
Chris@411 857
Chris@411 858 QPoint localPos;
Chris@551 859 RegionModel::Point illuminatePoint(0);
Chris@551 860 bool shouldIlluminate = false;
Chris@411 861
Chris@411 862 if (v->shouldIlluminateLocalFeatures(this, localPos)) {
Chris@551 863 shouldIlluminate = getPointToDrag(v, localPos.x(), localPos.y(),
Chris@551 864 illuminatePoint);
Chris@411 865 }
Chris@411 866
Chris@411 867 paint.save();
Chris@411 868 paint.setRenderHint(QPainter::Antialiasing, false);
Chris@411 869
Chris@413 870 //!!! point y coords if model does not haveDistinctValues() should
Chris@413 871 //!!! be assigned to avoid overlaps
Chris@413 872
Chris@413 873 //!!! if it does have distinct values, we should still ensure y
Chris@413 874 //!!! coord is never completely flat on the top or bottom
Chris@413 875
Chris@550 876 int fontHeight = paint.fontMetrics().height();
Chris@550 877 int fontAscent = paint.fontMetrics().ascent();
Chris@550 878
Chris@411 879 for (RegionModel::PointList::const_iterator i = points.begin();
Chris@411 880 i != points.end(); ++i) {
Chris@411 881
Chris@411 882 const RegionModel::Point &p(*i);
Chris@411 883
Chris@411 884 int x = v->getXForFrame(p.frame);
Chris@411 885 int y = getYForValue(v, p.value);
Chris@411 886 int w = v->getXForFrame(p.frame + p.duration) - x;
Chris@413 887 int h = 9;
Chris@433 888 int ex = x + w;
Chris@427 889
Chris@427 890 RegionModel::PointList::const_iterator j = i;
Chris@427 891 ++j;
Chris@427 892
Chris@427 893 if (j != points.end()) {
Chris@427 894 const RegionModel::Point &q(*j);
Chris@433 895 int nx = v->getXForFrame(q.frame);
Chris@433 896 if (nx < ex) ex = nx;
Chris@427 897 }
Chris@427 898
Chris@411 899 if (m_model->getValueQuantization() != 0.0) {
Chris@411 900 h = y - getYForValue(v, p.value + m_model->getValueQuantization());
Chris@411 901 if (h < 3) h = 3;
Chris@411 902 }
Chris@411 903
Chris@411 904 if (w < 1) w = 1;
Chris@411 905
Chris@427 906 if (m_plotStyle == PlotSegmentation) {
Chris@427 907 paint.setPen(getForegroundQColor(v));
Chris@427 908 paint.setBrush(getColourForValue(v, p.value));
Chris@427 909 } else {
Chris@427 910 paint.setPen(getBaseQColor());
Chris@427 911 paint.setBrush(brushColour);
Chris@427 912 }
Chris@427 913
Chris@427 914 if (m_plotStyle == PlotSegmentation) {
Chris@427 915
Chris@433 916 if (ex <= x) continue;
Chris@427 917
Chris@551 918 if (!shouldIlluminate ||
Chris@551 919 // "illuminatePoint != p"
Chris@551 920 RegionModel::Point::Comparator()(illuminatePoint, p) ||
Chris@551 921 RegionModel::Point::Comparator()(p, illuminatePoint)) {
Chris@551 922
Chris@559 923 paint.setPen(QPen(getForegroundQColor(v), 1));
Chris@552 924 paint.drawLine(x, 0, x, v->height());
Chris@551 925 paint.setPen(Qt::NoPen);
Chris@552 926
Chris@552 927 } else {
Chris@552 928 paint.setPen(QPen(getForegroundQColor(v), 2));
Chris@551 929 }
Chris@427 930
Chris@559 931 paint.drawRect(x, -1, ex - x, v->height() + 2);
Chris@427 932
Chris@427 933 } else {
Chris@427 934
Chris@551 935 if (shouldIlluminate &&
Chris@551 936 // "illuminatePoint == p"
Chris@551 937 !RegionModel::Point::Comparator()(illuminatePoint, p) &&
Chris@551 938 !RegionModel::Point::Comparator()(p, illuminatePoint)) {
Chris@551 939
Chris@551 940 paint.setPen(v->getForeground());
Chris@551 941 paint.setBrush(v->getForeground());
Chris@551 942
Chris@551 943 QString vlabel = QString("%1%2").arg(p.value).arg(m_model->getScaleUnits());
Chris@551 944 v->drawVisibleText(paint,
Chris@551 945 x - paint.fontMetrics().width(vlabel) - 2,
Chris@551 946 y + paint.fontMetrics().height()/2
Chris@551 947 - paint.fontMetrics().descent(),
Chris@551 948 vlabel, View::OutlinedText);
Chris@551 949
Chris@551 950 QString hlabel = RealTime::frame2RealTime
Chris@551 951 (p.frame, m_model->getSampleRate()).toText(true).c_str();
Chris@551 952 v->drawVisibleText(paint,
Chris@551 953 x,
Chris@551 954 y - h/2 - paint.fontMetrics().descent() - 2,
Chris@551 955 hlabel, View::OutlinedText);
Chris@427 956 }
Chris@551 957
Chris@427 958 paint.drawLine(x, y-1, x + w, y-1);
Chris@427 959 paint.drawLine(x, y+1, x + w, y+1);
Chris@427 960 paint.drawLine(x, y - h/2, x, y + h/2);
Chris@427 961 paint.drawLine(x+w, y - h/2, x + w, y + h/2);
Chris@427 962 }
Chris@552 963 }
Chris@552 964
Chris@552 965 int nextLabelMinX = -100;
Chris@552 966 int lastLabelY = 0;
Chris@552 967
Chris@552 968 for (RegionModel::PointList::const_iterator i = points.begin();
Chris@552 969 i != points.end(); ++i) {
Chris@552 970
Chris@552 971 const RegionModel::Point &p(*i);
Chris@552 972
Chris@552 973 int x = v->getXForFrame(p.frame);
Chris@552 974 int y = getYForValue(v, p.value);
Chris@552 975
Chris@552 976 bool illuminated = false;
Chris@552 977
Chris@552 978 if (m_plotStyle != PlotSegmentation) {
Chris@552 979
Chris@552 980 if (shouldIlluminate &&
Chris@552 981 // "illuminatePoint == p"
Chris@552 982 !RegionModel::Point::Comparator()(illuminatePoint, p) &&
Chris@552 983 !RegionModel::Point::Comparator()(p, illuminatePoint)) {
Chris@552 984
Chris@552 985 illuminated = true;
Chris@552 986 }
Chris@552 987 }
Chris@427 988
Chris@551 989 if (!illuminated) {
Chris@551 990 QString label = p.label;
Chris@551 991 if (label == "") {
Chris@551 992 label = QString("%1%2").arg(p.value).arg(m_model->getScaleUnits());
Chris@551 993 }
Chris@551 994
Chris@552 995 int labelX, labelY;
Chris@552 996
Chris@551 997 if (m_plotStyle != PlotSegmentation) {
Chris@552 998 labelX = x - paint.fontMetrics().width(label) - 2;
Chris@552 999 labelY = y + paint.fontMetrics().height()/2
Chris@552 1000 - paint.fontMetrics().descent();
Chris@551 1001 } else {
Chris@552 1002 labelX = x + 5;
Chris@552 1003 labelY = v->getTextLabelHeight(this, paint);
Chris@552 1004 if (labelX < nextLabelMinX) {
Chris@552 1005 if (lastLabelY < v->height()/2) {
Chris@552 1006 labelY = lastLabelY + fontHeight;
Chris@552 1007 }
Chris@552 1008 }
Chris@552 1009 lastLabelY = labelY;
Chris@552 1010 nextLabelMinX = labelX + paint.fontMetrics().width(label);
Chris@551 1011 }
Chris@552 1012
Chris@552 1013 v->drawVisibleText(paint, labelX, labelY, label, View::OutlinedText);
Chris@550 1014 }
Chris@411 1015 }
Chris@411 1016
Chris@411 1017 paint.restore();
Chris@411 1018 }
Chris@411 1019
Chris@411 1020 void
Chris@411 1021 RegionLayer::drawStart(View *v, QMouseEvent *e)
Chris@411 1022 {
Chris@411 1023 if (!m_model) return;
Chris@411 1024
Chris@411 1025 long frame = v->getFrameForX(e->x());
Chris@411 1026 if (frame < 0) frame = 0;
Chris@411 1027 frame = frame / m_model->getResolution() * m_model->getResolution();
Chris@411 1028
Chris@411 1029 float value = getValueForY(v, e->y());
Chris@411 1030
Chris@550 1031 m_editingPoint = RegionModel::Point(frame, value, 0, "");
Chris@411 1032 m_originalPoint = m_editingPoint;
Chris@411 1033
Chris@411 1034 if (m_editingCommand) finish(m_editingCommand);
Chris@411 1035 m_editingCommand = new RegionModel::EditCommand(m_model,
Chris@543 1036 tr("Draw Region"));
Chris@411 1037 m_editingCommand->addPoint(m_editingPoint);
Chris@411 1038
Chris@550 1039 recalcSpacing();
Chris@550 1040
Chris@411 1041 m_editing = true;
Chris@411 1042 }
Chris@411 1043
Chris@411 1044 void
Chris@411 1045 RegionLayer::drawDrag(View *v, QMouseEvent *e)
Chris@411 1046 {
Chris@411 1047 if (!m_model || !m_editing) return;
Chris@411 1048
Chris@411 1049 long frame = v->getFrameForX(e->x());
Chris@411 1050 if (frame < 0) frame = 0;
Chris@411 1051 frame = frame / m_model->getResolution() * m_model->getResolution();
Chris@411 1052
Chris@542 1053 float newValue = m_editingPoint.value;
Chris@542 1054 if (m_verticalScale != EqualSpaced) newValue = getValueForY(v, e->y());
Chris@411 1055
Chris@411 1056 long newFrame = m_editingPoint.frame;
Chris@411 1057 long newDuration = frame - newFrame;
Chris@411 1058 if (newDuration < 0) {
Chris@411 1059 newFrame = frame;
Chris@411 1060 newDuration = -newDuration;
Chris@411 1061 } else if (newDuration == 0) {
Chris@411 1062 newDuration = 1;
Chris@411 1063 }
Chris@411 1064
Chris@411 1065 m_editingCommand->deletePoint(m_editingPoint);
Chris@411 1066 m_editingPoint.frame = newFrame;
Chris@411 1067 m_editingPoint.value = newValue;
Chris@411 1068 m_editingPoint.duration = newDuration;
Chris@411 1069 m_editingCommand->addPoint(m_editingPoint);
Chris@550 1070
Chris@551 1071 recalcSpacing();
Chris@411 1072 }
Chris@411 1073
Chris@411 1074 void
Chris@411 1075 RegionLayer::drawEnd(View *, QMouseEvent *)
Chris@411 1076 {
Chris@411 1077 if (!m_model || !m_editing) return;
Chris@411 1078 finish(m_editingCommand);
Chris@411 1079 m_editingCommand = 0;
Chris@411 1080 m_editing = false;
Chris@550 1081
Chris@550 1082 recalcSpacing();
Chris@411 1083 }
Chris@411 1084
Chris@411 1085 void
Chris@411 1086 RegionLayer::eraseStart(View *v, QMouseEvent *e)
Chris@411 1087 {
Chris@411 1088 if (!m_model) return;
Chris@411 1089
Chris@550 1090 if (!getPointToDrag(v, e->x(), e->y(), m_editingPoint)) return;
Chris@411 1091
Chris@411 1092 if (m_editingCommand) {
Chris@411 1093 finish(m_editingCommand);
Chris@411 1094 m_editingCommand = 0;
Chris@411 1095 }
Chris@411 1096
Chris@411 1097 m_editing = true;
Chris@550 1098 recalcSpacing();
Chris@411 1099 }
Chris@411 1100
Chris@411 1101 void
Chris@411 1102 RegionLayer::eraseDrag(View *v, QMouseEvent *e)
Chris@411 1103 {
Chris@411 1104 }
Chris@411 1105
Chris@411 1106 void
Chris@411 1107 RegionLayer::eraseEnd(View *v, QMouseEvent *e)
Chris@411 1108 {
Chris@411 1109 if (!m_model || !m_editing) return;
Chris@411 1110
Chris@411 1111 m_editing = false;
Chris@551 1112
Chris@550 1113 RegionModel::Point p(0);
Chris@550 1114 if (!getPointToDrag(v, e->x(), e->y(), p)) return;
Chris@550 1115 if (p.frame != m_editingPoint.frame || p.value != m_editingPoint.value) return;
Chris@411 1116
Chris@411 1117 m_editingCommand = new RegionModel::EditCommand
Chris@543 1118 (m_model, tr("Erase Region"));
Chris@411 1119
Chris@411 1120 m_editingCommand->deletePoint(m_editingPoint);
Chris@411 1121
Chris@411 1122 finish(m_editingCommand);
Chris@411 1123 m_editingCommand = 0;
Chris@411 1124 m_editing = false;
Chris@550 1125 recalcSpacing();
Chris@411 1126 }
Chris@411 1127
Chris@411 1128 void
Chris@411 1129 RegionLayer::editStart(View *v, QMouseEvent *e)
Chris@411 1130 {
Chris@411 1131 if (!m_model) return;
Chris@411 1132
Chris@550 1133 if (!getPointToDrag(v, e->x(), e->y(), m_editingPoint)) {
Chris@550 1134 return;
Chris@550 1135 }
Chris@550 1136
Chris@551 1137 m_dragPointX = v->getXForFrame(m_editingPoint.frame);
Chris@551 1138 m_dragPointY = getYForValue(v, m_editingPoint.value);
Chris@551 1139
Chris@411 1140 m_originalPoint = m_editingPoint;
Chris@411 1141
Chris@411 1142 if (m_editingCommand) {
Chris@411 1143 finish(m_editingCommand);
Chris@411 1144 m_editingCommand = 0;
Chris@411 1145 }
Chris@411 1146
Chris@411 1147 m_editing = true;
Chris@551 1148 m_dragStartX = e->x();
Chris@551 1149 m_dragStartY = e->y();
Chris@550 1150 recalcSpacing();
Chris@411 1151 }
Chris@411 1152
Chris@411 1153 void
Chris@411 1154 RegionLayer::editDrag(View *v, QMouseEvent *e)
Chris@411 1155 {
Chris@411 1156 if (!m_model || !m_editing) return;
Chris@411 1157
Chris@551 1158 int xdist = e->x() - m_dragStartX;
Chris@551 1159 int ydist = e->y() - m_dragStartY;
Chris@551 1160 int newx = m_dragPointX + xdist;
Chris@551 1161 int newy = m_dragPointY + ydist;
Chris@551 1162
Chris@551 1163 long frame = v->getFrameForX(newx);
Chris@411 1164 if (frame < 0) frame = 0;
Chris@411 1165 frame = frame / m_model->getResolution() * m_model->getResolution();
Chris@411 1166
Chris@551 1167 // Do not bisect between two values, if one of those values is
Chris@551 1168 // that of the point we're actually moving ...
Chris@551 1169 int avoid = m_spacingMap[m_editingPoint.value];
Chris@551 1170
Chris@551 1171 // ... unless there are other points with the same value
Chris@551 1172 if (m_distributionMap[m_editingPoint.value] > 1) avoid = -1;
Chris@551 1173
Chris@551 1174 float value = getValueForY(v, newy, avoid);
Chris@411 1175
Chris@411 1176 if (!m_editingCommand) {
Chris@411 1177 m_editingCommand = new RegionModel::EditCommand(m_model,
Chris@543 1178 tr("Drag Region"));
Chris@411 1179 }
Chris@411 1180
Chris@411 1181 m_editingCommand->deletePoint(m_editingPoint);
Chris@411 1182 m_editingPoint.frame = frame;
Chris@411 1183 m_editingPoint.value = value;
Chris@411 1184 m_editingCommand->addPoint(m_editingPoint);
Chris@550 1185 recalcSpacing();
Chris@411 1186 }
Chris@411 1187
Chris@411 1188 void
Chris@550 1189 RegionLayer::editEnd(View *, QMouseEvent *e)
Chris@411 1190 {
Chris@411 1191 if (!m_model || !m_editing) return;
Chris@411 1192
Chris@411 1193 if (m_editingCommand) {
Chris@411 1194
Chris@411 1195 QString newName = m_editingCommand->getName();
Chris@411 1196
Chris@411 1197 if (m_editingPoint.frame != m_originalPoint.frame) {
Chris@411 1198 if (m_editingPoint.value != m_originalPoint.value) {
Chris@543 1199 newName = tr("Edit Region");
Chris@411 1200 } else {
Chris@543 1201 newName = tr("Relocate Region");
Chris@411 1202 }
Chris@411 1203 } else {
Chris@411 1204 newName = tr("Change Point Value");
Chris@411 1205 }
Chris@411 1206
Chris@411 1207 m_editingCommand->setName(newName);
Chris@411 1208 finish(m_editingCommand);
Chris@411 1209 }
Chris@411 1210
Chris@411 1211 m_editingCommand = 0;
Chris@411 1212 m_editing = false;
Chris@550 1213 recalcSpacing();
Chris@411 1214 }
Chris@411 1215
Chris@411 1216 bool
Chris@411 1217 RegionLayer::editOpen(View *v, QMouseEvent *e)
Chris@411 1218 {
Chris@411 1219 if (!m_model) return false;
Chris@411 1220
Chris@550 1221 RegionModel::Point region(0);
Chris@550 1222 if (!getPointToDrag(v, e->x(), e->y(), region)) return false;
Chris@550 1223
Chris@411 1224 ItemEditDialog *dialog = new ItemEditDialog
Chris@411 1225 (m_model->getSampleRate(),
Chris@411 1226 ItemEditDialog::ShowTime |
Chris@411 1227 ItemEditDialog::ShowDuration |
Chris@411 1228 ItemEditDialog::ShowValue |
Chris@411 1229 ItemEditDialog::ShowText,
Chris@411 1230 m_model->getScaleUnits());
Chris@411 1231
Chris@411 1232 dialog->setFrameTime(region.frame);
Chris@411 1233 dialog->setValue(region.value);
Chris@411 1234 dialog->setFrameDuration(region.duration);
Chris@411 1235 dialog->setText(region.label);
Chris@411 1236
Chris@411 1237 if (dialog->exec() == QDialog::Accepted) {
Chris@411 1238
Chris@411 1239 RegionModel::Point newRegion = region;
Chris@411 1240 newRegion.frame = dialog->getFrameTime();
Chris@411 1241 newRegion.value = dialog->getValue();
Chris@411 1242 newRegion.duration = dialog->getFrameDuration();
Chris@411 1243 newRegion.label = dialog->getText();
Chris@411 1244
Chris@411 1245 RegionModel::EditCommand *command = new RegionModel::EditCommand
Chris@543 1246 (m_model, tr("Edit Region"));
Chris@411 1247 command->deletePoint(region);
Chris@411 1248 command->addPoint(newRegion);
Chris@411 1249 finish(command);
Chris@411 1250 }
Chris@411 1251
Chris@411 1252 delete dialog;
Chris@550 1253 recalcSpacing();
Chris@411 1254 return true;
Chris@411 1255 }
Chris@411 1256
Chris@411 1257 void
Chris@411 1258 RegionLayer::moveSelection(Selection s, size_t newStartFrame)
Chris@411 1259 {
Chris@411 1260 if (!m_model) return;
Chris@411 1261
Chris@411 1262 RegionModel::EditCommand *command =
Chris@411 1263 new RegionModel::EditCommand(m_model, tr("Drag Selection"));
Chris@411 1264
Chris@411 1265 RegionModel::PointList points =
Chris@411 1266 m_model->getPoints(s.getStartFrame(), s.getEndFrame());
Chris@411 1267
Chris@411 1268 for (RegionModel::PointList::iterator i = points.begin();
Chris@411 1269 i != points.end(); ++i) {
Chris@411 1270
Chris@411 1271 if (s.contains(i->frame)) {
Chris@411 1272 RegionModel::Point newPoint(*i);
Chris@411 1273 newPoint.frame = i->frame + newStartFrame - s.getStartFrame();
Chris@411 1274 command->deletePoint(*i);
Chris@411 1275 command->addPoint(newPoint);
Chris@411 1276 }
Chris@411 1277 }
Chris@411 1278
Chris@411 1279 finish(command);
Chris@550 1280 recalcSpacing();
Chris@411 1281 }
Chris@411 1282
Chris@411 1283 void
Chris@411 1284 RegionLayer::resizeSelection(Selection s, Selection newSize)
Chris@411 1285 {
Chris@411 1286 if (!m_model) return;
Chris@411 1287
Chris@411 1288 RegionModel::EditCommand *command =
Chris@411 1289 new RegionModel::EditCommand(m_model, tr("Resize Selection"));
Chris@411 1290
Chris@411 1291 RegionModel::PointList points =
Chris@411 1292 m_model->getPoints(s.getStartFrame(), s.getEndFrame());
Chris@411 1293
Chris@411 1294 double ratio =
Chris@411 1295 double(newSize.getEndFrame() - newSize.getStartFrame()) /
Chris@411 1296 double(s.getEndFrame() - s.getStartFrame());
Chris@411 1297
Chris@411 1298 for (RegionModel::PointList::iterator i = points.begin();
Chris@411 1299 i != points.end(); ++i) {
Chris@411 1300
Chris@411 1301 if (s.contains(i->frame)) {
Chris@411 1302
Chris@411 1303 double targetStart = i->frame;
Chris@411 1304 targetStart = newSize.getStartFrame() +
Chris@411 1305 double(targetStart - s.getStartFrame()) * ratio;
Chris@411 1306
Chris@411 1307 double targetEnd = i->frame + i->duration;
Chris@411 1308 targetEnd = newSize.getStartFrame() +
Chris@411 1309 double(targetEnd - s.getStartFrame()) * ratio;
Chris@411 1310
Chris@411 1311 RegionModel::Point newPoint(*i);
Chris@411 1312 newPoint.frame = lrint(targetStart);
Chris@411 1313 newPoint.duration = lrint(targetEnd - targetStart);
Chris@411 1314 command->deletePoint(*i);
Chris@411 1315 command->addPoint(newPoint);
Chris@411 1316 }
Chris@411 1317 }
Chris@411 1318
Chris@411 1319 finish(command);
Chris@550 1320 recalcSpacing();
Chris@411 1321 }
Chris@411 1322
Chris@411 1323 void
Chris@411 1324 RegionLayer::deleteSelection(Selection s)
Chris@411 1325 {
Chris@411 1326 if (!m_model) return;
Chris@411 1327
Chris@411 1328 RegionModel::EditCommand *command =
Chris@411 1329 new RegionModel::EditCommand(m_model, tr("Delete Selected Points"));
Chris@411 1330
Chris@411 1331 RegionModel::PointList points =
Chris@411 1332 m_model->getPoints(s.getStartFrame(), s.getEndFrame());
Chris@411 1333
Chris@411 1334 for (RegionModel::PointList::iterator i = points.begin();
Chris@411 1335 i != points.end(); ++i) {
Chris@411 1336
Chris@411 1337 if (s.contains(i->frame)) {
Chris@411 1338 command->deletePoint(*i);
Chris@411 1339 }
Chris@411 1340 }
Chris@411 1341
Chris@411 1342 finish(command);
Chris@550 1343 recalcSpacing();
Chris@411 1344 }
Chris@411 1345
Chris@411 1346 void
Chris@411 1347 RegionLayer::copy(View *v, Selection s, Clipboard &to)
Chris@411 1348 {
Chris@411 1349 if (!m_model) return;
Chris@411 1350
Chris@411 1351 RegionModel::PointList points =
Chris@411 1352 m_model->getPoints(s.getStartFrame(), s.getEndFrame());
Chris@411 1353
Chris@411 1354 for (RegionModel::PointList::iterator i = points.begin();
Chris@411 1355 i != points.end(); ++i) {
Chris@411 1356 if (s.contains(i->frame)) {
Chris@411 1357 Clipboard::Point point(i->frame, i->value, i->duration, i->label);
Chris@411 1358 point.setReferenceFrame(alignToReference(v, i->frame));
Chris@411 1359 to.addPoint(point);
Chris@411 1360 }
Chris@411 1361 }
Chris@411 1362 }
Chris@411 1363
Chris@411 1364 bool
Chris@411 1365 RegionLayer::paste(View *v, const Clipboard &from, int frameOffset, bool /* interactive */)
Chris@411 1366 {
Chris@411 1367 if (!m_model) return false;
Chris@411 1368
Chris@411 1369 const Clipboard::PointList &points = from.getPoints();
Chris@411 1370
Chris@411 1371 bool realign = false;
Chris@411 1372
Chris@411 1373 if (clipboardHasDifferentAlignment(v, from)) {
Chris@411 1374
Chris@411 1375 QMessageBox::StandardButton button =
Chris@411 1376 QMessageBox::question(v, tr("Re-align pasted items?"),
Chris@411 1377 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 1378 QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel,
Chris@411 1379 QMessageBox::Yes);
Chris@411 1380
Chris@411 1381 if (button == QMessageBox::Cancel) {
Chris@411 1382 return false;
Chris@411 1383 }
Chris@411 1384
Chris@411 1385 if (button == QMessageBox::Yes) {
Chris@411 1386 realign = true;
Chris@411 1387 }
Chris@411 1388 }
Chris@411 1389
Chris@411 1390 RegionModel::EditCommand *command =
Chris@411 1391 new RegionModel::EditCommand(m_model, tr("Paste"));
Chris@411 1392
Chris@411 1393 for (Clipboard::PointList::const_iterator i = points.begin();
Chris@411 1394 i != points.end(); ++i) {
Chris@411 1395
Chris@411 1396 if (!i->haveFrame()) continue;
Chris@411 1397 size_t frame = 0;
Chris@411 1398
Chris@411 1399 if (!realign) {
Chris@411 1400
Chris@411 1401 frame = i->getFrame();
Chris@411 1402
Chris@411 1403 } else {
Chris@411 1404
Chris@411 1405 if (i->haveReferenceFrame()) {
Chris@411 1406 frame = i->getReferenceFrame();
Chris@411 1407 frame = alignFromReference(v, frame);
Chris@411 1408 } else {
Chris@411 1409 frame = i->getFrame();
Chris@411 1410 }
Chris@411 1411 }
Chris@411 1412
Chris@411 1413 RegionModel::Point newPoint(frame);
Chris@411 1414
Chris@411 1415 if (i->haveLabel()) newPoint.label = i->getLabel();
Chris@411 1416 if (i->haveValue()) newPoint.value = i->getValue();
Chris@411 1417 else newPoint.value = (m_model->getValueMinimum() +
Chris@411 1418 m_model->getValueMaximum()) / 2;
Chris@411 1419 if (i->haveDuration()) newPoint.duration = i->getDuration();
Chris@411 1420 else {
Chris@411 1421 size_t nextFrame = frame;
Chris@411 1422 Clipboard::PointList::const_iterator j = i;
Chris@411 1423 for (; j != points.end(); ++j) {
Chris@411 1424 if (!j->haveFrame()) continue;
Chris@411 1425 if (j != i) break;
Chris@411 1426 }
Chris@411 1427 if (j != points.end()) {
Chris@411 1428 nextFrame = j->getFrame();
Chris@411 1429 }
Chris@411 1430 if (nextFrame == frame) {
Chris@411 1431 newPoint.duration = m_model->getResolution();
Chris@411 1432 } else {
Chris@411 1433 newPoint.duration = nextFrame - frame;
Chris@411 1434 }
Chris@411 1435 }
Chris@411 1436
Chris@411 1437 command->addPoint(newPoint);
Chris@411 1438 }
Chris@411 1439
Chris@411 1440 finish(command);
Chris@550 1441 recalcSpacing();
Chris@411 1442 return true;
Chris@411 1443 }
Chris@411 1444
Chris@411 1445 void
Chris@411 1446 RegionLayer::toXml(QTextStream &stream,
Chris@411 1447 QString indent, QString extraAttributes) const
Chris@411 1448 {
Chris@411 1449 SingleColourLayer::toXml(stream, indent, extraAttributes +
Chris@412 1450 QString(" verticalScale=\"%1\" plotStyle=\"%2\"")
Chris@412 1451 .arg(m_verticalScale)
Chris@412 1452 .arg(m_plotStyle));
Chris@411 1453 }
Chris@411 1454
Chris@411 1455 void
Chris@411 1456 RegionLayer::setProperties(const QXmlAttributes &attributes)
Chris@411 1457 {
Chris@411 1458 SingleColourLayer::setProperties(attributes);
Chris@411 1459
Chris@411 1460 bool ok;
Chris@411 1461 VerticalScale scale = (VerticalScale)
Chris@411 1462 attributes.value("verticalScale").toInt(&ok);
Chris@411 1463 if (ok) setVerticalScale(scale);
Chris@412 1464 PlotStyle style = (PlotStyle)
Chris@412 1465 attributes.value("plotStyle").toInt(&ok);
Chris@412 1466 if (ok) setPlotStyle(style);
Chris@411 1467 }
Chris@411 1468
Chris@411 1469