annotate layer/BoxLayer.cpp @ 1605:ae2d5f8ff005

When asked to render the whole view width, we need to wait for the layers to be ready before we can determine what the width is
author Chris Cannam
date Thu, 30 Apr 2020 14:47:13 +0100
parents e95cefd4aa81
children
rev   line source
Chris@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@1535 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@1535 230
Chris@1535 231 SVDEBUG << "BoxLayer[" << this << "]::adoptExtents: min " << min
Chris@1535 232 << ", max " << max << ", unit " << unit << endl;
Chris@1535 233
Chris@1518 234 if (model->getScaleUnits() == "") {
Chris@1518 235 model->setScaleUnits(unit);
Chris@1518 236 return true;
Chris@1518 237 } else {
Chris@1518 238 return false;
Chris@1518 239 }
Chris@1518 240 }
Chris@1518 241
Chris@1547 242 bool
Chris@1547 243 BoxLayer::getLocalPoint(LayerGeometryProvider *v, int x, int y,
Chris@1547 244 Event &point) const
Chris@1511 245 {
Chris@1518 246 auto model = ModelById::getAs<BoxModel>(m_model);
Chris@1547 247 if (!model || !model->isReady()) return false;
Chris@1511 248
Chris@1511 249 sv_frame_t frame = v->getFrameForX(x);
Chris@1511 250
Chris@1511 251 EventVector onPoints = model->getEventsCovering(frame);
Chris@1511 252 if (onPoints.empty()) return false;
Chris@1511 253
Chris@1546 254 Event bestContaining;
Chris@1511 255 for (const auto &p: onPoints) {
Chris@1546 256 auto r = getRange(p);
Chris@1546 257 if (y > getYForValue(v, r.first) || y < getYForValue(v, r.second)) {
Chris@1546 258 SVCERR << "inPoints: rejecting " << p.toXmlString() << endl;
Chris@1546 259 continue;
Chris@1546 260 }
Chris@1546 261 SVCERR << "inPoints: looking at " << p.toXmlString() << endl;
Chris@1546 262 if (bestContaining == Event()) {
Chris@1546 263 bestContaining = p;
Chris@1546 264 continue;
Chris@1546 265 }
Chris@1546 266 auto br = getRange(bestContaining);
Chris@1546 267 if (r.first < br.first && r.second > br.second) {
Chris@1546 268 continue;
Chris@1546 269 }
Chris@1546 270 if (r.first > br.first && r.second < br.second) {
Chris@1546 271 bestContaining = p;
Chris@1546 272 continue;
Chris@1546 273 }
Chris@1546 274 if (p.getFrame() > bestContaining.getFrame() &&
Chris@1546 275 p.getFrame() + p.getDuration() <
Chris@1546 276 bestContaining.getFrame() + bestContaining.getDuration()) {
Chris@1546 277 bestContaining = p;
Chris@1546 278 continue;
Chris@1546 279 }
Chris@1546 280 }
Chris@1546 281
Chris@1546 282 if (bestContaining != Event()) {
Chris@1546 283 point = bestContaining;
Chris@1546 284 } else {
Chris@1546 285 int nearestDistance = -1;
Chris@1546 286 for (const auto &p: onPoints) {
Chris@1547 287 const auto r = getRange(p);
Chris@1546 288 int distance = std::min
Chris@1547 289 (getYForValue(v, r.first) - y,
Chris@1547 290 getYForValue(v, r.second) - y);
Chris@1546 291 if (distance < 0) distance = -distance;
Chris@1546 292 if (nearestDistance == -1 || distance < nearestDistance) {
Chris@1546 293 nearestDistance = distance;
Chris@1546 294 point = p;
Chris@1546 295 }
Chris@1511 296 }
Chris@1511 297 }
Chris@1511 298
Chris@1511 299 return true;
Chris@1511 300 }
Chris@1511 301
Chris@1511 302 QString
Chris@1518 303 BoxLayer::getLabelPreceding(sv_frame_t frame) const
Chris@1511 304 {
Chris@1518 305 auto model = ModelById::getAs<BoxModel>(m_model);
Chris@1511 306 if (!model) return "";
Chris@1511 307 EventVector points = model->getEventsStartingWithin
Chris@1511 308 (model->getStartFrame(), frame - model->getStartFrame());
Chris@1511 309 if (!points.empty()) {
Chris@1511 310 for (auto i = points.rbegin(); i != points.rend(); ++i) {
Chris@1511 311 if (i->getLabel() != QString()) {
Chris@1511 312 return i->getLabel();
Chris@1511 313 }
Chris@1511 314 }
Chris@1511 315 }
Chris@1511 316 return QString();
Chris@1511 317 }
Chris@1511 318
Chris@1511 319 QString
Chris@1518 320 BoxLayer::getFeatureDescription(LayerGeometryProvider *v,
Chris@1546 321 QPoint &pos) const
Chris@1511 322 {
Chris@1518 323 auto model = ModelById::getAs<BoxModel>(m_model);
Chris@1511 324 if (!model || !model->getSampleRate()) return "";
Chris@1547 325
Chris@1547 326 Event box;
Chris@1547 327
Chris@1547 328 if (!getLocalPoint(v, pos.x(), pos.y(), box)) {
Chris@1511 329 if (!model->isReady()) {
Chris@1511 330 return tr("In progress");
Chris@1511 331 } else {
Chris@1511 332 return tr("No local points");
Chris@1511 333 }
Chris@1511 334 }
Chris@1511 335
Chris@1511 336 RealTime rt = RealTime::frame2RealTime(box.getFrame(),
Chris@1511 337 model->getSampleRate());
Chris@1511 338 RealTime rd = RealTime::frame2RealTime(box.getDuration(),
Chris@1511 339 model->getSampleRate());
Chris@1511 340
Chris@1511 341 QString rangeText;
Chris@1547 342 auto r = getRange(box);
Chris@1547 343
Chris@1511 344 rangeText = tr("%1 %2 - %3 %4")
Chris@1547 345 .arg(r.first).arg(getScaleUnits())
Chris@1547 346 .arg(r.second).arg(getScaleUnits());
Chris@1511 347
Chris@1511 348 QString text;
Chris@1511 349
Chris@1511 350 if (box.getLabel() == "") {
Chris@1518 351 text = QString(tr("Time:\t%1\nDuration:\t%2\nValue:\t%3\nNo label"))
Chris@1511 352 .arg(rt.toText(true).c_str())
Chris@1511 353 .arg(rd.toText(true).c_str())
Chris@1511 354 .arg(rangeText);
Chris@1511 355 } else {
Chris@1518 356 text = QString(tr("Time:\t%1\nDuration:\t%2\nValue:\t%3\nLabel:\t%4"))
Chris@1511 357 .arg(rt.toText(true).c_str())
Chris@1511 358 .arg(rd.toText(true).c_str())
Chris@1511 359 .arg(rangeText)
Chris@1511 360 .arg(box.getLabel());
Chris@1511 361 }
Chris@1511 362
Chris@1511 363 pos = QPoint(v->getXForFrame(box.getFrame()),
Chris@1511 364 getYForValue(v, box.getValue()));
Chris@1511 365 return text;
Chris@1511 366 }
Chris@1511 367
Chris@1511 368 bool
Chris@1518 369 BoxLayer::snapToFeatureFrame(LayerGeometryProvider *v,
Chris@1546 370 sv_frame_t &frame,
Chris@1546 371 int &resolution,
Chris@1547 372 SnapType snap,
Chris@1547 373 int ycoord) const
Chris@1511 374 {
Chris@1518 375 auto model = ModelById::getAs<BoxModel>(m_model);
Chris@1511 376 if (!model) {
Chris@1547 377 return Layer::snapToFeatureFrame(v, frame, resolution, snap, ycoord);
Chris@1511 378 }
Chris@1511 379
Chris@1511 380 // SnapLeft / SnapRight: return frame of nearest feature in that
Chris@1511 381 // direction no matter how far away
Chris@1511 382 //
Chris@1511 383 // SnapNeighbouring: return frame of feature that would be used in
Chris@1511 384 // an editing operation, i.e. closest feature in either direction
Chris@1511 385 // but only if it is "close enough"
Chris@1511 386
Chris@1511 387 resolution = model->getResolution();
Chris@1511 388
Chris@1547 389 Event containing;
Chris@1547 390
Chris@1547 391 if (getLocalPoint(v, v->getXForFrame(frame), ycoord, containing)) {
Chris@1547 392
Chris@1547 393 switch (snap) {
Chris@1547 394
Chris@1547 395 case SnapLeft:
Chris@1547 396 case SnapNeighbouring:
Chris@1547 397 frame = containing.getFrame();
Chris@1547 398 return true;
Chris@1547 399
Chris@1547 400 case SnapRight:
Chris@1547 401 frame = containing.getFrame() + containing.getDuration();
Chris@1547 402 return true;
Chris@1547 403 }
Chris@1547 404 }
Chris@1547 405
Chris@1511 406 if (snap == SnapNeighbouring) {
Chris@1547 407 return false;
Chris@1511 408 }
Chris@1511 409
Chris@1547 410 // We aren't actually contained (in time) by any single event, so
Chris@1547 411 // seek the next one in the relevant direction
Chris@1547 412
Chris@1547 413 Event e;
Chris@1511 414
Chris@1547 415 if (snap == SnapLeft) {
Chris@1547 416 if (model->getNearestEventMatching
Chris@1547 417 (frame, [](Event) { return true; }, EventSeries::Backward, e)) {
Chris@1511 418
Chris@1547 419 if (e.getFrame() + e.getDuration() < frame) {
Chris@1547 420 frame = e.getFrame() + e.getDuration();
Chris@1511 421 } else {
Chris@1547 422 frame = e.getFrame();
Chris@1511 423 }
Chris@1511 424 return true;
Chris@1511 425 }
Chris@1511 426 }
Chris@1547 427
Chris@1547 428 if (snap == SnapRight) {
Chris@1547 429 if (model->getNearestEventMatching
Chris@1547 430 (frame, [](Event) { return true; }, EventSeries::Forward, e)) {
Chris@1511 431
Chris@1547 432 frame = e.getFrame();
Chris@1547 433 return true;
Chris@1547 434 }
Chris@1511 435 }
Chris@1511 436
Chris@1511 437 return false;
Chris@1511 438 }
Chris@1511 439
Chris@1511 440 QString
Chris@1518 441 BoxLayer::getScaleUnits() const
Chris@1511 442 {
Chris@1518 443 auto model = ModelById::getAs<BoxModel>(m_model);
Chris@1511 444 if (model) return model->getScaleUnits();
Chris@1511 445 else return "";
Chris@1511 446 }
Chris@1511 447
Chris@1511 448 void
Chris@1518 449 BoxLayer::getScaleExtents(LayerGeometryProvider *v,
Chris@1511 450 double &min, double &max,
Chris@1511 451 bool &log) const
Chris@1511 452 {
Chris@1511 453 min = 0.0;
Chris@1511 454 max = 0.0;
Chris@1511 455 log = false;
Chris@1511 456
Chris@1518 457 auto model = ModelById::getAs<BoxModel>(m_model);
Chris@1511 458 if (!model) return;
Chris@1511 459
Chris@1511 460 QString queryUnits;
Chris@1511 461 queryUnits = getScaleUnits();
Chris@1511 462
Chris@1511 463 if (m_verticalScale == AutoAlignScale) {
Chris@1511 464
Chris@1537 465 if (!v->getVisibleExtentsForUnit(queryUnits, min, max, log)) {
Chris@1511 466
Chris@1518 467 min = model->getValueMinimum();
Chris@1518 468 max = model->getValueMaximum();
Chris@1511 469
Chris@1518 470 // cerr << "BoxLayer[" << this << "]::getScaleExtents: min = " << min << ", max = " << max << ", log = " << log << endl;
Chris@1511 471
Chris@1511 472 } else if (log) {
Chris@1511 473
Chris@1511 474 LogRange::mapRange(min, max);
Chris@1511 475
Chris@1518 476 // cerr << "BoxLayer[" << this << "]::getScaleExtents: min = " << min << ", max = " << max << ", log = " << log << endl;
Chris@1511 477
Chris@1511 478 }
Chris@1511 479
Chris@1511 480 } else {
Chris@1511 481
Chris@1518 482 min = model->getValueMinimum();
Chris@1518 483 max = model->getValueMaximum();
Chris@1511 484
Chris@1511 485 if (m_verticalScale == LogScale) {
Chris@1511 486 LogRange::mapRange(min, max);
Chris@1511 487 log = true;
Chris@1511 488 }
Chris@1511 489 }
Chris@1511 490
Chris@1511 491 if (max == min) max = min + 1.0;
Chris@1511 492 }
Chris@1511 493
Chris@1511 494 int
Chris@1518 495 BoxLayer::getYForValue(LayerGeometryProvider *v, double val) const
Chris@1511 496 {
Chris@1511 497 double min = 0.0, max = 0.0;
Chris@1511 498 bool logarithmic = false;
Chris@1511 499 int h = v->getPaintHeight();
Chris@1511 500
Chris@1511 501 getScaleExtents(v, min, max, logarithmic);
Chris@1511 502
Chris@1518 503 // cerr << "BoxLayer[" << this << "]::getYForValue(" << val << "): min = " << min << ", max = " << max << ", log = " << logarithmic << endl;
Chris@1511 504 // cerr << "h = " << h << ", margin = " << margin << endl;
Chris@1511 505
Chris@1511 506 if (logarithmic) {
Chris@1511 507 val = LogRange::map(val);
Chris@1511 508 }
Chris@1511 509
Chris@1511 510 return int(h - ((val - min) * h) / (max - min));
Chris@1511 511 }
Chris@1511 512
Chris@1511 513 double
Chris@1518 514 BoxLayer::getValueForY(LayerGeometryProvider *v, int y) const
Chris@1511 515 {
Chris@1511 516 double min = 0.0, max = 0.0;
Chris@1511 517 bool logarithmic = false;
Chris@1511 518 int h = v->getPaintHeight();
Chris@1511 519
Chris@1511 520 getScaleExtents(v, min, max, logarithmic);
Chris@1511 521
Chris@1511 522 double val = min + (double(h - y) * double(max - min)) / h;
Chris@1511 523
Chris@1511 524 if (logarithmic) {
Chris@1511 525 val = pow(10.0, val);
Chris@1511 526 }
Chris@1511 527
Chris@1511 528 return val;
Chris@1511 529 }
Chris@1511 530
Chris@1511 531 void
Chris@1518 532 BoxLayer::paint(LayerGeometryProvider *v, QPainter &paint,
Chris@1511 533 QRect rect) const
Chris@1511 534 {
Chris@1518 535 auto model = ModelById::getAs<BoxModel>(m_model);
Chris@1511 536 if (!model || !model->isOK()) return;
Chris@1511 537
Chris@1511 538 sv_samplerate_t sampleRate = model->getSampleRate();
Chris@1511 539 if (!sampleRate) return;
Chris@1511 540
Chris@1518 541 // Profiler profiler("BoxLayer::paint", true);
Chris@1511 542
Chris@1550 543 int x0 = rect.left() - 40;
Chris@1550 544 int x1 = x0 + rect.width() + 80;
Chris@1511 545
Chris@1511 546 sv_frame_t wholeFrame0 = v->getFrameForX(0);
Chris@1511 547 sv_frame_t wholeFrame1 = v->getFrameForX(v->getPaintWidth());
Chris@1511 548
Chris@1511 549 EventVector points(model->getEventsSpanning(wholeFrame0,
Chris@1511 550 wholeFrame1 - wholeFrame0));
Chris@1511 551 if (points.empty()) return;
Chris@1511 552
Chris@1511 553 paint.setPen(getBaseQColor());
Chris@1511 554
Chris@1518 555 // SVDEBUG << "BoxLayer::paint: resolution is "
Chris@1511 556 // << model->getResolution() << " frames" << endl;
Chris@1511 557
Chris@1518 558 double min = model->getValueMinimum();
Chris@1518 559 double max = model->getValueMaximum();
Chris@1511 560 if (max == min) max = min + 1.0;
Chris@1511 561
Chris@1511 562 QPoint localPos;
Chris@1511 563 Event illuminatePoint(0);
Chris@1511 564 bool shouldIlluminate = false;
Chris@1511 565
Chris@1511 566 if (v->shouldIlluminateLocalFeatures(this, localPos)) {
Chris@1547 567 shouldIlluminate = getLocalPoint(v, localPos.x(), localPos.y(),
Chris@1547 568 illuminatePoint);
Chris@1511 569 }
Chris@1511 570
Chris@1511 571 paint.save();
Chris@1511 572 paint.setRenderHint(QPainter::Antialiasing, false);
Chris@1511 573
Chris@1514 574 QFontMetrics fm = paint.fontMetrics();
Chris@1514 575
Chris@1511 576 for (EventVector::const_iterator i = points.begin();
Chris@1511 577 i != points.end(); ++i) {
Chris@1511 578
Chris@1511 579 const Event &p(*i);
Chris@1547 580 const auto r = getRange(p);
Chris@1511 581
Chris@1511 582 int x = v->getXForFrame(p.getFrame());
Chris@1511 583 int w = v->getXForFrame(p.getFrame() + p.getDuration()) - x;
Chris@1547 584 int y = getYForValue(v, r.first);
Chris@1547 585 int h = getYForValue(v, r.second) - y;
Chris@1511 586 int ex = x + w;
Chris@1514 587 int gap = v->scalePixelSize(2);
Chris@1511 588
Chris@1511 589 EventVector::const_iterator j = i;
Chris@1511 590 ++j;
Chris@1511 591
Chris@1511 592 if (j != points.end()) {
Chris@1511 593 const Event &q(*j);
Chris@1511 594 int nx = v->getXForFrame(q.getFrame());
Chris@1511 595 if (nx < ex) ex = nx;
Chris@1511 596 }
Chris@1511 597
Chris@1511 598 if (w < 1) w = 1;
Chris@1511 599
Chris@1511 600 paint.setPen(getBaseQColor());
Chris@1514 601 paint.setBrush(Qt::NoBrush);
Chris@1511 602
Chris@1514 603 if ((shouldIlluminate && illuminatePoint == p) ||
Chris@1514 604 (m_editing && m_editingPoint == p)) {
Chris@1511 605
Chris@1514 606 paint.setPen(QPen(getBaseQColor(), v->scalePixelSize(2)));
Chris@1511 607
Chris@1511 608 // Qt 5.13 deprecates QFontMetrics::width(), but its suggested
Chris@1511 609 // replacement (horizontalAdvance) was only added in Qt 5.11
Chris@1511 610 // which is too new for us
Chris@1511 611 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
Chris@1511 612
Chris@1514 613 if (abs(h) > 2 * fm.height()) {
Chris@1514 614
Chris@1514 615 QString y0label = QString("%1 %2")
Chris@1547 616 .arg(r.first)
Chris@1514 617 .arg(getScaleUnits());
Chris@1514 618
Chris@1514 619 QString y1label = QString("%1 %2")
Chris@1547 620 .arg(r.second)
Chris@1514 621 .arg(getScaleUnits());
Chris@1514 622
Chris@1514 623 PaintAssistant::drawVisibleText
Chris@1514 624 (v, paint,
Chris@1514 625 x - fm.width(y0label) - gap,
Chris@1514 626 y - fm.descent(),
Chris@1514 627 y0label, PaintAssistant::OutlinedText);
Chris@1514 628
Chris@1514 629 PaintAssistant::drawVisibleText
Chris@1514 630 (v, paint,
Chris@1514 631 x - fm.width(y1label) - gap,
Chris@1514 632 y + h + fm.ascent(),
Chris@1514 633 y1label, PaintAssistant::OutlinedText);
Chris@1514 634
Chris@1514 635 } else {
Chris@1514 636
Chris@1514 637 QString ylabel = QString("%1 %2 - %3 %4")
Chris@1547 638 .arg(r.first)
Chris@1514 639 .arg(getScaleUnits())
Chris@1547 640 .arg(r.second)
Chris@1514 641 .arg(getScaleUnits());
Chris@1514 642
Chris@1514 643 PaintAssistant::drawVisibleText
Chris@1514 644 (v, paint,
Chris@1514 645 x - fm.width(ylabel) - gap,
Chris@1514 646 y - fm.descent(),
Chris@1514 647 ylabel, PaintAssistant::OutlinedText);
Chris@1514 648 }
Chris@1514 649
Chris@1514 650 QString t0label = RealTime::frame2RealTime
Chris@1514 651 (p.getFrame(), model->getSampleRate()).toText(true).c_str();
Chris@1514 652
Chris@1514 653 QString t1label = RealTime::frame2RealTime
Chris@1514 654 (p.getFrame() + p.getDuration(), model->getSampleRate())
Chris@1514 655 .toText(true).c_str();
Chris@1514 656
Chris@1511 657 PaintAssistant::drawVisibleText
Chris@1514 658 (v, paint, x, y + fm.ascent() + gap,
Chris@1514 659 t0label, PaintAssistant::OutlinedText);
Chris@1514 660
Chris@1514 661 if (w > fm.width(t0label) + fm.width(t1label) + gap * 3) {
Chris@1514 662
Chris@1514 663 PaintAssistant::drawVisibleText
Chris@1514 664 (v, paint,
Chris@1514 665 x + w - fm.width(t1label),
Chris@1514 666 y + fm.ascent() + gap,
Chris@1514 667 t1label, PaintAssistant::OutlinedText);
Chris@1514 668
Chris@1514 669 } else {
Chris@1514 670
Chris@1514 671 PaintAssistant::drawVisibleText
Chris@1514 672 (v, paint,
Chris@1514 673 x + w - fm.width(t1label),
Chris@1514 674 y + fm.ascent() + fm.height() + gap,
Chris@1514 675 t1label, PaintAssistant::OutlinedText);
Chris@1514 676 }
Chris@1511 677 }
Chris@1511 678
Chris@1511 679 paint.drawRect(x, y, w, h);
Chris@1511 680 }
Chris@1511 681
Chris@1511 682 for (EventVector::const_iterator i = points.begin();
Chris@1511 683 i != points.end(); ++i) {
Chris@1511 684
Chris@1511 685 const Event &p(*i);
Chris@1511 686
Chris@1514 687 QString label = p.getLabel();
Chris@1514 688 if (label == "") continue;
Chris@1514 689
Chris@1514 690 if (shouldIlluminate && illuminatePoint == p) {
Chris@1514 691 continue; // already handled this in illumination special case
Chris@1514 692 }
Chris@1514 693
Chris@1511 694 int x = v->getXForFrame(p.getFrame());
Chris@1511 695 int w = v->getXForFrame(p.getFrame() + p.getDuration()) - x;
Chris@1511 696 int y = getYForValue(v, p.getValue());
Chris@1511 697
Chris@1514 698 int labelWidth = fm.width(label);
Chris@1511 699
Chris@1511 700 int gap = v->scalePixelSize(2);
Chris@1511 701
Chris@1511 702 if (x + w < x0 || x - labelWidth - gap > x1) {
Chris@1511 703 continue;
Chris@1511 704 }
Chris@1511 705
Chris@1514 706 int labelX, labelY;
Chris@1511 707
Chris@1514 708 labelX = x - labelWidth - gap;
Chris@1514 709 labelY = y - fm.descent();
Chris@1511 710
Chris@1514 711 PaintAssistant::drawVisibleText(v, paint, labelX, labelY, label,
Chris@1514 712 PaintAssistant::OutlinedText);
Chris@1511 713 }
Chris@1511 714
Chris@1511 715 paint.restore();
Chris@1511 716 }
Chris@1511 717
Chris@1511 718 int
Chris@1518 719 BoxLayer::getVerticalScaleWidth(LayerGeometryProvider *v,
Chris@1511 720 bool, QPainter &paint) const
Chris@1511 721 {
Chris@1518 722 auto model = ModelById::getAs<BoxModel>(m_model);
Chris@1511 723 if (!model || m_verticalScale == AutoAlignScale) {
Chris@1511 724 return 0;
Chris@1511 725 } else {
Chris@1511 726 if (m_verticalScale == LogScale) {
Chris@1511 727 return LogNumericalScale().getWidth(v, paint);
Chris@1511 728 } else {
Chris@1511 729 return LinearNumericalScale().getWidth(v, paint);
Chris@1511 730 }
Chris@1511 731 }
Chris@1511 732 }
Chris@1511 733
Chris@1511 734 void
Chris@1518 735 BoxLayer::paintVerticalScale(LayerGeometryProvider *v,
Chris@1511 736 bool, QPainter &paint, QRect) const
Chris@1511 737 {
Chris@1518 738 auto model = ModelById::getAs<BoxModel>(m_model);
Chris@1511 739 if (!model || model->isEmpty()) return;
Chris@1511 740
Chris@1511 741 QString unit;
Chris@1511 742 double min, max;
Chris@1511 743 bool logarithmic;
Chris@1511 744
Chris@1511 745 int w = getVerticalScaleWidth(v, false, paint);
Chris@1511 746
Chris@1511 747 getScaleExtents(v, min, max, logarithmic);
Chris@1511 748
Chris@1511 749 if (logarithmic) {
Chris@1511 750 LogNumericalScale().paintVertical(v, this, paint, 0, min, max);
Chris@1511 751 } else {
Chris@1511 752 LinearNumericalScale().paintVertical(v, this, paint, 0, min, max);
Chris@1511 753 }
Chris@1511 754
Chris@1511 755 if (getScaleUnits() != "") {
Chris@1511 756 int mw = w - 5;
Chris@1511 757 paint.drawText(5,
Chris@1511 758 5 + paint.fontMetrics().ascent(),
Chris@1511 759 TextAbbrev::abbreviate(getScaleUnits(),
Chris@1511 760 paint.fontMetrics(),
Chris@1511 761 mw));
Chris@1511 762 }
Chris@1511 763 }
Chris@1511 764
Chris@1511 765 void
Chris@1518 766 BoxLayer::drawStart(LayerGeometryProvider *v, QMouseEvent *e)
Chris@1511 767 {
Chris@1518 768 auto model = ModelById::getAs<BoxModel>(m_model);
Chris@1511 769 if (!model) return;
Chris@1511 770
Chris@1511 771 sv_frame_t frame = v->getFrameForX(e->x());
Chris@1511 772 if (frame < 0) frame = 0;
Chris@1511 773 frame = frame / model->getResolution() * model->getResolution();
Chris@1511 774
Chris@1518 775 double value = getValueForY(v, e->y());
Chris@1511 776
Chris@1518 777 m_editingPoint = Event(frame, float(value), 0, "");
Chris@1511 778 m_originalPoint = m_editingPoint;
Chris@1511 779
Chris@1511 780 if (m_editingCommand) finish(m_editingCommand);
Chris@1511 781 m_editingCommand = new ChangeEventsCommand(m_model.untyped,
Chris@1518 782 tr("Draw Box"));
Chris@1511 783 m_editingCommand->add(m_editingPoint);
Chris@1511 784
Chris@1511 785 m_editing = true;
Chris@1511 786 }
Chris@1511 787
Chris@1511 788 void
Chris@1518 789 BoxLayer::drawDrag(LayerGeometryProvider *v, QMouseEvent *e)
Chris@1511 790 {
Chris@1518 791 auto model = ModelById::getAs<BoxModel>(m_model);
Chris@1511 792 if (!model || !m_editing) return;
Chris@1511 793
Chris@1514 794 sv_frame_t dragFrame = v->getFrameForX(e->x());
Chris@1514 795 if (dragFrame < 0) dragFrame = 0;
Chris@1514 796 dragFrame = dragFrame / model->getResolution() * model->getResolution();
Chris@1511 797
Chris@1514 798 sv_frame_t eventFrame = m_originalPoint.getFrame();
Chris@1514 799 sv_frame_t eventDuration = dragFrame - eventFrame;
Chris@1514 800 if (eventDuration < 0) {
Chris@1514 801 eventFrame = eventFrame + eventDuration;
Chris@1514 802 eventDuration = -eventDuration;
Chris@1514 803 } else if (eventDuration == 0) {
Chris@1514 804 eventDuration = model->getResolution();
Chris@1514 805 }
Chris@1511 806
Chris@1518 807 double dragValue = getValueForY(v, e->y());
Chris@1514 808
Chris@1518 809 double eventValue = m_originalPoint.getValue();
Chris@1518 810 double eventFreqDiff = dragValue - eventValue;
Chris@1514 811 if (eventFreqDiff < 0) {
Chris@1518 812 eventValue = eventValue + eventFreqDiff;
Chris@1514 813 eventFreqDiff = -eventFreqDiff;
Chris@1511 814 }
Chris@1511 815
Chris@1511 816 m_editingCommand->remove(m_editingPoint);
Chris@1511 817 m_editingPoint = m_editingPoint
Chris@1514 818 .withFrame(eventFrame)
Chris@1514 819 .withDuration(eventDuration)
Chris@1518 820 .withValue(float(eventValue))
Chris@1514 821 .withLevel(float(eventFreqDiff));
Chris@1511 822 m_editingCommand->add(m_editingPoint);
Chris@1511 823 }
Chris@1511 824
Chris@1511 825 void
Chris@1518 826 BoxLayer::drawEnd(LayerGeometryProvider *, QMouseEvent *)
Chris@1511 827 {
Chris@1518 828 auto model = ModelById::getAs<BoxModel>(m_model);
Chris@1511 829 if (!model || !m_editing) return;
Chris@1511 830 finish(m_editingCommand);
Chris@1511 831 m_editingCommand = nullptr;
Chris@1511 832 m_editing = false;
Chris@1511 833 }
Chris@1511 834
Chris@1511 835 void
Chris@1518 836 BoxLayer::eraseStart(LayerGeometryProvider *v, QMouseEvent *e)
Chris@1511 837 {
Chris@1518 838 auto model = ModelById::getAs<BoxModel>(m_model);
Chris@1511 839 if (!model) return;
Chris@1511 840
Chris@1547 841 if (!getLocalPoint(v, e->x(), e->y(), m_editingPoint)) return;
Chris@1511 842
Chris@1511 843 if (m_editingCommand) {
Chris@1511 844 finish(m_editingCommand);
Chris@1511 845 m_editingCommand = nullptr;
Chris@1511 846 }
Chris@1511 847
Chris@1511 848 m_editing = true;
Chris@1511 849 }
Chris@1511 850
Chris@1511 851 void
Chris@1518 852 BoxLayer::eraseDrag(LayerGeometryProvider *, QMouseEvent *)
Chris@1511 853 {
Chris@1511 854 }
Chris@1511 855
Chris@1511 856 void
Chris@1518 857 BoxLayer::eraseEnd(LayerGeometryProvider *v, QMouseEvent *e)
Chris@1511 858 {
Chris@1518 859 auto model = ModelById::getAs<BoxModel>(m_model);
Chris@1511 860 if (!model || !m_editing) return;
Chris@1511 861
Chris@1511 862 m_editing = false;
Chris@1511 863
Chris@1511 864 Event p(0);
Chris@1547 865 if (!getLocalPoint(v, e->x(), e->y(), p)) return;
Chris@1511 866 if (p.getFrame() != m_editingPoint.getFrame() ||
Chris@1511 867 p.getValue() != m_editingPoint.getValue()) return;
Chris@1511 868
Chris@1511 869 m_editingCommand = new ChangeEventsCommand
Chris@1518 870 (m_model.untyped, tr("Erase Box"));
Chris@1511 871
Chris@1511 872 m_editingCommand->remove(m_editingPoint);
Chris@1511 873
Chris@1511 874 finish(m_editingCommand);
Chris@1511 875 m_editingCommand = nullptr;
Chris@1511 876 m_editing = false;
Chris@1511 877 }
Chris@1511 878
Chris@1511 879 void
Chris@1518 880 BoxLayer::editStart(LayerGeometryProvider *v, QMouseEvent *e)
Chris@1511 881 {
Chris@1518 882 auto model = ModelById::getAs<BoxModel>(m_model);
Chris@1511 883 if (!model) return;
Chris@1511 884
Chris@1547 885 if (!getLocalPoint(v, e->x(), e->y(), m_editingPoint)) {
Chris@1511 886 return;
Chris@1511 887 }
Chris@1511 888
Chris@1511 889 m_dragPointX = v->getXForFrame(m_editingPoint.getFrame());
Chris@1511 890 m_dragPointY = getYForValue(v, m_editingPoint.getValue());
Chris@1511 891
Chris@1511 892 m_originalPoint = m_editingPoint;
Chris@1511 893
Chris@1511 894 if (m_editingCommand) {
Chris@1511 895 finish(m_editingCommand);
Chris@1511 896 m_editingCommand = nullptr;
Chris@1511 897 }
Chris@1511 898
Chris@1511 899 m_editing = true;
Chris@1511 900 m_dragStartX = e->x();
Chris@1511 901 m_dragStartY = e->y();
Chris@1511 902 }
Chris@1511 903
Chris@1511 904 void
Chris@1518 905 BoxLayer::editDrag(LayerGeometryProvider *v, QMouseEvent *e)
Chris@1511 906 {
Chris@1518 907 auto model = ModelById::getAs<BoxModel>(m_model);
Chris@1511 908 if (!model || !m_editing) return;
Chris@1511 909
Chris@1511 910 int xdist = e->x() - m_dragStartX;
Chris@1511 911 int ydist = e->y() - m_dragStartY;
Chris@1511 912 int newx = m_dragPointX + xdist;
Chris@1511 913 int newy = m_dragPointY + ydist;
Chris@1511 914
Chris@1511 915 sv_frame_t frame = v->getFrameForX(newx);
Chris@1511 916 if (frame < 0) frame = 0;
Chris@1511 917 frame = frame / model->getResolution() * model->getResolution();
Chris@1511 918
Chris@1511 919 double value = getValueForY(v, newy);
Chris@1511 920
Chris@1511 921 if (!m_editingCommand) {
Chris@1511 922 m_editingCommand = new ChangeEventsCommand
Chris@1511 923 (m_model.untyped,
Chris@1518 924 tr("Drag Box"));
Chris@1511 925 }
Chris@1511 926
Chris@1511 927 m_editingCommand->remove(m_editingPoint);
Chris@1511 928 m_editingPoint = m_editingPoint
Chris@1511 929 .withFrame(frame)
Chris@1511 930 .withValue(float(value));
Chris@1511 931 m_editingCommand->add(m_editingPoint);
Chris@1511 932 }
Chris@1511 933
Chris@1511 934 void
Chris@1518 935 BoxLayer::editEnd(LayerGeometryProvider *, QMouseEvent *)
Chris@1511 936 {
Chris@1518 937 auto model = ModelById::getAs<BoxModel>(m_model);
Chris@1511 938 if (!model || !m_editing) return;
Chris@1511 939
Chris@1511 940 if (m_editingCommand) {
Chris@1511 941
Chris@1511 942 QString newName = m_editingCommand->getName();
Chris@1511 943
Chris@1511 944 if (m_editingPoint.getFrame() != m_originalPoint.getFrame()) {
Chris@1511 945 if (m_editingPoint.getValue() != m_originalPoint.getValue()) {
Chris@1518 946 newName = tr("Edit Box");
Chris@1511 947 } else {
Chris@1518 948 newName = tr("Relocate Box");
Chris@1511 949 }
Chris@1511 950 } else {
Chris@1511 951 newName = tr("Change Point Value");
Chris@1511 952 }
Chris@1511 953
Chris@1511 954 m_editingCommand->setName(newName);
Chris@1511 955 finish(m_editingCommand);
Chris@1511 956 }
Chris@1511 957
Chris@1511 958 m_editingCommand = nullptr;
Chris@1511 959 m_editing = false;
Chris@1511 960 }
Chris@1511 961
Chris@1511 962 bool
Chris@1518 963 BoxLayer::editOpen(LayerGeometryProvider *v, QMouseEvent *e)
Chris@1511 964 {
Chris@1518 965 auto model = ModelById::getAs<BoxModel>(m_model);
Chris@1511 966 if (!model) return false;
Chris@1511 967
Chris@1511 968 Event region(0);
Chris@1547 969 if (!getLocalPoint(v, e->x(), e->y(), region)) return false;
Chris@1511 970
Chris@1515 971 ItemEditDialog::LabelOptions labelOptions;
Chris@1518 972 labelOptions.valueLabel = tr("Minimum Value");
Chris@1518 973 labelOptions.levelLabel = tr("Value Extent");
Chris@1515 974 labelOptions.valueUnits = getScaleUnits();
Chris@1515 975 labelOptions.levelUnits = getScaleUnits();
Chris@1515 976
Chris@1511 977 ItemEditDialog *dialog = new ItemEditDialog
Chris@1511 978 (model->getSampleRate(),
Chris@1511 979 ItemEditDialog::ShowTime |
Chris@1511 980 ItemEditDialog::ShowDuration |
Chris@1511 981 ItemEditDialog::ShowValue |
Chris@1515 982 ItemEditDialog::ShowLevel |
Chris@1511 983 ItemEditDialog::ShowText,
Chris@1515 984 labelOptions);
Chris@1511 985
Chris@1511 986 dialog->setFrameTime(region.getFrame());
Chris@1511 987 dialog->setValue(region.getValue());
Chris@1515 988 dialog->setLevel(region.getLevel());
Chris@1511 989 dialog->setFrameDuration(region.getDuration());
Chris@1511 990 dialog->setText(region.getLabel());
Chris@1511 991
Chris@1511 992 if (dialog->exec() == QDialog::Accepted) {
Chris@1511 993
Chris@1518 994 Event newBox = region
Chris@1511 995 .withFrame(dialog->getFrameTime())
Chris@1511 996 .withValue(dialog->getValue())
Chris@1515 997 .withLevel(dialog->getLevel())
Chris@1511 998 .withDuration(dialog->getFrameDuration())
Chris@1511 999 .withLabel(dialog->getText());
Chris@1511 1000
Chris@1511 1001 ChangeEventsCommand *command = new ChangeEventsCommand
Chris@1518 1002 (m_model.untyped, tr("Edit Box"));
Chris@1511 1003 command->remove(region);
Chris@1518 1004 command->add(newBox);
Chris@1511 1005 finish(command);
Chris@1511 1006 }
Chris@1511 1007
Chris@1511 1008 delete dialog;
Chris@1511 1009 return true;
Chris@1511 1010 }
Chris@1511 1011
Chris@1511 1012 void
Chris@1518 1013 BoxLayer::moveSelection(Selection s, sv_frame_t newStartFrame)
Chris@1511 1014 {
Chris@1518 1015 auto model = ModelById::getAs<BoxModel>(m_model);
Chris@1511 1016 if (!model) return;
Chris@1511 1017
Chris@1511 1018 ChangeEventsCommand *command =
Chris@1511 1019 new ChangeEventsCommand(m_model.untyped, tr("Drag Selection"));
Chris@1511 1020
Chris@1511 1021 EventVector points =
Chris@1511 1022 model->getEventsStartingWithin(s.getStartFrame(), s.getDuration());
Chris@1511 1023
Chris@1511 1024 for (EventVector::iterator i = points.begin();
Chris@1511 1025 i != points.end(); ++i) {
Chris@1511 1026
Chris@1511 1027 Event newPoint = (*i)
Chris@1511 1028 .withFrame(i->getFrame() + newStartFrame - s.getStartFrame());
Chris@1511 1029 command->remove(*i);
Chris@1511 1030 command->add(newPoint);
Chris@1511 1031 }
Chris@1511 1032
Chris@1511 1033 finish(command);
Chris@1511 1034 }
Chris@1511 1035
Chris@1511 1036 void
Chris@1518 1037 BoxLayer::resizeSelection(Selection s, Selection newSize)
Chris@1511 1038 {
Chris@1518 1039 auto model = ModelById::getAs<BoxModel>(m_model);
Chris@1511 1040 if (!model || !s.getDuration()) return;
Chris@1511 1041
Chris@1511 1042 ChangeEventsCommand *command =
Chris@1511 1043 new ChangeEventsCommand(m_model.untyped, tr("Resize Selection"));
Chris@1511 1044
Chris@1511 1045 EventVector points =
Chris@1511 1046 model->getEventsStartingWithin(s.getStartFrame(), s.getDuration());
Chris@1511 1047
Chris@1511 1048 double ratio = double(newSize.getDuration()) / double(s.getDuration());
Chris@1511 1049 double oldStart = double(s.getStartFrame());
Chris@1511 1050 double newStart = double(newSize.getStartFrame());
Chris@1511 1051
Chris@1511 1052 for (Event p: points) {
Chris@1511 1053
Chris@1511 1054 double newFrame = (double(p.getFrame()) - oldStart) * ratio + newStart;
Chris@1511 1055 double newDuration = double(p.getDuration()) * ratio;
Chris@1511 1056
Chris@1511 1057 Event newPoint = p
Chris@1511 1058 .withFrame(lrint(newFrame))
Chris@1511 1059 .withDuration(lrint(newDuration));
Chris@1511 1060 command->remove(p);
Chris@1511 1061 command->add(newPoint);
Chris@1511 1062 }
Chris@1511 1063
Chris@1511 1064 finish(command);
Chris@1511 1065 }
Chris@1511 1066
Chris@1511 1067 void
Chris@1518 1068 BoxLayer::deleteSelection(Selection s)
Chris@1511 1069 {
Chris@1518 1070 auto model = ModelById::getAs<BoxModel>(m_model);
Chris@1511 1071 if (!model) return;
Chris@1511 1072
Chris@1511 1073 ChangeEventsCommand *command =
Chris@1511 1074 new ChangeEventsCommand(m_model.untyped, tr("Delete Selected Points"));
Chris@1511 1075
Chris@1511 1076 EventVector points =
Chris@1511 1077 model->getEventsStartingWithin(s.getStartFrame(), s.getDuration());
Chris@1511 1078
Chris@1511 1079 for (EventVector::iterator i = points.begin();
Chris@1511 1080 i != points.end(); ++i) {
Chris@1511 1081
Chris@1511 1082 if (s.contains(i->getFrame())) {
Chris@1511 1083 command->remove(*i);
Chris@1511 1084 }
Chris@1511 1085 }
Chris@1511 1086
Chris@1511 1087 finish(command);
Chris@1511 1088 }
Chris@1511 1089
Chris@1511 1090 void
Chris@1518 1091 BoxLayer::copy(LayerGeometryProvider *v, Selection s, Clipboard &to)
Chris@1511 1092 {
Chris@1518 1093 auto model = ModelById::getAs<BoxModel>(m_model);
Chris@1511 1094 if (!model) return;
Chris@1511 1095
Chris@1511 1096 EventVector points =
Chris@1511 1097 model->getEventsStartingWithin(s.getStartFrame(), s.getDuration());
Chris@1511 1098
Chris@1511 1099 for (Event p: points) {
Chris@1511 1100 to.addPoint(p.withReferenceFrame(alignToReference(v, p.getFrame())));
Chris@1511 1101 }
Chris@1511 1102 }
Chris@1511 1103
Chris@1511 1104 bool
Chris@1533 1105 BoxLayer::paste(LayerGeometryProvider *v, const Clipboard &from,
Chris@1533 1106 sv_frame_t /* frameOffset */, bool /* interactive */)
Chris@1511 1107 {
Chris@1518 1108 auto model = ModelById::getAs<BoxModel>(m_model);
Chris@1511 1109 if (!model) return false;
Chris@1511 1110
Chris@1511 1111 const EventVector &points = from.getPoints();
Chris@1511 1112
Chris@1511 1113 bool realign = false;
Chris@1511 1114
Chris@1511 1115 if (clipboardHasDifferentAlignment(v, from)) {
Chris@1511 1116
Chris@1511 1117 QMessageBox::StandardButton button =
Chris@1511 1118 QMessageBox::question(v->getView(), tr("Re-align pasted items?"),
Chris@1511 1119 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 1120 QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel,
Chris@1511 1121 QMessageBox::Yes);
Chris@1511 1122
Chris@1511 1123 if (button == QMessageBox::Cancel) {
Chris@1511 1124 return false;
Chris@1511 1125 }
Chris@1511 1126
Chris@1511 1127 if (button == QMessageBox::Yes) {
Chris@1511 1128 realign = true;
Chris@1511 1129 }
Chris@1511 1130 }
Chris@1511 1131
Chris@1511 1132 ChangeEventsCommand *command =
Chris@1511 1133 new ChangeEventsCommand(m_model.untyped, tr("Paste"));
Chris@1511 1134
Chris@1511 1135 for (EventVector::const_iterator i = points.begin();
Chris@1511 1136 i != points.end(); ++i) {
Chris@1511 1137
Chris@1511 1138 sv_frame_t frame = 0;
Chris@1511 1139
Chris@1511 1140 if (!realign) {
Chris@1511 1141
Chris@1511 1142 frame = i->getFrame();
Chris@1511 1143
Chris@1511 1144 } else {
Chris@1511 1145
Chris@1511 1146 if (i->hasReferenceFrame()) {
Chris@1511 1147 frame = i->getReferenceFrame();
Chris@1511 1148 frame = alignFromReference(v, frame);
Chris@1511 1149 } else {
Chris@1511 1150 frame = i->getFrame();
Chris@1511 1151 }
Chris@1511 1152 }
Chris@1511 1153
Chris@1533 1154 Event p = i->withFrame(frame);
Chris@1533 1155
Chris@1511 1156 Event newPoint = p;
Chris@1511 1157 if (!p.hasValue()) {
Chris@1518 1158 newPoint = newPoint.withValue((model->getValueMinimum() +
Chris@1518 1159 model->getValueMaximum()) / 2);
Chris@1511 1160 }
Chris@1511 1161 if (!p.hasDuration()) {
Chris@1511 1162 sv_frame_t nextFrame = frame;
Chris@1511 1163 EventVector::const_iterator j = i;
Chris@1511 1164 for (; j != points.end(); ++j) {
Chris@1511 1165 if (j != i) break;
Chris@1511 1166 }
Chris@1511 1167 if (j != points.end()) {
Chris@1511 1168 nextFrame = j->getFrame();
Chris@1511 1169 }
Chris@1511 1170 if (nextFrame == frame) {
Chris@1511 1171 newPoint = newPoint.withDuration(model->getResolution());
Chris@1511 1172 } else {
Chris@1511 1173 newPoint = newPoint.withDuration(nextFrame - frame);
Chris@1511 1174 }
Chris@1511 1175 }
Chris@1511 1176
Chris@1511 1177 command->add(newPoint);
Chris@1511 1178 }
Chris@1511 1179
Chris@1511 1180 finish(command);
Chris@1511 1181 return true;
Chris@1511 1182 }
Chris@1511 1183
Chris@1511 1184 void
Chris@1518 1185 BoxLayer::toXml(QTextStream &stream,
Chris@1515 1186 QString indent, QString extraAttributes) const
Chris@1511 1187 {
Chris@1511 1188 QString s;
Chris@1511 1189
Chris@1515 1190 s += QString("verticalScale=\"%1\" ").arg(m_verticalScale);
Chris@1511 1191
Chris@1511 1192 SingleColourLayer::toXml(stream, indent, extraAttributes + " " + s);
Chris@1511 1193 }
Chris@1511 1194
Chris@1511 1195 void
Chris@1518 1196 BoxLayer::setProperties(const QXmlAttributes &attributes)
Chris@1511 1197 {
Chris@1511 1198 SingleColourLayer::setProperties(attributes);
Chris@1511 1199
Chris@1511 1200 bool ok;
Chris@1511 1201 VerticalScale scale = (VerticalScale)
Chris@1511 1202 attributes.value("verticalScale").toInt(&ok);
Chris@1511 1203 if (ok) setVerticalScale(scale);
Chris@1511 1204 }
Chris@1511 1205
Chris@1511 1206