annotate layer/RegionLayer.cpp @ 1605:ae2d5f8ff005

When asked to render the whole view width, we need to wait for the layers to be ready before we can determine what the width is
author Chris Cannam
date Thu, 30 Apr 2020 14:47:13 +0100
parents e95cefd4aa81
children
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@1547 469 SnapType snap, int ycoord) const
Chris@411 470 {
Chris@1471 471 auto model = ModelById::getAs<RegionModel>(m_model);
Chris@1471 472 if (!model) {
Chris@1547 473 return Layer::snapToFeatureFrame(v, frame, resolution, snap, ycoord);
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@1537 611 if (!v->getVisibleExtentsForUnit(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@1550 852 int x0 = rect.left() - 40;
Chris@1550 853 int x1 = x0 + rect.width() + 80;
Chris@411 854
Chris@1409 855 sv_frame_t wholeFrame0 = v->getFrameForX(0);
Chris@1409 856 sv_frame_t wholeFrame1 = v->getFrameForX(v->getPaintWidth());
Chris@1409 857
Chris@1471 858 EventVector points(model->getEventsSpanning(wholeFrame0,
Chris@1428 859 wholeFrame1 - wholeFrame0));
Chris@411 860 if (points.empty()) return;
Chris@411 861
Chris@411 862 paint.setPen(getBaseQColor());
Chris@411 863
Chris@411 864 QColor brushColour(getBaseQColor());
Chris@411 865 brushColour.setAlpha(80);
Chris@411 866
Chris@587 867 // SVDEBUG << "RegionLayer::paint: resolution is "
Chris@1471 868 // << model->getResolution() << " frames" << endl;
Chris@411 869
Chris@1471 870 double min = model->getValueMinimum();
Chris@1471 871 double max = model->getValueMaximum();
Chris@411 872 if (max == min) max = min + 1.0;
Chris@411 873
Chris@411 874 QPoint localPos;
Chris@1428 875 Event illuminatePoint(0);
Chris@551 876 bool shouldIlluminate = false;
Chris@411 877
Chris@411 878 if (v->shouldIlluminateLocalFeatures(this, localPos)) {
Chris@551 879 shouldIlluminate = getPointToDrag(v, localPos.x(), localPos.y(),
Chris@551 880 illuminatePoint);
Chris@411 881 }
Chris@411 882
Chris@411 883 paint.save();
Chris@411 884 paint.setRenderHint(QPainter::Antialiasing, false);
Chris@411 885
Chris@413 886 //!!! point y coords if model does not haveDistinctValues() should
Chris@413 887 //!!! be assigned to avoid overlaps
Chris@413 888
Chris@413 889 //!!! if it does have distinct values, we should still ensure y
Chris@413 890 //!!! coord is never completely flat on the top or bottom
Chris@413 891
Chris@550 892 int fontHeight = paint.fontMetrics().height();
Chris@550 893
Chris@1428 894 for (EventVector::const_iterator i = points.begin();
Chris@1266 895 i != points.end(); ++i) {
Chris@411 896
Chris@1428 897 const Event &p(*i);
Chris@411 898
Chris@1428 899 int x = v->getXForFrame(p.getFrame());
Chris@1428 900 int w = v->getXForFrame(p.getFrame() + p.getDuration()) - x;
Chris@1428 901 int y = getYForValue(v, p.getValue());
Chris@1266 902 int h = 9;
Chris@1266 903 int ex = x + w;
Chris@427 904
Chris@1409 905 int gap = v->scalePixelSize(2);
Chris@1409 906
Chris@1428 907 EventVector::const_iterator j = i;
Chris@1266 908 ++j;
Chris@427 909
Chris@1266 910 if (j != points.end()) {
Chris@1428 911 const Event &q(*j);
Chris@1428 912 int nx = v->getXForFrame(q.getFrame());
Chris@433 913 if (nx < ex) ex = nx;
Chris@427 914 }
Chris@427 915
Chris@1471 916 if (model->getValueQuantization() != 0.0) {
Chris@1428 917 h = y - getYForValue
Chris@1471 918 (v, p.getValue() + model->getValueQuantization());
Chris@1266 919 if (h < 3) h = 3;
Chris@1266 920 }
Chris@411 921
Chris@1266 922 if (w < 1) w = 1;
Chris@411 923
Chris@1266 924 if (m_plotStyle == PlotSegmentation) {
Chris@918 925 paint.setPen(getForegroundQColor(v->getView()));
Chris@1428 926 paint.setBrush(getColourForValue(v, p.getValue()));
Chris@427 927 } else {
Chris@427 928 paint.setPen(getBaseQColor());
Chris@427 929 paint.setBrush(brushColour);
Chris@427 930 }
Chris@427 931
Chris@1266 932 if (m_plotStyle == PlotSegmentation) {
Chris@427 933
Chris@1266 934 if (ex <= x) continue;
Chris@427 935
Chris@1428 936 if (!shouldIlluminate || illuminatePoint != p) {
Chris@551 937
Chris@918 938 paint.setPen(QPen(getForegroundQColor(v->getView()), 1));
Chris@918 939 paint.drawLine(x, 0, x, v->getPaintHeight());
Chris@551 940 paint.setPen(Qt::NoPen);
Chris@552 941
Chris@552 942 } else {
Chris@918 943 paint.setPen(QPen(getForegroundQColor(v->getView()), 2));
Chris@551 944 }
Chris@427 945
Chris@1409 946 paint.drawRect(x, -1, ex - x, v->getPaintHeight() + gap);
Chris@427 947
Chris@1266 948 } else {
Chris@427 949
Chris@1428 950 if (shouldIlluminate && illuminatePoint == p) {
Chris@551 951
Chris@551 952 paint.setPen(v->getForeground());
Chris@551 953 paint.setBrush(v->getForeground());
Chris@551 954
Chris@1471 955 // Qt 5.13 deprecates QFontMetrics::width(), but its suggested
Chris@1471 956 // replacement (horizontalAdvance) was only added in Qt 5.11
Chris@1471 957 // which is too new for us
Chris@1471 958 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
Chris@1471 959
Chris@1428 960 QString vlabel =
Chris@1428 961 QString("%1%2").arg(p.getValue()).arg(getScaleUnits());
Chris@1078 962 PaintAssistant::drawVisibleText(v, paint,
Chris@1409 963 x - paint.fontMetrics().width(vlabel) - gap,
Chris@551 964 y + paint.fontMetrics().height()/2
Chris@551 965 - paint.fontMetrics().descent(),
Chris@1078 966 vlabel, PaintAssistant::OutlinedText);
Chris@551 967
Chris@551 968 QString hlabel = RealTime::frame2RealTime
Chris@1471 969 (p.getFrame(), model->getSampleRate()).toText(true).c_str();
Chris@1078 970 PaintAssistant::drawVisibleText(v, paint,
Chris@551 971 x,
Chris@1409 972 y - h/2 - paint.fontMetrics().descent() - gap,
Chris@1078 973 hlabel, PaintAssistant::OutlinedText);
Chris@427 974 }
Chris@551 975
Chris@427 976 paint.drawLine(x, y-1, x + w, y-1);
Chris@427 977 paint.drawLine(x, y+1, x + w, y+1);
Chris@427 978 paint.drawLine(x, y - h/2, x, y + h/2);
Chris@427 979 paint.drawLine(x+w, y - h/2, x + w, y + h/2);
Chris@427 980 }
Chris@552 981 }
Chris@552 982
Chris@552 983 int nextLabelMinX = -100;
Chris@552 984 int lastLabelY = 0;
Chris@552 985
Chris@1428 986 for (EventVector::const_iterator i = points.begin();
Chris@1266 987 i != points.end(); ++i) {
Chris@552 988
Chris@1428 989 const Event &p(*i);
Chris@552 990
Chris@1428 991 int x = v->getXForFrame(p.getFrame());
Chris@1428 992 int w = v->getXForFrame(p.getFrame() + p.getDuration()) - x;
Chris@1428 993 int y = getYForValue(v, p.getValue());
Chris@552 994
Chris@1428 995 QString label = p.getLabel();
Chris@1409 996 if (label == "") {
Chris@1428 997 label = QString("%1%2").arg(p.getValue()).arg(getScaleUnits());
Chris@1409 998 }
Chris@1409 999 int labelWidth = paint.fontMetrics().width(label);
Chris@1409 1000
Chris@1409 1001 int gap = v->scalePixelSize(2);
Chris@1409 1002
Chris@1409 1003 if (m_plotStyle == PlotSegmentation) {
Chris@1409 1004 if ((x + w < x0 && x + labelWidth + gap < x0) || x > x1) {
Chris@1409 1005 continue;
Chris@1409 1006 }
Chris@1409 1007 } else {
Chris@1409 1008 if (x + w < x0 || x - labelWidth - gap > x1) {
Chris@1409 1009 continue;
Chris@1409 1010 }
Chris@1409 1011 }
Chris@1409 1012
Chris@552 1013 bool illuminated = false;
Chris@552 1014
Chris@1266 1015 if (m_plotStyle != PlotSegmentation) {
Chris@1428 1016 if (shouldIlluminate && illuminatePoint == p) {
Chris@552 1017 illuminated = true;
Chris@552 1018 }
Chris@552 1019 }
Chris@427 1020
Chris@551 1021 if (!illuminated) {
Chris@551 1022
Chris@552 1023 int labelX, labelY;
Chris@552 1024
Chris@551 1025 if (m_plotStyle != PlotSegmentation) {
Chris@1409 1026 labelX = x - labelWidth - gap;
Chris@552 1027 labelY = y + paint.fontMetrics().height()/2
Chris@552 1028 - paint.fontMetrics().descent();
Chris@551 1029 } else {
Chris@552 1030 labelX = x + 5;
Chris@1537 1031 labelY = v->getTextLabelYCoord(this, paint);
Chris@552 1032 if (labelX < nextLabelMinX) {
Chris@918 1033 if (lastLabelY < v->getPaintHeight()/2) {
Chris@552 1034 labelY = lastLabelY + fontHeight;
Chris@552 1035 }
Chris@552 1036 }
Chris@552 1037 lastLabelY = labelY;
Chris@1409 1038 nextLabelMinX = labelX + labelWidth;
Chris@551 1039 }
Chris@552 1040
Chris@1409 1041 PaintAssistant::drawVisibleText(v, paint, labelX, labelY, label,
Chris@1409 1042 PaintAssistant::OutlinedText);
Chris@550 1043 }
Chris@411 1044 }
Chris@411 1045
Chris@411 1046 paint.restore();
Chris@411 1047 }
Chris@411 1048
Chris@701 1049 int
Chris@918 1050 RegionLayer::getVerticalScaleWidth(LayerGeometryProvider *v, bool, QPainter &paint) const
Chris@701 1051 {
Chris@1471 1052 auto model = ModelById::getAs<RegionModel>(m_model);
Chris@1471 1053 if (!model ||
Chris@701 1054 m_verticalScale == AutoAlignScale ||
Chris@701 1055 m_verticalScale == EqualSpaced) {
Chris@701 1056 return 0;
Chris@701 1057 } else if (m_plotStyle == PlotSegmentation) {
Chris@701 1058 if (m_verticalScale == LogScale) {
Chris@701 1059 return LogColourScale().getWidth(v, paint);
Chris@701 1060 } else {
Chris@701 1061 return LinearColourScale().getWidth(v, paint);
Chris@701 1062 }
Chris@701 1063 } else {
Chris@701 1064 if (m_verticalScale == LogScale) {
Chris@701 1065 return LogNumericalScale().getWidth(v, paint);
Chris@701 1066 } else {
Chris@701 1067 return LinearNumericalScale().getWidth(v, paint);
Chris@701 1068 }
Chris@701 1069 }
Chris@701 1070 }
Chris@701 1071
Chris@701 1072 void
Chris@918 1073 RegionLayer::paintVerticalScale(LayerGeometryProvider *v, bool, QPainter &paint, QRect) const
Chris@701 1074 {
Chris@1471 1075 auto model = ModelById::getAs<RegionModel>(m_model);
Chris@1471 1076 if (!model || model->isEmpty()) return;
Chris@701 1077
Chris@701 1078 QString unit;
Chris@905 1079 double min, max;
Chris@701 1080 bool logarithmic;
Chris@701 1081
Chris@701 1082 int w = getVerticalScaleWidth(v, false, paint);
Chris@701 1083
Chris@701 1084 if (m_plotStyle == PlotSegmentation) {
Chris@701 1085
Chris@701 1086 getValueExtents(min, max, logarithmic, unit);
Chris@701 1087
Chris@701 1088 if (logarithmic) {
Chris@701 1089 LogRange::mapRange(min, max);
Chris@701 1090 LogColourScale().paintVertical(v, this, paint, 0, min, max);
Chris@701 1091 } else {
Chris@701 1092 LinearColourScale().paintVertical(v, this, paint, 0, min, max);
Chris@701 1093 }
Chris@701 1094
Chris@701 1095 } else {
Chris@701 1096
Chris@701 1097 getScaleExtents(v, min, max, logarithmic);
Chris@701 1098
Chris@701 1099 if (logarithmic) {
Chris@701 1100 LogNumericalScale().paintVertical(v, this, paint, 0, min, max);
Chris@701 1101 } else {
Chris@701 1102 LinearNumericalScale().paintVertical(v, this, paint, 0, min, max);
Chris@701 1103 }
Chris@701 1104 }
Chris@701 1105
Chris@701 1106 if (getScaleUnits() != "") {
Chris@701 1107 int mw = w - 5;
Chris@701 1108 paint.drawText(5,
Chris@701 1109 5 + paint.fontMetrics().ascent(),
Chris@701 1110 TextAbbrev::abbreviate(getScaleUnits(),
Chris@701 1111 paint.fontMetrics(),
Chris@701 1112 mw));
Chris@701 1113 }
Chris@701 1114 }
Chris@701 1115
Chris@411 1116 void
Chris@918 1117 RegionLayer::drawStart(LayerGeometryProvider *v, QMouseEvent *e)
Chris@411 1118 {
Chris@1471 1119 auto model = ModelById::getAs<RegionModel>(m_model);
Chris@1471 1120 if (!model) return;
Chris@411 1121
Chris@989 1122 sv_frame_t frame = v->getFrameForX(e->x());
Chris@411 1123 if (frame < 0) frame = 0;
Chris@1471 1124 frame = frame / model->getResolution() * model->getResolution();
Chris@411 1125
Chris@905 1126 double value = getValueForY(v, e->y());
Chris@411 1127
Chris@1428 1128 m_editingPoint = Event(frame, float(value), 0, "");
Chris@411 1129 m_originalPoint = m_editingPoint;
Chris@411 1130
Chris@411 1131 if (m_editingCommand) finish(m_editingCommand);
Chris@1471 1132 m_editingCommand = new ChangeEventsCommand(m_model.untyped, tr("Draw Region"));
Chris@1428 1133 m_editingCommand->add(m_editingPoint);
Chris@411 1134
Chris@550 1135 recalcSpacing();
Chris@550 1136
Chris@411 1137 m_editing = true;
Chris@411 1138 }
Chris@411 1139
Chris@411 1140 void
Chris@918 1141 RegionLayer::drawDrag(LayerGeometryProvider *v, QMouseEvent *e)
Chris@411 1142 {
Chris@1471 1143 auto model = ModelById::getAs<RegionModel>(m_model);
Chris@1471 1144 if (!model || !m_editing) return;
Chris@411 1145
Chris@905 1146 sv_frame_t frame = v->getFrameForX(e->x());
Chris@411 1147 if (frame < 0) frame = 0;
Chris@1471 1148 frame = frame / model->getResolution() * model->getResolution();
Chris@411 1149
Chris@1428 1150 double newValue = m_editingPoint.getValue();
Chris@542 1151 if (m_verticalScale != EqualSpaced) newValue = getValueForY(v, e->y());
Chris@411 1152
Chris@1428 1153 sv_frame_t newFrame = m_editingPoint.getFrame();
Chris@905 1154 sv_frame_t newDuration = frame - newFrame;
Chris@411 1155 if (newDuration < 0) {
Chris@411 1156 newFrame = frame;
Chris@411 1157 newDuration = -newDuration;
Chris@411 1158 } else if (newDuration == 0) {
Chris@411 1159 newDuration = 1;
Chris@411 1160 }
Chris@411 1161
Chris@1428 1162 m_editingCommand->remove(m_editingPoint);
Chris@1428 1163 m_editingPoint = m_editingPoint
Chris@1428 1164 .withFrame(newFrame)
Chris@1428 1165 .withValue(float(newValue))
Chris@1428 1166 .withDuration(newDuration);
Chris@1428 1167 m_editingCommand->add(m_editingPoint);
Chris@550 1168
Chris@551 1169 recalcSpacing();
Chris@411 1170 }
Chris@411 1171
Chris@411 1172 void
Chris@918 1173 RegionLayer::drawEnd(LayerGeometryProvider *, QMouseEvent *)
Chris@411 1174 {
Chris@1471 1175 auto model = ModelById::getAs<RegionModel>(m_model);
Chris@1471 1176 if (!model || !m_editing) return;
Chris@411 1177 finish(m_editingCommand);
Chris@1408 1178 m_editingCommand = nullptr;
Chris@411 1179 m_editing = false;
Chris@550 1180
Chris@550 1181 recalcSpacing();
Chris@411 1182 }
Chris@411 1183
Chris@411 1184 void
Chris@918 1185 RegionLayer::eraseStart(LayerGeometryProvider *v, QMouseEvent *e)
Chris@411 1186 {
Chris@1471 1187 auto model = ModelById::getAs<RegionModel>(m_model);
Chris@1471 1188 if (!model) return;
Chris@411 1189
Chris@550 1190 if (!getPointToDrag(v, e->x(), e->y(), m_editingPoint)) return;
Chris@411 1191
Chris@411 1192 if (m_editingCommand) {
Chris@1266 1193 finish(m_editingCommand);
Chris@1408 1194 m_editingCommand = nullptr;
Chris@411 1195 }
Chris@411 1196
Chris@411 1197 m_editing = true;
Chris@550 1198 recalcSpacing();
Chris@411 1199 }
Chris@411 1200
Chris@411 1201 void
Chris@918 1202 RegionLayer::eraseDrag(LayerGeometryProvider *, QMouseEvent *)
Chris@411 1203 {
Chris@411 1204 }
Chris@411 1205
Chris@411 1206 void
Chris@918 1207 RegionLayer::eraseEnd(LayerGeometryProvider *v, QMouseEvent *e)
Chris@411 1208 {
Chris@1471 1209 auto model = ModelById::getAs<RegionModel>(m_model);
Chris@1471 1210 if (!model || !m_editing) return;
Chris@411 1211
Chris@411 1212 m_editing = false;
Chris@551 1213
Chris@1428 1214 Event p(0);
Chris@550 1215 if (!getPointToDrag(v, e->x(), e->y(), p)) return;
Chris@1428 1216 if (p.getFrame() != m_editingPoint.getFrame() ||
Chris@1428 1217 p.getValue() != m_editingPoint.getValue()) return;
Chris@411 1218
Chris@1428 1219 m_editingCommand = new ChangeEventsCommand
Chris@1471 1220 (m_model.untyped, tr("Erase Region"));
Chris@411 1221
Chris@1428 1222 m_editingCommand->remove(m_editingPoint);
Chris@411 1223
Chris@411 1224 finish(m_editingCommand);
Chris@1408 1225 m_editingCommand = nullptr;
Chris@411 1226 m_editing = false;
Chris@550 1227 recalcSpacing();
Chris@411 1228 }
Chris@411 1229
Chris@411 1230 void
Chris@918 1231 RegionLayer::editStart(LayerGeometryProvider *v, QMouseEvent *e)
Chris@411 1232 {
Chris@1471 1233 auto model = ModelById::getAs<RegionModel>(m_model);
Chris@1471 1234 if (!model) return;
Chris@411 1235
Chris@550 1236 if (!getPointToDrag(v, e->x(), e->y(), m_editingPoint)) {
Chris@550 1237 return;
Chris@550 1238 }
Chris@550 1239
Chris@1428 1240 m_dragPointX = v->getXForFrame(m_editingPoint.getFrame());
Chris@1428 1241 m_dragPointY = getYForValue(v, m_editingPoint.getValue());
Chris@551 1242
Chris@411 1243 m_originalPoint = m_editingPoint;
Chris@411 1244
Chris@411 1245 if (m_editingCommand) {
Chris@1266 1246 finish(m_editingCommand);
Chris@1408 1247 m_editingCommand = nullptr;
Chris@411 1248 }
Chris@411 1249
Chris@411 1250 m_editing = true;
Chris@551 1251 m_dragStartX = e->x();
Chris@551 1252 m_dragStartY = e->y();
Chris@550 1253 recalcSpacing();
Chris@411 1254 }
Chris@411 1255
Chris@411 1256 void
Chris@918 1257 RegionLayer::editDrag(LayerGeometryProvider *v, QMouseEvent *e)
Chris@411 1258 {
Chris@1471 1259 auto model = ModelById::getAs<RegionModel>(m_model);
Chris@1471 1260 if (!model || !m_editing) return;
Chris@411 1261
Chris@551 1262 int xdist = e->x() - m_dragStartX;
Chris@551 1263 int ydist = e->y() - m_dragStartY;
Chris@551 1264 int newx = m_dragPointX + xdist;
Chris@551 1265 int newy = m_dragPointY + ydist;
Chris@551 1266
Chris@989 1267 sv_frame_t frame = v->getFrameForX(newx);
Chris@411 1268 if (frame < 0) frame = 0;
Chris@1471 1269 frame = frame / model->getResolution() * model->getResolution();
Chris@411 1270
Chris@551 1271 // Do not bisect between two values, if one of those values is
Chris@551 1272 // that of the point we're actually moving ...
Chris@1428 1273 int avoid = m_spacingMap[m_editingPoint.getValue()];
Chris@551 1274
Chris@551 1275 // ... unless there are other points with the same value
Chris@1428 1276 if (m_distributionMap[m_editingPoint.getValue()] > 1) avoid = -1;
Chris@551 1277
Chris@905 1278 double value = getValueForY(v, newy, avoid);
Chris@411 1279
Chris@411 1280 if (!m_editingCommand) {
Chris@1471 1281 m_editingCommand = new ChangeEventsCommand(m_model.untyped,
Chris@1266 1282 tr("Drag Region"));
Chris@411 1283 }
Chris@411 1284
Chris@1428 1285 m_editingCommand->remove(m_editingPoint);
Chris@1428 1286 m_editingPoint = m_editingPoint
Chris@1428 1287 .withFrame(frame)
Chris@1428 1288 .withValue(float(value));
Chris@1428 1289 m_editingCommand->add(m_editingPoint);
Chris@550 1290 recalcSpacing();
Chris@411 1291 }
Chris@411 1292
Chris@411 1293 void
Chris@918 1294 RegionLayer::editEnd(LayerGeometryProvider *, QMouseEvent *)
Chris@411 1295 {
Chris@1471 1296 auto model = ModelById::getAs<RegionModel>(m_model);
Chris@1471 1297 if (!model || !m_editing) return;
Chris@411 1298
Chris@411 1299 if (m_editingCommand) {
Chris@411 1300
Chris@1266 1301 QString newName = m_editingCommand->getName();
Chris@411 1302
Chris@1428 1303 if (m_editingPoint.getFrame() != m_originalPoint.getFrame()) {
Chris@1428 1304 if (m_editingPoint.getValue() != m_originalPoint.getValue()) {
Chris@1266 1305 newName = tr("Edit Region");
Chris@1266 1306 } else {
Chris@1266 1307 newName = tr("Relocate Region");
Chris@1266 1308 }
Chris@1266 1309 } else {
Chris@1266 1310 newName = tr("Change Point Value");
Chris@1266 1311 }
Chris@411 1312
Chris@1266 1313 m_editingCommand->setName(newName);
Chris@1266 1314 finish(m_editingCommand);
Chris@411 1315 }
Chris@411 1316
Chris@1408 1317 m_editingCommand = nullptr;
Chris@411 1318 m_editing = false;
Chris@550 1319 recalcSpacing();
Chris@411 1320 }
Chris@411 1321
Chris@411 1322 bool
Chris@918 1323 RegionLayer::editOpen(LayerGeometryProvider *v, QMouseEvent *e)
Chris@411 1324 {
Chris@1471 1325 auto model = ModelById::getAs<RegionModel>(m_model);
Chris@1471 1326 if (!model) return false;
Chris@411 1327
Chris@1428 1328 Event region(0);
Chris@550 1329 if (!getPointToDrag(v, e->x(), e->y(), region)) return false;
Chris@550 1330
Chris@411 1331 ItemEditDialog *dialog = new ItemEditDialog
Chris@1471 1332 (model->getSampleRate(),
Chris@411 1333 ItemEditDialog::ShowTime |
Chris@411 1334 ItemEditDialog::ShowDuration |
Chris@411 1335 ItemEditDialog::ShowValue |
Chris@411 1336 ItemEditDialog::ShowText,
Chris@701 1337 getScaleUnits());
Chris@411 1338
Chris@1428 1339 dialog->setFrameTime(region.getFrame());
Chris@1428 1340 dialog->setValue(region.getValue());
Chris@1428 1341 dialog->setFrameDuration(region.getDuration());
Chris@1428 1342 dialog->setText(region.getLabel());
Chris@411 1343
Chris@411 1344 if (dialog->exec() == QDialog::Accepted) {
Chris@411 1345
Chris@1428 1346 Event newRegion = region
Chris@1428 1347 .withFrame(dialog->getFrameTime())
Chris@1428 1348 .withValue(dialog->getValue())
Chris@1428 1349 .withDuration(dialog->getFrameDuration())
Chris@1428 1350 .withLabel(dialog->getText());
Chris@411 1351
Chris@1428 1352 ChangeEventsCommand *command = new ChangeEventsCommand
Chris@1471 1353 (m_model.untyped, tr("Edit Region"));
Chris@1428 1354 command->remove(region);
Chris@1428 1355 command->add(newRegion);
Chris@411 1356 finish(command);
Chris@411 1357 }
Chris@411 1358
Chris@411 1359 delete dialog;
Chris@550 1360 recalcSpacing();
Chris@411 1361 return true;
Chris@411 1362 }
Chris@411 1363
Chris@411 1364 void
Chris@905 1365 RegionLayer::moveSelection(Selection s, sv_frame_t newStartFrame)
Chris@411 1366 {
Chris@1471 1367 auto model = ModelById::getAs<RegionModel>(m_model);
Chris@1471 1368 if (!model) return;
Chris@411 1369
Chris@1428 1370 ChangeEventsCommand *command =
Chris@1471 1371 new ChangeEventsCommand(m_model.untyped, tr("Drag Selection"));
Chris@411 1372
Chris@1428 1373 EventVector points =
Chris@1471 1374 model->getEventsStartingWithin(s.getStartFrame(), s.getDuration());
Chris@411 1375
Chris@1428 1376 for (EventVector::iterator i = points.begin();
Chris@1266 1377 i != points.end(); ++i) {
Chris@411 1378
Chris@1429 1379 Event newPoint = (*i)
Chris@1429 1380 .withFrame(i->getFrame() + newStartFrame - s.getStartFrame());
Chris@1429 1381 command->remove(*i);
Chris@1429 1382 command->add(newPoint);
Chris@411 1383 }
Chris@411 1384
Chris@411 1385 finish(command);
Chris@550 1386 recalcSpacing();
Chris@411 1387 }
Chris@411 1388
Chris@411 1389 void
Chris@411 1390 RegionLayer::resizeSelection(Selection s, Selection newSize)
Chris@411 1391 {
Chris@1471 1392 auto model = ModelById::getAs<RegionModel>(m_model);
Chris@1471 1393 if (!model || !s.getDuration()) return;
Chris@411 1394
Chris@1428 1395 ChangeEventsCommand *command =
Chris@1471 1396 new ChangeEventsCommand(m_model.untyped, tr("Resize Selection"));
Chris@411 1397
Chris@1428 1398 EventVector points =
Chris@1471 1399 model->getEventsStartingWithin(s.getStartFrame(), s.getDuration());
Chris@411 1400
Chris@1428 1401 double ratio = double(newSize.getDuration()) / double(s.getDuration());
Chris@1428 1402 double oldStart = double(s.getStartFrame());
Chris@1428 1403 double newStart = double(newSize.getStartFrame());
Chris@1428 1404
Chris@1428 1405 for (Event p: points) {
Chris@411 1406
Chris@1428 1407 double newFrame = (double(p.getFrame()) - oldStart) * ratio + newStart;
Chris@1428 1408 double newDuration = double(p.getDuration()) * ratio;
Chris@411 1409
Chris@1428 1410 Event newPoint = p
Chris@1428 1411 .withFrame(lrint(newFrame))
Chris@1428 1412 .withDuration(lrint(newDuration));
Chris@1428 1413 command->remove(p);
Chris@1428 1414 command->add(newPoint);
Chris@411 1415 }
Chris@411 1416
Chris@411 1417 finish(command);
Chris@550 1418 recalcSpacing();
Chris@411 1419 }
Chris@411 1420
Chris@411 1421 void
Chris@411 1422 RegionLayer::deleteSelection(Selection s)
Chris@411 1423 {
Chris@1471 1424 auto model = ModelById::getAs<RegionModel>(m_model);
Chris@1471 1425 if (!model) return;
Chris@411 1426
Chris@1428 1427 ChangeEventsCommand *command =
Chris@1471 1428 new ChangeEventsCommand(m_model.untyped, tr("Delete Selected Points"));
Chris@411 1429
Chris@1428 1430 EventVector points =
Chris@1471 1431 model->getEventsStartingWithin(s.getStartFrame(), s.getDuration());
Chris@411 1432
Chris@1428 1433 for (EventVector::iterator i = points.begin();
Chris@1266 1434 i != points.end(); ++i) {
Chris@411 1435
Chris@1428 1436 if (s.contains(i->getFrame())) {
Chris@1428 1437 command->remove(*i);
Chris@411 1438 }
Chris@411 1439 }
Chris@411 1440
Chris@411 1441 finish(command);
Chris@550 1442 recalcSpacing();
Chris@411 1443 }
Chris@411 1444
Chris@411 1445 void
Chris@918 1446 RegionLayer::copy(LayerGeometryProvider *v, Selection s, Clipboard &to)
Chris@411 1447 {
Chris@1471 1448 auto model = ModelById::getAs<RegionModel>(m_model);
Chris@1471 1449 if (!model) return;
Chris@411 1450
Chris@1428 1451 EventVector points =
Chris@1471 1452 model->getEventsStartingWithin(s.getStartFrame(), s.getDuration());
Chris@411 1453
Chris@1428 1454 for (Event p: points) {
Chris@1428 1455 to.addPoint(p.withReferenceFrame(alignToReference(v, p.getFrame())));
Chris@411 1456 }
Chris@411 1457 }
Chris@411 1458
Chris@411 1459 bool
Chris@1533 1460 RegionLayer::paste(LayerGeometryProvider *v, const Clipboard &from,
Chris@1533 1461 sv_frame_t /* frameOffset */, bool /* interactive */)
Chris@411 1462 {
Chris@1471 1463 auto model = ModelById::getAs<RegionModel>(m_model);
Chris@1471 1464 if (!model) return false;
Chris@411 1465
Chris@1423 1466 const EventVector &points = from.getPoints();
Chris@411 1467
Chris@411 1468 bool realign = false;
Chris@411 1469
Chris@411 1470 if (clipboardHasDifferentAlignment(v, from)) {
Chris@411 1471
Chris@411 1472 QMessageBox::StandardButton button =
Chris@918 1473 QMessageBox::question(v->getView(), tr("Re-align pasted items?"),
Chris@411 1474 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 1475 QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel,
Chris@411 1476 QMessageBox::Yes);
Chris@411 1477
Chris@411 1478 if (button == QMessageBox::Cancel) {
Chris@411 1479 return false;
Chris@411 1480 }
Chris@411 1481
Chris@411 1482 if (button == QMessageBox::Yes) {
Chris@411 1483 realign = true;
Chris@411 1484 }
Chris@411 1485 }
Chris@411 1486
Chris@1428 1487 ChangeEventsCommand *command =
Chris@1471 1488 new ChangeEventsCommand(m_model.untyped, tr("Paste"));
Chris@411 1489
Chris@1423 1490 for (EventVector::const_iterator i = points.begin();
Chris@411 1491 i != points.end(); ++i) {
Chris@411 1492
Chris@905 1493 sv_frame_t frame = 0;
Chris@411 1494
Chris@411 1495 if (!realign) {
Chris@411 1496
Chris@411 1497 frame = i->getFrame();
Chris@411 1498
Chris@411 1499 } else {
Chris@411 1500
Chris@1423 1501 if (i->hasReferenceFrame()) {
Chris@411 1502 frame = i->getReferenceFrame();
Chris@411 1503 frame = alignFromReference(v, frame);
Chris@411 1504 } else {
Chris@411 1505 frame = i->getFrame();
Chris@411 1506 }
Chris@411 1507 }
Chris@411 1508
Chris@1533 1509 Event p = i->withFrame(frame);
Chris@1533 1510
Chris@1428 1511 Event newPoint = p;
Chris@1428 1512 if (!p.hasValue()) {
Chris@1471 1513 newPoint = newPoint.withValue((model->getValueMinimum() +
Chris@1471 1514 model->getValueMaximum()) / 2);
Chris@1428 1515 }
Chris@1428 1516 if (!p.hasDuration()) {
Chris@905 1517 sv_frame_t nextFrame = frame;
Chris@1423 1518 EventVector::const_iterator j = i;
Chris@411 1519 for (; j != points.end(); ++j) {
Chris@411 1520 if (j != i) break;
Chris@411 1521 }
Chris@411 1522 if (j != points.end()) {
Chris@411 1523 nextFrame = j->getFrame();
Chris@411 1524 }
Chris@411 1525 if (nextFrame == frame) {
Chris@1471 1526 newPoint = newPoint.withDuration(model->getResolution());
Chris@411 1527 } else {
Chris@1428 1528 newPoint = newPoint.withDuration(nextFrame - frame);
Chris@411 1529 }
Chris@411 1530 }
Chris@411 1531
Chris@1428 1532 command->add(newPoint);
Chris@411 1533 }
Chris@411 1534
Chris@411 1535 finish(command);
Chris@550 1536 recalcSpacing();
Chris@411 1537 return true;
Chris@411 1538 }
Chris@411 1539
Chris@411 1540 void
Chris@411 1541 RegionLayer::toXml(QTextStream &stream,
Chris@411 1542 QString indent, QString extraAttributes) const
Chris@411 1543 {
Chris@1362 1544 QString s;
Chris@1362 1545
Chris@1362 1546 s += QString("verticalScale=\"%1\" "
Chris@1362 1547 "plotStyle=\"%2\" ")
Chris@1362 1548 .arg(m_verticalScale)
Chris@1362 1549 .arg(m_plotStyle);
Chris@1362 1550
Chris@1362 1551 // New-style colour map attribute, by string id rather than by
Chris@1362 1552 // number
Chris@1362 1553
Chris@1362 1554 s += QString("fillColourMap=\"%1\" ")
Chris@1362 1555 .arg(ColourMapper::getColourMapId(m_colourMap));
Chris@1362 1556
Chris@1362 1557 // Old-style colour map attribute
Chris@1362 1558
Chris@1362 1559 s += QString("colourMap=\"%1\" ")
Chris@1362 1560 .arg(ColourMapper::getBackwardCompatibilityColourMap(m_colourMap));
Chris@1362 1561
Chris@1362 1562 SingleColourLayer::toXml(stream, indent, extraAttributes + " " + s);
Chris@411 1563 }
Chris@411 1564
Chris@411 1565 void
Chris@411 1566 RegionLayer::setProperties(const QXmlAttributes &attributes)
Chris@411 1567 {
Chris@411 1568 SingleColourLayer::setProperties(attributes);
Chris@411 1569
Chris@411 1570 bool ok;
Chris@411 1571 VerticalScale scale = (VerticalScale)
Chris@1266 1572 attributes.value("verticalScale").toInt(&ok);
Chris@411 1573 if (ok) setVerticalScale(scale);
Chris@412 1574 PlotStyle style = (PlotStyle)
Chris@1266 1575 attributes.value("plotStyle").toInt(&ok);
Chris@412 1576 if (ok) setPlotStyle(style);
Chris@1362 1577
Chris@1362 1578 QString colourMapId = attributes.value("fillColourMap");
Chris@1362 1579 int colourMap = ColourMapper::getColourMapById(colourMapId);
Chris@1362 1580 if (colourMap >= 0) {
Chris@1362 1581 setFillColourMap(colourMap);
Chris@1362 1582 } else {
Chris@1362 1583 colourMap = attributes.value("colourMap").toInt(&ok);
Chris@1362 1584 if (ok && colourMap < ColourMapper::getColourMapCount()) {
Chris@1362 1585 setFillColourMap(colourMap);
Chris@1362 1586 }
Chris@1362 1587 }
Chris@411 1588 }
Chris@411 1589
Chris@411 1590