annotate layer/RegionLayer.cpp @ 640:c6d705bf1672

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