annotate layer/RegionLayer.cpp @ 1528:14c07e445365

Fix compiler warning
author Chris Cannam
date Mon, 07 Oct 2019 09:43:31 +0100
parents bd7fdc418f36
children 37df1530519d
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@918 1459 RegionLayer::paste(LayerGeometryProvider *v, const Clipboard &from, sv_frame_t /* frameOffset */, bool /* interactive */)
Chris@411 1460 {
Chris@1471 1461 auto model = ModelById::getAs<RegionModel>(m_model);
Chris@1471 1462 if (!model) return false;
Chris@411 1463
Chris@1423 1464 const EventVector &points = from.getPoints();
Chris@411 1465
Chris@411 1466 bool realign = false;
Chris@411 1467
Chris@411 1468 if (clipboardHasDifferentAlignment(v, from)) {
Chris@411 1469
Chris@411 1470 QMessageBox::StandardButton button =
Chris@918 1471 QMessageBox::question(v->getView(), tr("Re-align pasted items?"),
Chris@411 1472 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 1473 QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel,
Chris@411 1474 QMessageBox::Yes);
Chris@411 1475
Chris@411 1476 if (button == QMessageBox::Cancel) {
Chris@411 1477 return false;
Chris@411 1478 }
Chris@411 1479
Chris@411 1480 if (button == QMessageBox::Yes) {
Chris@411 1481 realign = true;
Chris@411 1482 }
Chris@411 1483 }
Chris@411 1484
Chris@1428 1485 ChangeEventsCommand *command =
Chris@1471 1486 new ChangeEventsCommand(m_model.untyped, tr("Paste"));
Chris@411 1487
Chris@1423 1488 for (EventVector::const_iterator i = points.begin();
Chris@411 1489 i != points.end(); ++i) {
Chris@411 1490
Chris@905 1491 sv_frame_t frame = 0;
Chris@411 1492
Chris@411 1493 if (!realign) {
Chris@411 1494
Chris@411 1495 frame = i->getFrame();
Chris@411 1496
Chris@411 1497 } else {
Chris@411 1498
Chris@1423 1499 if (i->hasReferenceFrame()) {
Chris@411 1500 frame = i->getReferenceFrame();
Chris@411 1501 frame = alignFromReference(v, frame);
Chris@411 1502 } else {
Chris@411 1503 frame = i->getFrame();
Chris@411 1504 }
Chris@411 1505 }
Chris@411 1506
Chris@1428 1507 Event p = *i;
Chris@1428 1508 Event newPoint = p;
Chris@1428 1509 if (!p.hasValue()) {
Chris@1471 1510 newPoint = newPoint.withValue((model->getValueMinimum() +
Chris@1471 1511 model->getValueMaximum()) / 2);
Chris@1428 1512 }
Chris@1428 1513 if (!p.hasDuration()) {
Chris@905 1514 sv_frame_t nextFrame = frame;
Chris@1423 1515 EventVector::const_iterator j = i;
Chris@411 1516 for (; j != points.end(); ++j) {
Chris@411 1517 if (j != i) break;
Chris@411 1518 }
Chris@411 1519 if (j != points.end()) {
Chris@411 1520 nextFrame = j->getFrame();
Chris@411 1521 }
Chris@411 1522 if (nextFrame == frame) {
Chris@1471 1523 newPoint = newPoint.withDuration(model->getResolution());
Chris@411 1524 } else {
Chris@1428 1525 newPoint = newPoint.withDuration(nextFrame - frame);
Chris@411 1526 }
Chris@411 1527 }
Chris@411 1528
Chris@1428 1529 command->add(newPoint);
Chris@411 1530 }
Chris@411 1531
Chris@411 1532 finish(command);
Chris@550 1533 recalcSpacing();
Chris@411 1534 return true;
Chris@411 1535 }
Chris@411 1536
Chris@411 1537 void
Chris@411 1538 RegionLayer::toXml(QTextStream &stream,
Chris@411 1539 QString indent, QString extraAttributes) const
Chris@411 1540 {
Chris@1362 1541 QString s;
Chris@1362 1542
Chris@1362 1543 s += QString("verticalScale=\"%1\" "
Chris@1362 1544 "plotStyle=\"%2\" ")
Chris@1362 1545 .arg(m_verticalScale)
Chris@1362 1546 .arg(m_plotStyle);
Chris@1362 1547
Chris@1362 1548 // New-style colour map attribute, by string id rather than by
Chris@1362 1549 // number
Chris@1362 1550
Chris@1362 1551 s += QString("fillColourMap=\"%1\" ")
Chris@1362 1552 .arg(ColourMapper::getColourMapId(m_colourMap));
Chris@1362 1553
Chris@1362 1554 // Old-style colour map attribute
Chris@1362 1555
Chris@1362 1556 s += QString("colourMap=\"%1\" ")
Chris@1362 1557 .arg(ColourMapper::getBackwardCompatibilityColourMap(m_colourMap));
Chris@1362 1558
Chris@1362 1559 SingleColourLayer::toXml(stream, indent, extraAttributes + " " + s);
Chris@411 1560 }
Chris@411 1561
Chris@411 1562 void
Chris@411 1563 RegionLayer::setProperties(const QXmlAttributes &attributes)
Chris@411 1564 {
Chris@411 1565 SingleColourLayer::setProperties(attributes);
Chris@411 1566
Chris@411 1567 bool ok;
Chris@411 1568 VerticalScale scale = (VerticalScale)
Chris@1266 1569 attributes.value("verticalScale").toInt(&ok);
Chris@411 1570 if (ok) setVerticalScale(scale);
Chris@412 1571 PlotStyle style = (PlotStyle)
Chris@1266 1572 attributes.value("plotStyle").toInt(&ok);
Chris@412 1573 if (ok) setPlotStyle(style);
Chris@1362 1574
Chris@1362 1575 QString colourMapId = attributes.value("fillColourMap");
Chris@1362 1576 int colourMap = ColourMapper::getColourMapById(colourMapId);
Chris@1362 1577 if (colourMap >= 0) {
Chris@1362 1578 setFillColourMap(colourMap);
Chris@1362 1579 } else {
Chris@1362 1580 colourMap = attributes.value("colourMap").toInt(&ok);
Chris@1362 1581 if (ok && colourMap < ColourMapper::getColourMapCount()) {
Chris@1362 1582 setFillColourMap(colourMap);
Chris@1362 1583 }
Chris@1362 1584 }
Chris@411 1585 }
Chris@411 1586
Chris@411 1587