annotate layer/RegionLayer.cpp @ 1204:d421df27e184 3.0-integration

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