annotate layer/RegionLayer.cpp @ 1534:bfd8b22fd67c

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