annotate layer/RegionLayer.cpp @ 947:e53a87a5efb2

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