annotate layer/BoxLayer.cpp @ 1534:bfd8b22fd67c

Fix #1904 Scrolling colour 3d plot does not always work when in View normalisation mode. We shouldn't imagine we've just invalidated the cache if the truth is that we've only just created the renderer
author Chris Cannam
date Wed, 09 Oct 2019 13:45:17 +0100
parents 37df1530519d
children 873ff035364c
rev   line source
Chris@1511 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@1511 2
Chris@1511 3 /*
Chris@1511 4 Sonic Visualiser
Chris@1511 5 An audio file viewer and annotation editor.
Chris@1511 6 Centre for Digital Music, Queen Mary, University of London.
Chris@1511 7
Chris@1511 8 This program is free software; you can redistribute it and/or
Chris@1511 9 modify it under the terms of the GNU General Public License as
Chris@1511 10 published by the Free Software Foundation; either version 2 of the
Chris@1511 11 License, or (at your option) any later version. See the file
Chris@1511 12 COPYING included with this distribution for more information.
Chris@1511 13 */
Chris@1511 14
Chris@1518 15 #include "BoxLayer.h"
Chris@1511 16
Chris@1511 17 #include "data/model/Model.h"
Chris@1511 18 #include "base/RealTime.h"
Chris@1511 19 #include "base/Profiler.h"
Chris@1511 20 #include "base/LogRange.h"
Chris@1511 21
Chris@1511 22 #include "ColourDatabase.h"
Chris@1511 23 #include "ColourMapper.h"
Chris@1511 24 #include "LinearNumericalScale.h"
Chris@1511 25 #include "LogNumericalScale.h"
Chris@1511 26 #include "PaintAssistant.h"
Chris@1511 27
Chris@1511 28 #include "view/View.h"
Chris@1511 29
Chris@1518 30 #include "data/model/BoxModel.h"
Chris@1511 31
Chris@1511 32 #include "widgets/ItemEditDialog.h"
Chris@1511 33 #include "widgets/TextAbbrev.h"
Chris@1511 34
Chris@1511 35 #include <QPainter>
Chris@1511 36 #include <QPainterPath>
Chris@1511 37 #include <QMouseEvent>
Chris@1511 38 #include <QTextStream>
Chris@1511 39 #include <QMessageBox>
Chris@1511 40
Chris@1511 41 #include <iostream>
Chris@1511 42 #include <cmath>
Chris@1511 43
Chris@1518 44 BoxLayer::BoxLayer() :
Chris@1511 45 SingleColourLayer(),
Chris@1511 46 m_editing(false),
Chris@1511 47 m_dragPointX(0),
Chris@1511 48 m_dragPointY(0),
Chris@1511 49 m_dragStartX(0),
Chris@1511 50 m_dragStartY(0),
Chris@1511 51 m_originalPoint(0, 0.0, 0, tr("New Box")),
Chris@1511 52 m_editingPoint(0, 0.0, 0, tr("New Box")),
Chris@1518 53 m_editingCommand(nullptr),
Chris@1518 54 m_verticalScale(AutoAlignScale)
Chris@1511 55 {
Chris@1511 56
Chris@1511 57 }
Chris@1511 58
Chris@1511 59 int
Chris@1518 60 BoxLayer::getCompletion(LayerGeometryProvider *) const
Chris@1511 61 {
Chris@1511 62 auto model = ModelById::get(m_model);
Chris@1511 63 if (model) return model->getCompletion();
Chris@1511 64 else return 0;
Chris@1511 65 }
Chris@1511 66
Chris@1511 67 void
Chris@1518 68 BoxLayer::setModel(ModelId modelId)
Chris@1511 69 {
Chris@1518 70 auto oldModel = ModelById::getAs<BoxModel>(m_model);
Chris@1518 71 auto newModel = ModelById::getAs<BoxModel>(modelId);
Chris@1511 72
Chris@1511 73 if (!modelId.isNone() && !newModel) {
Chris@1518 74 throw std::logic_error("Not a BoxModel");
Chris@1511 75 }
Chris@1511 76
Chris@1511 77 if (m_model == modelId) return;
Chris@1511 78 m_model = modelId;
Chris@1511 79
Chris@1511 80 if (newModel) {
Chris@1511 81 connectSignals(m_model);
Chris@1511 82 }
Chris@1511 83
Chris@1511 84 emit modelReplaced();
Chris@1511 85 }
Chris@1511 86
Chris@1511 87 Layer::PropertyList
Chris@1518 88 BoxLayer::getProperties() const
Chris@1511 89 {
Chris@1511 90 PropertyList list = SingleColourLayer::getProperties();
Chris@1511 91 list.push_back("Vertical Scale");
Chris@1518 92 list.push_back("Scale Units");
Chris@1511 93 return list;
Chris@1511 94 }
Chris@1511 95
Chris@1511 96 QString
Chris@1518 97 BoxLayer::getPropertyLabel(const PropertyName &name) const
Chris@1511 98 {
Chris@1511 99 if (name == "Vertical Scale") return tr("Vertical Scale");
Chris@1518 100 if (name == "Scale Units") return tr("Scale Units");
Chris@1511 101 return SingleColourLayer::getPropertyLabel(name);
Chris@1511 102 }
Chris@1511 103
Chris@1511 104 Layer::PropertyType
Chris@1518 105 BoxLayer::getPropertyType(const PropertyName &name) const
Chris@1511 106 {
Chris@1511 107 if (name == "Vertical Scale") return ValueProperty;
Chris@1518 108 if (name == "Scale Units") return UnitsProperty;
Chris@1511 109 return SingleColourLayer::getPropertyType(name);
Chris@1511 110 }
Chris@1511 111
Chris@1511 112 QString
Chris@1518 113 BoxLayer::getPropertyGroupName(const PropertyName &name) const
Chris@1511 114 {
Chris@1518 115 if (name == "Vertical Scale" || name == "Scale Units") {
Chris@1511 116 return tr("Scale");
Chris@1511 117 }
Chris@1511 118 return SingleColourLayer::getPropertyGroupName(name);
Chris@1511 119 }
Chris@1511 120
Chris@1511 121 int
Chris@1518 122 BoxLayer::getPropertyRangeAndValue(const PropertyName &name,
Chris@1511 123 int *min, int *max, int *deflt) const
Chris@1511 124 {
Chris@1511 125 int val = 0;
Chris@1511 126
Chris@1511 127 if (name == "Vertical Scale") {
Chris@1511 128
Chris@1511 129 if (min) *min = 0;
Chris@1511 130 if (max) *max = 2;
Chris@1511 131 if (deflt) *deflt = int(LinearScale);
Chris@1511 132
Chris@1511 133 val = int(m_verticalScale);
Chris@1511 134
Chris@1518 135 } else if (name == "Scale Units") {
Chris@1518 136
Chris@1518 137 if (deflt) *deflt = 0;
Chris@1518 138 auto model = ModelById::getAs<BoxModel>(m_model);
Chris@1518 139 if (model) {
Chris@1518 140 val = UnitDatabase::getInstance()->getUnitId
Chris@1518 141 (model->getScaleUnits());
Chris@1518 142 }
Chris@1518 143
Chris@1511 144 } else {
Chris@1511 145 val = SingleColourLayer::getPropertyRangeAndValue(name, min, max, deflt);
Chris@1511 146 }
Chris@1511 147
Chris@1511 148 return val;
Chris@1511 149 }
Chris@1511 150
Chris@1511 151 QString
Chris@1518 152 BoxLayer::getPropertyValueLabel(const PropertyName &name,
Chris@1511 153 int value) const
Chris@1511 154 {
Chris@1511 155 if (name == "Vertical Scale") {
Chris@1511 156 switch (value) {
Chris@1511 157 default:
Chris@1511 158 case 0: return tr("Auto-Align");
Chris@1511 159 case 1: return tr("Linear");
Chris@1511 160 case 2: return tr("Log");
Chris@1511 161 }
Chris@1511 162 }
Chris@1511 163 return SingleColourLayer::getPropertyValueLabel(name, value);
Chris@1511 164 }
Chris@1511 165
Chris@1511 166 void
Chris@1518 167 BoxLayer::setProperty(const PropertyName &name, int value)
Chris@1511 168 {
Chris@1511 169 if (name == "Vertical Scale") {
Chris@1511 170 setVerticalScale(VerticalScale(value));
Chris@1518 171 } else if (name == "Scale Units") {
Chris@1518 172 auto model = ModelById::getAs<BoxModel>(m_model);
Chris@1518 173 if (model) {
Chris@1518 174 model->setScaleUnits
Chris@1518 175 (UnitDatabase::getInstance()->getUnitById(value));
Chris@1518 176 emit modelChanged(m_model);
Chris@1518 177 }
Chris@1511 178 } else {
Chris@1511 179 return SingleColourLayer::setProperty(name, value);
Chris@1511 180 }
Chris@1511 181 }
Chris@1511 182
Chris@1511 183 void
Chris@1518 184 BoxLayer::setVerticalScale(VerticalScale scale)
Chris@1511 185 {
Chris@1511 186 if (m_verticalScale == scale) return;
Chris@1511 187 m_verticalScale = scale;
Chris@1511 188 emit layerParametersChanged();
Chris@1511 189 }
Chris@1511 190
Chris@1511 191 bool
Chris@1518 192 BoxLayer::isLayerScrollable(const LayerGeometryProvider *v) const
Chris@1511 193 {
Chris@1511 194 QPoint discard;
Chris@1511 195 return !v->shouldIlluminateLocalFeatures(this, discard);
Chris@1511 196 }
Chris@1511 197
Chris@1511 198 bool
Chris@1518 199 BoxLayer::getValueExtents(double &min, double &max,
Chris@1518 200 bool &logarithmic, QString &unit) const
Chris@1511 201 {
Chris@1518 202 auto model = ModelById::getAs<BoxModel>(m_model);
Chris@1511 203 if (!model) return false;
Chris@1518 204 min = model->getValueMinimum();
Chris@1518 205 max = model->getValueMaximum();
Chris@1511 206 unit = getScaleUnits();
Chris@1511 207
Chris@1511 208 if (m_verticalScale == LogScale) logarithmic = true;
Chris@1511 209
Chris@1511 210 return true;
Chris@1511 211 }
Chris@1511 212
Chris@1511 213 bool
Chris@1518 214 BoxLayer::getDisplayExtents(double &min, double &max) const
Chris@1511 215 {
Chris@1518 216 auto model = ModelById::getAs<BoxModel>(m_model);
Chris@1511 217 if (!model || m_verticalScale == AutoAlignScale) return false;
Chris@1511 218
Chris@1518 219 min = model->getValueMinimum();
Chris@1518 220 max = model->getValueMaximum();
Chris@1511 221
Chris@1511 222 return true;
Chris@1511 223 }
Chris@1511 224
Chris@1518 225 bool
Chris@1528 226 BoxLayer::adoptExtents(double /* min */, double /* max */, QString unit)
Chris@1518 227 {
Chris@1518 228 auto model = ModelById::getAs<BoxModel>(m_model);
Chris@1518 229 if (!model) return false;
Chris@1518 230 if (model->getScaleUnits() == "") {
Chris@1518 231 model->setScaleUnits(unit);
Chris@1518 232 return true;
Chris@1518 233 } else {
Chris@1518 234 return false;
Chris@1518 235 }
Chris@1518 236 }
Chris@1518 237
Chris@1511 238 EventVector
Chris@1518 239 BoxLayer::getLocalPoints(LayerGeometryProvider *v, int x) const
Chris@1511 240 {
Chris@1518 241 auto model = ModelById::getAs<BoxModel>(m_model);
Chris@1511 242 if (!model) return EventVector();
Chris@1511 243
Chris@1511 244 sv_frame_t frame = v->getFrameForX(x);
Chris@1511 245
Chris@1511 246 EventVector local = model->getEventsCovering(frame);
Chris@1511 247 if (!local.empty()) return local;
Chris@1511 248
Chris@1511 249 int fuzz = ViewManager::scalePixelSize(2);
Chris@1511 250 sv_frame_t start = v->getFrameForX(x - fuzz);
Chris@1511 251 sv_frame_t end = v->getFrameForX(x + fuzz);
Chris@1511 252
Chris@1511 253 local = model->getEventsStartingWithin(frame, end - frame);
Chris@1511 254 if (!local.empty()) return local;
Chris@1511 255
Chris@1511 256 local = model->getEventsSpanning(start, frame - start);
Chris@1511 257 if (!local.empty()) return local;
Chris@1511 258
Chris@1511 259 return {};
Chris@1511 260 }
Chris@1511 261
Chris@1511 262 bool
Chris@1518 263 BoxLayer::getPointToDrag(LayerGeometryProvider *v, int x, int y,
Chris@1511 264 Event &point) const
Chris@1511 265 {
Chris@1518 266 auto model = ModelById::getAs<BoxModel>(m_model);
Chris@1511 267 if (!model) return false;
Chris@1511 268
Chris@1511 269 sv_frame_t frame = v->getFrameForX(x);
Chris@1511 270
Chris@1511 271 EventVector onPoints = model->getEventsCovering(frame);
Chris@1511 272 if (onPoints.empty()) return false;
Chris@1511 273
Chris@1511 274 int nearestDistance = -1;
Chris@1511 275 for (const auto &p: onPoints) {
Chris@1511 276 int distance = getYForValue(v, p.getValue()) - y;
Chris@1511 277 if (distance < 0) distance = -distance;
Chris@1511 278 if (nearestDistance == -1 || distance < nearestDistance) {
Chris@1511 279 nearestDistance = distance;
Chris@1511 280 point = p;
Chris@1511 281 }
Chris@1511 282 }
Chris@1511 283
Chris@1511 284 return true;
Chris@1511 285 }
Chris@1511 286
Chris@1511 287 QString
Chris@1518 288 BoxLayer::getLabelPreceding(sv_frame_t frame) const
Chris@1511 289 {
Chris@1518 290 auto model = ModelById::getAs<BoxModel>(m_model);
Chris@1511 291 if (!model) return "";
Chris@1511 292 EventVector points = model->getEventsStartingWithin
Chris@1511 293 (model->getStartFrame(), frame - model->getStartFrame());
Chris@1511 294 if (!points.empty()) {
Chris@1511 295 for (auto i = points.rbegin(); i != points.rend(); ++i) {
Chris@1511 296 if (i->getLabel() != QString()) {
Chris@1511 297 return i->getLabel();
Chris@1511 298 }
Chris@1511 299 }
Chris@1511 300 }
Chris@1511 301 return QString();
Chris@1511 302 }
Chris@1511 303
Chris@1511 304 QString
Chris@1518 305 BoxLayer::getFeatureDescription(LayerGeometryProvider *v,
Chris@1511 306 QPoint &pos) const
Chris@1511 307 {
Chris@1511 308 int x = pos.x();
Chris@1511 309
Chris@1518 310 auto model = ModelById::getAs<BoxModel>(m_model);
Chris@1511 311 if (!model || !model->getSampleRate()) return "";
Chris@1511 312
Chris@1511 313 EventVector points = getLocalPoints(v, x);
Chris@1511 314
Chris@1511 315 if (points.empty()) {
Chris@1511 316 if (!model->isReady()) {
Chris@1511 317 return tr("In progress");
Chris@1511 318 } else {
Chris@1511 319 return tr("No local points");
Chris@1511 320 }
Chris@1511 321 }
Chris@1511 322
Chris@1511 323 Event box;
Chris@1511 324 EventVector::iterator i;
Chris@1511 325
Chris@1511 326 for (i = points.begin(); i != points.end(); ++i) {
Chris@1511 327
Chris@1511 328 int y0 = getYForValue(v, i->getValue());
Chris@1511 329 int y1 = getYForValue(v, i->getValue() + fabsf(i->getLevel()));
Chris@1511 330
Chris@1511 331 if (pos.y() >= y0 && pos.y() <= y1) {
Chris@1511 332 box = *i;
Chris@1511 333 break;
Chris@1511 334 }
Chris@1511 335 }
Chris@1511 336
Chris@1511 337 if (i == points.end()) return tr("No local points");
Chris@1511 338
Chris@1511 339 RealTime rt = RealTime::frame2RealTime(box.getFrame(),
Chris@1511 340 model->getSampleRate());
Chris@1511 341 RealTime rd = RealTime::frame2RealTime(box.getDuration(),
Chris@1511 342 model->getSampleRate());
Chris@1511 343
Chris@1511 344 QString rangeText;
Chris@1511 345
Chris@1511 346 rangeText = tr("%1 %2 - %3 %4")
Chris@1511 347 .arg(box.getValue()).arg(getScaleUnits())
Chris@1511 348 .arg(box.getValue() + fabsf(box.getLevel())).arg(getScaleUnits());
Chris@1511 349
Chris@1511 350 QString text;
Chris@1511 351
Chris@1511 352 if (box.getLabel() == "") {
Chris@1518 353 text = QString(tr("Time:\t%1\nDuration:\t%2\nValue:\t%3\nNo label"))
Chris@1511 354 .arg(rt.toText(true).c_str())
Chris@1511 355 .arg(rd.toText(true).c_str())
Chris@1511 356 .arg(rangeText);
Chris@1511 357 } else {
Chris@1518 358 text = QString(tr("Time:\t%1\nDuration:\t%2\nValue:\t%3\nLabel:\t%4"))
Chris@1511 359 .arg(rt.toText(true).c_str())
Chris@1511 360 .arg(rd.toText(true).c_str())
Chris@1511 361 .arg(rangeText)
Chris@1511 362 .arg(box.getLabel());
Chris@1511 363 }
Chris@1511 364
Chris@1511 365 pos = QPoint(v->getXForFrame(box.getFrame()),
Chris@1511 366 getYForValue(v, box.getValue()));
Chris@1511 367 return text;
Chris@1511 368 }
Chris@1511 369
Chris@1511 370 bool
Chris@1518 371 BoxLayer::snapToFeatureFrame(LayerGeometryProvider *v,
Chris@1511 372 sv_frame_t &frame,
Chris@1511 373 int &resolution,
Chris@1511 374 SnapType snap) const
Chris@1511 375 {
Chris@1518 376 auto model = ModelById::getAs<BoxModel>(m_model);
Chris@1511 377 if (!model) {
Chris@1511 378 return Layer::snapToFeatureFrame(v, frame, resolution, snap);
Chris@1511 379 }
Chris@1511 380
Chris@1511 381 // SnapLeft / SnapRight: return frame of nearest feature in that
Chris@1511 382 // direction no matter how far away
Chris@1511 383 //
Chris@1511 384 // SnapNeighbouring: return frame of feature that would be used in
Chris@1511 385 // an editing operation, i.e. closest feature in either direction
Chris@1511 386 // but only if it is "close enough"
Chris@1511 387
Chris@1511 388 resolution = model->getResolution();
Chris@1511 389
Chris@1511 390 if (snap == SnapNeighbouring) {
Chris@1511 391 EventVector points = getLocalPoints(v, v->getXForFrame(frame));
Chris@1511 392 if (points.empty()) return false;
Chris@1511 393 frame = points.begin()->getFrame();
Chris@1511 394 return true;
Chris@1511 395 }
Chris@1511 396
Chris@1511 397 // Normally we snap to the start frame of whichever event we
Chris@1511 398 // find. However here, for SnapRight only, if the end frame of
Chris@1511 399 // whichever event we would have snapped to had we been snapping
Chris@1511 400 // left is closer than the start frame of the next event to the
Chris@1511 401 // right, then we snap to that frame instead. Clear?
Chris@1511 402
Chris@1511 403 Event left;
Chris@1511 404 bool haveLeft = false;
Chris@1511 405 if (model->getNearestEventMatching
Chris@1511 406 (frame, [](Event) { return true; }, EventSeries::Backward, left)) {
Chris@1511 407 haveLeft = true;
Chris@1511 408 }
Chris@1511 409
Chris@1511 410 if (snap == SnapLeft) {
Chris@1511 411 frame = left.getFrame();
Chris@1511 412 return haveLeft;
Chris@1511 413 }
Chris@1511 414
Chris@1511 415 Event right;
Chris@1511 416 bool haveRight = false;
Chris@1511 417 if (model->getNearestEventMatching
Chris@1511 418 (frame, [](Event) { return true; }, EventSeries::Forward, right)) {
Chris@1511 419 haveRight = true;
Chris@1511 420 }
Chris@1511 421
Chris@1511 422 if (haveLeft) {
Chris@1511 423 sv_frame_t leftEnd = left.getFrame() + left.getDuration();
Chris@1511 424 if (leftEnd > frame) {
Chris@1511 425 if (haveRight) {
Chris@1511 426 if (leftEnd - frame < right.getFrame() - frame) {
Chris@1511 427 frame = leftEnd;
Chris@1511 428 } else {
Chris@1511 429 frame = right.getFrame();
Chris@1511 430 }
Chris@1511 431 } else {
Chris@1511 432 frame = leftEnd;
Chris@1511 433 }
Chris@1511 434 return true;
Chris@1511 435 }
Chris@1511 436 }
Chris@1511 437
Chris@1511 438 if (haveRight) {
Chris@1511 439 frame = right.getFrame();
Chris@1511 440 return true;
Chris@1511 441 }
Chris@1511 442
Chris@1511 443 return false;
Chris@1511 444 }
Chris@1511 445
Chris@1511 446 QString
Chris@1518 447 BoxLayer::getScaleUnits() const
Chris@1511 448 {
Chris@1518 449 auto model = ModelById::getAs<BoxModel>(m_model);
Chris@1511 450 if (model) return model->getScaleUnits();
Chris@1511 451 else return "";
Chris@1511 452 }
Chris@1511 453
Chris@1511 454 void
Chris@1518 455 BoxLayer::getScaleExtents(LayerGeometryProvider *v,
Chris@1511 456 double &min, double &max,
Chris@1511 457 bool &log) const
Chris@1511 458 {
Chris@1511 459 min = 0.0;
Chris@1511 460 max = 0.0;
Chris@1511 461 log = false;
Chris@1511 462
Chris@1518 463 auto model = ModelById::getAs<BoxModel>(m_model);
Chris@1511 464 if (!model) return;
Chris@1511 465
Chris@1511 466 QString queryUnits;
Chris@1511 467 queryUnits = getScaleUnits();
Chris@1511 468
Chris@1511 469 if (m_verticalScale == AutoAlignScale) {
Chris@1511 470
Chris@1511 471 if (!v->getValueExtents(queryUnits, min, max, log)) {
Chris@1511 472
Chris@1518 473 min = model->getValueMinimum();
Chris@1518 474 max = model->getValueMaximum();
Chris@1511 475
Chris@1518 476 // cerr << "BoxLayer[" << this << "]::getScaleExtents: min = " << min << ", max = " << max << ", log = " << log << endl;
Chris@1511 477
Chris@1511 478 } else if (log) {
Chris@1511 479
Chris@1511 480 LogRange::mapRange(min, max);
Chris@1511 481
Chris@1518 482 // cerr << "BoxLayer[" << this << "]::getScaleExtents: min = " << min << ", max = " << max << ", log = " << log << endl;
Chris@1511 483
Chris@1511 484 }
Chris@1511 485
Chris@1511 486 } else {
Chris@1511 487
Chris@1518 488 min = model->getValueMinimum();
Chris@1518 489 max = model->getValueMaximum();
Chris@1511 490
Chris@1511 491 if (m_verticalScale == LogScale) {
Chris@1511 492 LogRange::mapRange(min, max);
Chris@1511 493 log = true;
Chris@1511 494 }
Chris@1511 495 }
Chris@1511 496
Chris@1511 497 if (max == min) max = min + 1.0;
Chris@1511 498 }
Chris@1511 499
Chris@1511 500 int
Chris@1518 501 BoxLayer::getYForValue(LayerGeometryProvider *v, double val) const
Chris@1511 502 {
Chris@1511 503 double min = 0.0, max = 0.0;
Chris@1511 504 bool logarithmic = false;
Chris@1511 505 int h = v->getPaintHeight();
Chris@1511 506
Chris@1511 507 getScaleExtents(v, min, max, logarithmic);
Chris@1511 508
Chris@1518 509 // cerr << "BoxLayer[" << this << "]::getYForValue(" << val << "): min = " << min << ", max = " << max << ", log = " << logarithmic << endl;
Chris@1511 510 // cerr << "h = " << h << ", margin = " << margin << endl;
Chris@1511 511
Chris@1511 512 if (logarithmic) {
Chris@1511 513 val = LogRange::map(val);
Chris@1511 514 }
Chris@1511 515
Chris@1511 516 return int(h - ((val - min) * h) / (max - min));
Chris@1511 517 }
Chris@1511 518
Chris@1511 519 double
Chris@1518 520 BoxLayer::getValueForY(LayerGeometryProvider *v, int y) const
Chris@1511 521 {
Chris@1511 522 double min = 0.0, max = 0.0;
Chris@1511 523 bool logarithmic = false;
Chris@1511 524 int h = v->getPaintHeight();
Chris@1511 525
Chris@1511 526 getScaleExtents(v, min, max, logarithmic);
Chris@1511 527
Chris@1511 528 double val = min + (double(h - y) * double(max - min)) / h;
Chris@1511 529
Chris@1511 530 if (logarithmic) {
Chris@1511 531 val = pow(10.0, val);
Chris@1511 532 }
Chris@1511 533
Chris@1511 534 return val;
Chris@1511 535 }
Chris@1511 536
Chris@1511 537 void
Chris@1518 538 BoxLayer::paint(LayerGeometryProvider *v, QPainter &paint,
Chris@1511 539 QRect rect) const
Chris@1511 540 {
Chris@1518 541 auto model = ModelById::getAs<BoxModel>(m_model);
Chris@1511 542 if (!model || !model->isOK()) return;
Chris@1511 543
Chris@1511 544 sv_samplerate_t sampleRate = model->getSampleRate();
Chris@1511 545 if (!sampleRate) return;
Chris@1511 546
Chris@1518 547 // Profiler profiler("BoxLayer::paint", true);
Chris@1511 548
Chris@1511 549 int x0 = rect.left() - 40, x1 = rect.right();
Chris@1511 550
Chris@1511 551 sv_frame_t wholeFrame0 = v->getFrameForX(0);
Chris@1511 552 sv_frame_t wholeFrame1 = v->getFrameForX(v->getPaintWidth());
Chris@1511 553
Chris@1511 554 EventVector points(model->getEventsSpanning(wholeFrame0,
Chris@1511 555 wholeFrame1 - wholeFrame0));
Chris@1511 556 if (points.empty()) return;
Chris@1511 557
Chris@1511 558 paint.setPen(getBaseQColor());
Chris@1511 559
Chris@1518 560 // SVDEBUG << "BoxLayer::paint: resolution is "
Chris@1511 561 // << model->getResolution() << " frames" << endl;
Chris@1511 562
Chris@1518 563 double min = model->getValueMinimum();
Chris@1518 564 double max = model->getValueMaximum();
Chris@1511 565 if (max == min) max = min + 1.0;
Chris@1511 566
Chris@1511 567 QPoint localPos;
Chris@1511 568 Event illuminatePoint(0);
Chris@1511 569 bool shouldIlluminate = false;
Chris@1511 570
Chris@1511 571 if (v->shouldIlluminateLocalFeatures(this, localPos)) {
Chris@1511 572 shouldIlluminate = getPointToDrag(v, localPos.x(), localPos.y(),
Chris@1511 573 illuminatePoint);
Chris@1511 574 }
Chris@1511 575
Chris@1511 576 paint.save();
Chris@1511 577 paint.setRenderHint(QPainter::Antialiasing, false);
Chris@1511 578
Chris@1514 579 QFontMetrics fm = paint.fontMetrics();
Chris@1514 580
Chris@1511 581 for (EventVector::const_iterator i = points.begin();
Chris@1511 582 i != points.end(); ++i) {
Chris@1511 583
Chris@1511 584 const Event &p(*i);
Chris@1511 585
Chris@1511 586 int x = v->getXForFrame(p.getFrame());
Chris@1511 587 int w = v->getXForFrame(p.getFrame() + p.getDuration()) - x;
Chris@1511 588 int y = getYForValue(v, p.getValue());
Chris@1514 589 int h = getYForValue(v, p.getValue() + fabsf(p.getLevel())) - y;
Chris@1511 590 int ex = x + w;
Chris@1514 591 int gap = v->scalePixelSize(2);
Chris@1511 592
Chris@1511 593 EventVector::const_iterator j = i;
Chris@1511 594 ++j;
Chris@1511 595
Chris@1511 596 if (j != points.end()) {
Chris@1511 597 const Event &q(*j);
Chris@1511 598 int nx = v->getXForFrame(q.getFrame());
Chris@1511 599 if (nx < ex) ex = nx;
Chris@1511 600 }
Chris@1511 601
Chris@1511 602 if (w < 1) w = 1;
Chris@1511 603
Chris@1511 604 paint.setPen(getBaseQColor());
Chris@1514 605 paint.setBrush(Qt::NoBrush);
Chris@1511 606
Chris@1514 607 if ((shouldIlluminate && illuminatePoint == p) ||
Chris@1514 608 (m_editing && m_editingPoint == p)) {
Chris@1511 609
Chris@1514 610 paint.setPen(QPen(getBaseQColor(), v->scalePixelSize(2)));
Chris@1511 611
Chris@1511 612 // Qt 5.13 deprecates QFontMetrics::width(), but its suggested
Chris@1511 613 // replacement (horizontalAdvance) was only added in Qt 5.11
Chris@1511 614 // which is too new for us
Chris@1511 615 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
Chris@1511 616
Chris@1514 617 if (abs(h) > 2 * fm.height()) {
Chris@1514 618
Chris@1514 619 QString y0label = QString("%1 %2")
Chris@1514 620 .arg(p.getValue())
Chris@1514 621 .arg(getScaleUnits());
Chris@1514 622
Chris@1514 623 QString y1label = QString("%1 %2")
Chris@1514 624 .arg(p.getValue() + fabsf(p.getLevel()))
Chris@1514 625 .arg(getScaleUnits());
Chris@1514 626
Chris@1514 627 PaintAssistant::drawVisibleText
Chris@1514 628 (v, paint,
Chris@1514 629 x - fm.width(y0label) - gap,
Chris@1514 630 y - fm.descent(),
Chris@1514 631 y0label, PaintAssistant::OutlinedText);
Chris@1514 632
Chris@1514 633 PaintAssistant::drawVisibleText
Chris@1514 634 (v, paint,
Chris@1514 635 x - fm.width(y1label) - gap,
Chris@1514 636 y + h + fm.ascent(),
Chris@1514 637 y1label, PaintAssistant::OutlinedText);
Chris@1514 638
Chris@1514 639 } else {
Chris@1514 640
Chris@1514 641 QString ylabel = QString("%1 %2 - %3 %4")
Chris@1514 642 .arg(p.getValue())
Chris@1514 643 .arg(getScaleUnits())
Chris@1514 644 .arg(p.getValue() + fabsf(p.getLevel()))
Chris@1514 645 .arg(getScaleUnits());
Chris@1514 646
Chris@1514 647 PaintAssistant::drawVisibleText
Chris@1514 648 (v, paint,
Chris@1514 649 x - fm.width(ylabel) - gap,
Chris@1514 650 y - fm.descent(),
Chris@1514 651 ylabel, PaintAssistant::OutlinedText);
Chris@1514 652 }
Chris@1514 653
Chris@1514 654 QString t0label = RealTime::frame2RealTime
Chris@1514 655 (p.getFrame(), model->getSampleRate()).toText(true).c_str();
Chris@1514 656
Chris@1514 657 QString t1label = RealTime::frame2RealTime
Chris@1514 658 (p.getFrame() + p.getDuration(), model->getSampleRate())
Chris@1514 659 .toText(true).c_str();
Chris@1514 660
Chris@1511 661 PaintAssistant::drawVisibleText
Chris@1514 662 (v, paint, x, y + fm.ascent() + gap,
Chris@1514 663 t0label, PaintAssistant::OutlinedText);
Chris@1514 664
Chris@1514 665 if (w > fm.width(t0label) + fm.width(t1label) + gap * 3) {
Chris@1514 666
Chris@1514 667 PaintAssistant::drawVisibleText
Chris@1514 668 (v, paint,
Chris@1514 669 x + w - fm.width(t1label),
Chris@1514 670 y + fm.ascent() + gap,
Chris@1514 671 t1label, PaintAssistant::OutlinedText);
Chris@1514 672
Chris@1514 673 } else {
Chris@1514 674
Chris@1514 675 PaintAssistant::drawVisibleText
Chris@1514 676 (v, paint,
Chris@1514 677 x + w - fm.width(t1label),
Chris@1514 678 y + fm.ascent() + fm.height() + gap,
Chris@1514 679 t1label, PaintAssistant::OutlinedText);
Chris@1514 680 }
Chris@1511 681 }
Chris@1511 682
Chris@1511 683 paint.drawRect(x, y, w, h);
Chris@1511 684 }
Chris@1511 685
Chris@1511 686 for (EventVector::const_iterator i = points.begin();
Chris@1511 687 i != points.end(); ++i) {
Chris@1511 688
Chris@1511 689 const Event &p(*i);
Chris@1511 690
Chris@1514 691 QString label = p.getLabel();
Chris@1514 692 if (label == "") continue;
Chris@1514 693
Chris@1514 694 if (shouldIlluminate && illuminatePoint == p) {
Chris@1514 695 continue; // already handled this in illumination special case
Chris@1514 696 }
Chris@1514 697
Chris@1511 698 int x = v->getXForFrame(p.getFrame());
Chris@1511 699 int w = v->getXForFrame(p.getFrame() + p.getDuration()) - x;
Chris@1511 700 int y = getYForValue(v, p.getValue());
Chris@1511 701
Chris@1514 702 int labelWidth = fm.width(label);
Chris@1511 703
Chris@1511 704 int gap = v->scalePixelSize(2);
Chris@1511 705
Chris@1511 706 if (x + w < x0 || x - labelWidth - gap > x1) {
Chris@1511 707 continue;
Chris@1511 708 }
Chris@1511 709
Chris@1514 710 int labelX, labelY;
Chris@1511 711
Chris@1514 712 labelX = x - labelWidth - gap;
Chris@1514 713 labelY = y - fm.descent();
Chris@1511 714
Chris@1514 715 PaintAssistant::drawVisibleText(v, paint, labelX, labelY, label,
Chris@1514 716 PaintAssistant::OutlinedText);
Chris@1511 717 }
Chris@1511 718
Chris@1511 719 paint.restore();
Chris@1511 720 }
Chris@1511 721
Chris@1511 722 int
Chris@1518 723 BoxLayer::getVerticalScaleWidth(LayerGeometryProvider *v,
Chris@1511 724 bool, QPainter &paint) const
Chris@1511 725 {
Chris@1518 726 auto model = ModelById::getAs<BoxModel>(m_model);
Chris@1511 727 if (!model || m_verticalScale == AutoAlignScale) {
Chris@1511 728 return 0;
Chris@1511 729 } else {
Chris@1511 730 if (m_verticalScale == LogScale) {
Chris@1511 731 return LogNumericalScale().getWidth(v, paint);
Chris@1511 732 } else {
Chris@1511 733 return LinearNumericalScale().getWidth(v, paint);
Chris@1511 734 }
Chris@1511 735 }
Chris@1511 736 }
Chris@1511 737
Chris@1511 738 void
Chris@1518 739 BoxLayer::paintVerticalScale(LayerGeometryProvider *v,
Chris@1511 740 bool, QPainter &paint, QRect) const
Chris@1511 741 {
Chris@1518 742 auto model = ModelById::getAs<BoxModel>(m_model);
Chris@1511 743 if (!model || model->isEmpty()) return;
Chris@1511 744
Chris@1511 745 QString unit;
Chris@1511 746 double min, max;
Chris@1511 747 bool logarithmic;
Chris@1511 748
Chris@1511 749 int w = getVerticalScaleWidth(v, false, paint);
Chris@1511 750
Chris@1511 751 getScaleExtents(v, min, max, logarithmic);
Chris@1511 752
Chris@1511 753 if (logarithmic) {
Chris@1511 754 LogNumericalScale().paintVertical(v, this, paint, 0, min, max);
Chris@1511 755 } else {
Chris@1511 756 LinearNumericalScale().paintVertical(v, this, paint, 0, min, max);
Chris@1511 757 }
Chris@1511 758
Chris@1511 759 if (getScaleUnits() != "") {
Chris@1511 760 int mw = w - 5;
Chris@1511 761 paint.drawText(5,
Chris@1511 762 5 + paint.fontMetrics().ascent(),
Chris@1511 763 TextAbbrev::abbreviate(getScaleUnits(),
Chris@1511 764 paint.fontMetrics(),
Chris@1511 765 mw));
Chris@1511 766 }
Chris@1511 767 }
Chris@1511 768
Chris@1511 769 void
Chris@1518 770 BoxLayer::drawStart(LayerGeometryProvider *v, QMouseEvent *e)
Chris@1511 771 {
Chris@1518 772 auto model = ModelById::getAs<BoxModel>(m_model);
Chris@1511 773 if (!model) return;
Chris@1511 774
Chris@1511 775 sv_frame_t frame = v->getFrameForX(e->x());
Chris@1511 776 if (frame < 0) frame = 0;
Chris@1511 777 frame = frame / model->getResolution() * model->getResolution();
Chris@1511 778
Chris@1518 779 double value = getValueForY(v, e->y());
Chris@1511 780
Chris@1518 781 m_editingPoint = Event(frame, float(value), 0, "");
Chris@1511 782 m_originalPoint = m_editingPoint;
Chris@1511 783
Chris@1511 784 if (m_editingCommand) finish(m_editingCommand);
Chris@1511 785 m_editingCommand = new ChangeEventsCommand(m_model.untyped,
Chris@1518 786 tr("Draw Box"));
Chris@1511 787 m_editingCommand->add(m_editingPoint);
Chris@1511 788
Chris@1511 789 m_editing = true;
Chris@1511 790 }
Chris@1511 791
Chris@1511 792 void
Chris@1518 793 BoxLayer::drawDrag(LayerGeometryProvider *v, QMouseEvent *e)
Chris@1511 794 {
Chris@1518 795 auto model = ModelById::getAs<BoxModel>(m_model);
Chris@1511 796 if (!model || !m_editing) return;
Chris@1511 797
Chris@1514 798 sv_frame_t dragFrame = v->getFrameForX(e->x());
Chris@1514 799 if (dragFrame < 0) dragFrame = 0;
Chris@1514 800 dragFrame = dragFrame / model->getResolution() * model->getResolution();
Chris@1511 801
Chris@1514 802 sv_frame_t eventFrame = m_originalPoint.getFrame();
Chris@1514 803 sv_frame_t eventDuration = dragFrame - eventFrame;
Chris@1514 804 if (eventDuration < 0) {
Chris@1514 805 eventFrame = eventFrame + eventDuration;
Chris@1514 806 eventDuration = -eventDuration;
Chris@1514 807 } else if (eventDuration == 0) {
Chris@1514 808 eventDuration = model->getResolution();
Chris@1514 809 }
Chris@1511 810
Chris@1518 811 double dragValue = getValueForY(v, e->y());
Chris@1514 812
Chris@1518 813 double eventValue = m_originalPoint.getValue();
Chris@1518 814 double eventFreqDiff = dragValue - eventValue;
Chris@1514 815 if (eventFreqDiff < 0) {
Chris@1518 816 eventValue = eventValue + eventFreqDiff;
Chris@1514 817 eventFreqDiff = -eventFreqDiff;
Chris@1511 818 }
Chris@1511 819
Chris@1511 820 m_editingCommand->remove(m_editingPoint);
Chris@1511 821 m_editingPoint = m_editingPoint
Chris@1514 822 .withFrame(eventFrame)
Chris@1514 823 .withDuration(eventDuration)
Chris@1518 824 .withValue(float(eventValue))
Chris@1514 825 .withLevel(float(eventFreqDiff));
Chris@1511 826 m_editingCommand->add(m_editingPoint);
Chris@1511 827 }
Chris@1511 828
Chris@1511 829 void
Chris@1518 830 BoxLayer::drawEnd(LayerGeometryProvider *, QMouseEvent *)
Chris@1511 831 {
Chris@1518 832 auto model = ModelById::getAs<BoxModel>(m_model);
Chris@1511 833 if (!model || !m_editing) return;
Chris@1511 834 finish(m_editingCommand);
Chris@1511 835 m_editingCommand = nullptr;
Chris@1511 836 m_editing = false;
Chris@1511 837 }
Chris@1511 838
Chris@1511 839 void
Chris@1518 840 BoxLayer::eraseStart(LayerGeometryProvider *v, QMouseEvent *e)
Chris@1511 841 {
Chris@1518 842 auto model = ModelById::getAs<BoxModel>(m_model);
Chris@1511 843 if (!model) return;
Chris@1511 844
Chris@1511 845 if (!getPointToDrag(v, e->x(), e->y(), m_editingPoint)) return;
Chris@1511 846
Chris@1511 847 if (m_editingCommand) {
Chris@1511 848 finish(m_editingCommand);
Chris@1511 849 m_editingCommand = nullptr;
Chris@1511 850 }
Chris@1511 851
Chris@1511 852 m_editing = true;
Chris@1511 853 }
Chris@1511 854
Chris@1511 855 void
Chris@1518 856 BoxLayer::eraseDrag(LayerGeometryProvider *, QMouseEvent *)
Chris@1511 857 {
Chris@1511 858 }
Chris@1511 859
Chris@1511 860 void
Chris@1518 861 BoxLayer::eraseEnd(LayerGeometryProvider *v, QMouseEvent *e)
Chris@1511 862 {
Chris@1518 863 auto model = ModelById::getAs<BoxModel>(m_model);
Chris@1511 864 if (!model || !m_editing) return;
Chris@1511 865
Chris@1511 866 m_editing = false;
Chris@1511 867
Chris@1511 868 Event p(0);
Chris@1511 869 if (!getPointToDrag(v, e->x(), e->y(), p)) return;
Chris@1511 870 if (p.getFrame() != m_editingPoint.getFrame() ||
Chris@1511 871 p.getValue() != m_editingPoint.getValue()) return;
Chris@1511 872
Chris@1511 873 m_editingCommand = new ChangeEventsCommand
Chris@1518 874 (m_model.untyped, tr("Erase Box"));
Chris@1511 875
Chris@1511 876 m_editingCommand->remove(m_editingPoint);
Chris@1511 877
Chris@1511 878 finish(m_editingCommand);
Chris@1511 879 m_editingCommand = nullptr;
Chris@1511 880 m_editing = false;
Chris@1511 881 }
Chris@1511 882
Chris@1511 883 void
Chris@1518 884 BoxLayer::editStart(LayerGeometryProvider *v, QMouseEvent *e)
Chris@1511 885 {
Chris@1518 886 auto model = ModelById::getAs<BoxModel>(m_model);
Chris@1511 887 if (!model) return;
Chris@1511 888
Chris@1511 889 if (!getPointToDrag(v, e->x(), e->y(), m_editingPoint)) {
Chris@1511 890 return;
Chris@1511 891 }
Chris@1511 892
Chris@1511 893 m_dragPointX = v->getXForFrame(m_editingPoint.getFrame());
Chris@1511 894 m_dragPointY = getYForValue(v, m_editingPoint.getValue());
Chris@1511 895
Chris@1511 896 m_originalPoint = m_editingPoint;
Chris@1511 897
Chris@1511 898 if (m_editingCommand) {
Chris@1511 899 finish(m_editingCommand);
Chris@1511 900 m_editingCommand = nullptr;
Chris@1511 901 }
Chris@1511 902
Chris@1511 903 m_editing = true;
Chris@1511 904 m_dragStartX = e->x();
Chris@1511 905 m_dragStartY = e->y();
Chris@1511 906 }
Chris@1511 907
Chris@1511 908 void
Chris@1518 909 BoxLayer::editDrag(LayerGeometryProvider *v, QMouseEvent *e)
Chris@1511 910 {
Chris@1518 911 auto model = ModelById::getAs<BoxModel>(m_model);
Chris@1511 912 if (!model || !m_editing) return;
Chris@1511 913
Chris@1511 914 int xdist = e->x() - m_dragStartX;
Chris@1511 915 int ydist = e->y() - m_dragStartY;
Chris@1511 916 int newx = m_dragPointX + xdist;
Chris@1511 917 int newy = m_dragPointY + ydist;
Chris@1511 918
Chris@1511 919 sv_frame_t frame = v->getFrameForX(newx);
Chris@1511 920 if (frame < 0) frame = 0;
Chris@1511 921 frame = frame / model->getResolution() * model->getResolution();
Chris@1511 922
Chris@1511 923 double value = getValueForY(v, newy);
Chris@1511 924
Chris@1511 925 if (!m_editingCommand) {
Chris@1511 926 m_editingCommand = new ChangeEventsCommand
Chris@1511 927 (m_model.untyped,
Chris@1518 928 tr("Drag Box"));
Chris@1511 929 }
Chris@1511 930
Chris@1511 931 m_editingCommand->remove(m_editingPoint);
Chris@1511 932 m_editingPoint = m_editingPoint
Chris@1511 933 .withFrame(frame)
Chris@1511 934 .withValue(float(value));
Chris@1511 935 m_editingCommand->add(m_editingPoint);
Chris@1511 936 }
Chris@1511 937
Chris@1511 938 void
Chris@1518 939 BoxLayer::editEnd(LayerGeometryProvider *, QMouseEvent *)
Chris@1511 940 {
Chris@1518 941 auto model = ModelById::getAs<BoxModel>(m_model);
Chris@1511 942 if (!model || !m_editing) return;
Chris@1511 943
Chris@1511 944 if (m_editingCommand) {
Chris@1511 945
Chris@1511 946 QString newName = m_editingCommand->getName();
Chris@1511 947
Chris@1511 948 if (m_editingPoint.getFrame() != m_originalPoint.getFrame()) {
Chris@1511 949 if (m_editingPoint.getValue() != m_originalPoint.getValue()) {
Chris@1518 950 newName = tr("Edit Box");
Chris@1511 951 } else {
Chris@1518 952 newName = tr("Relocate Box");
Chris@1511 953 }
Chris@1511 954 } else {
Chris@1511 955 newName = tr("Change Point Value");
Chris@1511 956 }
Chris@1511 957
Chris@1511 958 m_editingCommand->setName(newName);
Chris@1511 959 finish(m_editingCommand);
Chris@1511 960 }
Chris@1511 961
Chris@1511 962 m_editingCommand = nullptr;
Chris@1511 963 m_editing = false;
Chris@1511 964 }
Chris@1511 965
Chris@1511 966 bool
Chris@1518 967 BoxLayer::editOpen(LayerGeometryProvider *v, QMouseEvent *e)
Chris@1511 968 {
Chris@1518 969 auto model = ModelById::getAs<BoxModel>(m_model);
Chris@1511 970 if (!model) return false;
Chris@1511 971
Chris@1511 972 Event region(0);
Chris@1511 973 if (!getPointToDrag(v, e->x(), e->y(), region)) return false;
Chris@1511 974
Chris@1515 975 ItemEditDialog::LabelOptions labelOptions;
Chris@1518 976 labelOptions.valueLabel = tr("Minimum Value");
Chris@1518 977 labelOptions.levelLabel = tr("Value Extent");
Chris@1515 978 labelOptions.valueUnits = getScaleUnits();
Chris@1515 979 labelOptions.levelUnits = getScaleUnits();
Chris@1515 980
Chris@1511 981 ItemEditDialog *dialog = new ItemEditDialog
Chris@1511 982 (model->getSampleRate(),
Chris@1511 983 ItemEditDialog::ShowTime |
Chris@1511 984 ItemEditDialog::ShowDuration |
Chris@1511 985 ItemEditDialog::ShowValue |
Chris@1515 986 ItemEditDialog::ShowLevel |
Chris@1511 987 ItemEditDialog::ShowText,
Chris@1515 988 labelOptions);
Chris@1511 989
Chris@1511 990 dialog->setFrameTime(region.getFrame());
Chris@1511 991 dialog->setValue(region.getValue());
Chris@1515 992 dialog->setLevel(region.getLevel());
Chris@1511 993 dialog->setFrameDuration(region.getDuration());
Chris@1511 994 dialog->setText(region.getLabel());
Chris@1511 995
Chris@1511 996 if (dialog->exec() == QDialog::Accepted) {
Chris@1511 997
Chris@1518 998 Event newBox = region
Chris@1511 999 .withFrame(dialog->getFrameTime())
Chris@1511 1000 .withValue(dialog->getValue())
Chris@1515 1001 .withLevel(dialog->getLevel())
Chris@1511 1002 .withDuration(dialog->getFrameDuration())
Chris@1511 1003 .withLabel(dialog->getText());
Chris@1511 1004
Chris@1511 1005 ChangeEventsCommand *command = new ChangeEventsCommand
Chris@1518 1006 (m_model.untyped, tr("Edit Box"));
Chris@1511 1007 command->remove(region);
Chris@1518 1008 command->add(newBox);
Chris@1511 1009 finish(command);
Chris@1511 1010 }
Chris@1511 1011
Chris@1511 1012 delete dialog;
Chris@1511 1013 return true;
Chris@1511 1014 }
Chris@1511 1015
Chris@1511 1016 void
Chris@1518 1017 BoxLayer::moveSelection(Selection s, sv_frame_t newStartFrame)
Chris@1511 1018 {
Chris@1518 1019 auto model = ModelById::getAs<BoxModel>(m_model);
Chris@1511 1020 if (!model) return;
Chris@1511 1021
Chris@1511 1022 ChangeEventsCommand *command =
Chris@1511 1023 new ChangeEventsCommand(m_model.untyped, tr("Drag Selection"));
Chris@1511 1024
Chris@1511 1025 EventVector points =
Chris@1511 1026 model->getEventsStartingWithin(s.getStartFrame(), s.getDuration());
Chris@1511 1027
Chris@1511 1028 for (EventVector::iterator i = points.begin();
Chris@1511 1029 i != points.end(); ++i) {
Chris@1511 1030
Chris@1511 1031 Event newPoint = (*i)
Chris@1511 1032 .withFrame(i->getFrame() + newStartFrame - s.getStartFrame());
Chris@1511 1033 command->remove(*i);
Chris@1511 1034 command->add(newPoint);
Chris@1511 1035 }
Chris@1511 1036
Chris@1511 1037 finish(command);
Chris@1511 1038 }
Chris@1511 1039
Chris@1511 1040 void
Chris@1518 1041 BoxLayer::resizeSelection(Selection s, Selection newSize)
Chris@1511 1042 {
Chris@1518 1043 auto model = ModelById::getAs<BoxModel>(m_model);
Chris@1511 1044 if (!model || !s.getDuration()) return;
Chris@1511 1045
Chris@1511 1046 ChangeEventsCommand *command =
Chris@1511 1047 new ChangeEventsCommand(m_model.untyped, tr("Resize Selection"));
Chris@1511 1048
Chris@1511 1049 EventVector points =
Chris@1511 1050 model->getEventsStartingWithin(s.getStartFrame(), s.getDuration());
Chris@1511 1051
Chris@1511 1052 double ratio = double(newSize.getDuration()) / double(s.getDuration());
Chris@1511 1053 double oldStart = double(s.getStartFrame());
Chris@1511 1054 double newStart = double(newSize.getStartFrame());
Chris@1511 1055
Chris@1511 1056 for (Event p: points) {
Chris@1511 1057
Chris@1511 1058 double newFrame = (double(p.getFrame()) - oldStart) * ratio + newStart;
Chris@1511 1059 double newDuration = double(p.getDuration()) * ratio;
Chris@1511 1060
Chris@1511 1061 Event newPoint = p
Chris@1511 1062 .withFrame(lrint(newFrame))
Chris@1511 1063 .withDuration(lrint(newDuration));
Chris@1511 1064 command->remove(p);
Chris@1511 1065 command->add(newPoint);
Chris@1511 1066 }
Chris@1511 1067
Chris@1511 1068 finish(command);
Chris@1511 1069 }
Chris@1511 1070
Chris@1511 1071 void
Chris@1518 1072 BoxLayer::deleteSelection(Selection s)
Chris@1511 1073 {
Chris@1518 1074 auto model = ModelById::getAs<BoxModel>(m_model);
Chris@1511 1075 if (!model) return;
Chris@1511 1076
Chris@1511 1077 ChangeEventsCommand *command =
Chris@1511 1078 new ChangeEventsCommand(m_model.untyped, tr("Delete Selected Points"));
Chris@1511 1079
Chris@1511 1080 EventVector points =
Chris@1511 1081 model->getEventsStartingWithin(s.getStartFrame(), s.getDuration());
Chris@1511 1082
Chris@1511 1083 for (EventVector::iterator i = points.begin();
Chris@1511 1084 i != points.end(); ++i) {
Chris@1511 1085
Chris@1511 1086 if (s.contains(i->getFrame())) {
Chris@1511 1087 command->remove(*i);
Chris@1511 1088 }
Chris@1511 1089 }
Chris@1511 1090
Chris@1511 1091 finish(command);
Chris@1511 1092 }
Chris@1511 1093
Chris@1511 1094 void
Chris@1518 1095 BoxLayer::copy(LayerGeometryProvider *v, Selection s, Clipboard &to)
Chris@1511 1096 {
Chris@1518 1097 auto model = ModelById::getAs<BoxModel>(m_model);
Chris@1511 1098 if (!model) return;
Chris@1511 1099
Chris@1511 1100 EventVector points =
Chris@1511 1101 model->getEventsStartingWithin(s.getStartFrame(), s.getDuration());
Chris@1511 1102
Chris@1511 1103 for (Event p: points) {
Chris@1511 1104 to.addPoint(p.withReferenceFrame(alignToReference(v, p.getFrame())));
Chris@1511 1105 }
Chris@1511 1106 }
Chris@1511 1107
Chris@1511 1108 bool
Chris@1533 1109 BoxLayer::paste(LayerGeometryProvider *v, const Clipboard &from,
Chris@1533 1110 sv_frame_t /* frameOffset */, bool /* interactive */)
Chris@1511 1111 {
Chris@1518 1112 auto model = ModelById::getAs<BoxModel>(m_model);
Chris@1511 1113 if (!model) return false;
Chris@1511 1114
Chris@1511 1115 const EventVector &points = from.getPoints();
Chris@1511 1116
Chris@1511 1117 bool realign = false;
Chris@1511 1118
Chris@1511 1119 if (clipboardHasDifferentAlignment(v, from)) {
Chris@1511 1120
Chris@1511 1121 QMessageBox::StandardButton button =
Chris@1511 1122 QMessageBox::question(v->getView(), tr("Re-align pasted items?"),
Chris@1511 1123 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@1511 1124 QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel,
Chris@1511 1125 QMessageBox::Yes);
Chris@1511 1126
Chris@1511 1127 if (button == QMessageBox::Cancel) {
Chris@1511 1128 return false;
Chris@1511 1129 }
Chris@1511 1130
Chris@1511 1131 if (button == QMessageBox::Yes) {
Chris@1511 1132 realign = true;
Chris@1511 1133 }
Chris@1511 1134 }
Chris@1511 1135
Chris@1511 1136 ChangeEventsCommand *command =
Chris@1511 1137 new ChangeEventsCommand(m_model.untyped, tr("Paste"));
Chris@1511 1138
Chris@1511 1139 for (EventVector::const_iterator i = points.begin();
Chris@1511 1140 i != points.end(); ++i) {
Chris@1511 1141
Chris@1511 1142 sv_frame_t frame = 0;
Chris@1511 1143
Chris@1511 1144 if (!realign) {
Chris@1511 1145
Chris@1511 1146 frame = i->getFrame();
Chris@1511 1147
Chris@1511 1148 } else {
Chris@1511 1149
Chris@1511 1150 if (i->hasReferenceFrame()) {
Chris@1511 1151 frame = i->getReferenceFrame();
Chris@1511 1152 frame = alignFromReference(v, frame);
Chris@1511 1153 } else {
Chris@1511 1154 frame = i->getFrame();
Chris@1511 1155 }
Chris@1511 1156 }
Chris@1511 1157
Chris@1533 1158 Event p = i->withFrame(frame);
Chris@1533 1159
Chris@1511 1160 Event newPoint = p;
Chris@1511 1161 if (!p.hasValue()) {
Chris@1518 1162 newPoint = newPoint.withValue((model->getValueMinimum() +
Chris@1518 1163 model->getValueMaximum()) / 2);
Chris@1511 1164 }
Chris@1511 1165 if (!p.hasDuration()) {
Chris@1511 1166 sv_frame_t nextFrame = frame;
Chris@1511 1167 EventVector::const_iterator j = i;
Chris@1511 1168 for (; j != points.end(); ++j) {
Chris@1511 1169 if (j != i) break;
Chris@1511 1170 }
Chris@1511 1171 if (j != points.end()) {
Chris@1511 1172 nextFrame = j->getFrame();
Chris@1511 1173 }
Chris@1511 1174 if (nextFrame == frame) {
Chris@1511 1175 newPoint = newPoint.withDuration(model->getResolution());
Chris@1511 1176 } else {
Chris@1511 1177 newPoint = newPoint.withDuration(nextFrame - frame);
Chris@1511 1178 }
Chris@1511 1179 }
Chris@1511 1180
Chris@1511 1181 command->add(newPoint);
Chris@1511 1182 }
Chris@1511 1183
Chris@1511 1184 finish(command);
Chris@1511 1185 return true;
Chris@1511 1186 }
Chris@1511 1187
Chris@1511 1188 void
Chris@1518 1189 BoxLayer::toXml(QTextStream &stream,
Chris@1515 1190 QString indent, QString extraAttributes) const
Chris@1511 1191 {
Chris@1511 1192 QString s;
Chris@1511 1193
Chris@1515 1194 s += QString("verticalScale=\"%1\" ").arg(m_verticalScale);
Chris@1511 1195
Chris@1511 1196 SingleColourLayer::toXml(stream, indent, extraAttributes + " " + s);
Chris@1511 1197 }
Chris@1511 1198
Chris@1511 1199 void
Chris@1518 1200 BoxLayer::setProperties(const QXmlAttributes &attributes)
Chris@1511 1201 {
Chris@1511 1202 SingleColourLayer::setProperties(attributes);
Chris@1511 1203
Chris@1511 1204 bool ok;
Chris@1511 1205 VerticalScale scale = (VerticalScale)
Chris@1511 1206 attributes.value("verticalScale").toInt(&ok);
Chris@1511 1207 if (ok) setVerticalScale(scale);
Chris@1511 1208 }
Chris@1511 1209
Chris@1511 1210