annotate layer/RegionLayer.cpp @ 789:9fd1bdf214dd tonioni

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