annotate layer/RegionLayer.cpp @ 1560:27f3e64489e1 spectrogram-export

Give timer a parent so as not to leak it
author Chris Cannam
date Thu, 09 Jan 2020 14:29:21 +0000
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