annotate layer/BoxLayer.cpp @ 1551:e79731086b0f

Fixes to NoteLayer, particularly to calculation of vertical scale when model unit is not Hz. To avoid inconsistency we now behave as if the unit is always Hz from the point of view of the external API and display, converting at the point where we obtain values from the events themselves. Also various fixes to editing.
author Chris Cannam
date Thu, 21 Nov 2019 14:02:57 +0000
parents e6362cf5ff1d
children e95cefd4aa81
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@1511 543 int x0 = rect.left() - 40, x1 = rect.right();
Chris@1511 544
Chris@1511 545 sv_frame_t wholeFrame0 = v->getFrameForX(0);
Chris@1511 546 sv_frame_t wholeFrame1 = v->getFrameForX(v->getPaintWidth());
Chris@1511 547
Chris@1511 548 EventVector points(model->getEventsSpanning(wholeFrame0,
Chris@1511 549 wholeFrame1 - wholeFrame0));
Chris@1511 550 if (points.empty()) return;
Chris@1511 551
Chris@1511 552 paint.setPen(getBaseQColor());
Chris@1511 553
Chris@1518 554 // SVDEBUG << "BoxLayer::paint: resolution is "
Chris@1511 555 // << model->getResolution() << " frames" << endl;
Chris@1511 556
Chris@1518 557 double min = model->getValueMinimum();
Chris@1518 558 double max = model->getValueMaximum();
Chris@1511 559 if (max == min) max = min + 1.0;
Chris@1511 560
Chris@1511 561 QPoint localPos;
Chris@1511 562 Event illuminatePoint(0);
Chris@1511 563 bool shouldIlluminate = false;
Chris@1511 564
Chris@1511 565 if (v->shouldIlluminateLocalFeatures(this, localPos)) {
Chris@1547 566 shouldIlluminate = getLocalPoint(v, localPos.x(), localPos.y(),
Chris@1547 567 illuminatePoint);
Chris@1511 568 }
Chris@1511 569
Chris@1511 570 paint.save();
Chris@1511 571 paint.setRenderHint(QPainter::Antialiasing, false);
Chris@1511 572
Chris@1514 573 QFontMetrics fm = paint.fontMetrics();
Chris@1514 574
Chris@1511 575 for (EventVector::const_iterator i = points.begin();
Chris@1511 576 i != points.end(); ++i) {
Chris@1511 577
Chris@1511 578 const Event &p(*i);
Chris@1547 579 const auto r = getRange(p);
Chris@1511 580
Chris@1511 581 int x = v->getXForFrame(p.getFrame());
Chris@1511 582 int w = v->getXForFrame(p.getFrame() + p.getDuration()) - x;
Chris@1547 583 int y = getYForValue(v, r.first);
Chris@1547 584 int h = getYForValue(v, r.second) - y;
Chris@1511 585 int ex = x + w;
Chris@1514 586 int gap = v->scalePixelSize(2);
Chris@1511 587
Chris@1511 588 EventVector::const_iterator j = i;
Chris@1511 589 ++j;
Chris@1511 590
Chris@1511 591 if (j != points.end()) {
Chris@1511 592 const Event &q(*j);
Chris@1511 593 int nx = v->getXForFrame(q.getFrame());
Chris@1511 594 if (nx < ex) ex = nx;
Chris@1511 595 }
Chris@1511 596
Chris@1511 597 if (w < 1) w = 1;
Chris@1511 598
Chris@1511 599 paint.setPen(getBaseQColor());
Chris@1514 600 paint.setBrush(Qt::NoBrush);
Chris@1511 601
Chris@1514 602 if ((shouldIlluminate && illuminatePoint == p) ||
Chris@1514 603 (m_editing && m_editingPoint == p)) {
Chris@1511 604
Chris@1514 605 paint.setPen(QPen(getBaseQColor(), v->scalePixelSize(2)));
Chris@1511 606
Chris@1511 607 // Qt 5.13 deprecates QFontMetrics::width(), but its suggested
Chris@1511 608 // replacement (horizontalAdvance) was only added in Qt 5.11
Chris@1511 609 // which is too new for us
Chris@1511 610 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
Chris@1511 611
Chris@1514 612 if (abs(h) > 2 * fm.height()) {
Chris@1514 613
Chris@1514 614 QString y0label = QString("%1 %2")
Chris@1547 615 .arg(r.first)
Chris@1514 616 .arg(getScaleUnits());
Chris@1514 617
Chris@1514 618 QString y1label = QString("%1 %2")
Chris@1547 619 .arg(r.second)
Chris@1514 620 .arg(getScaleUnits());
Chris@1514 621
Chris@1514 622 PaintAssistant::drawVisibleText
Chris@1514 623 (v, paint,
Chris@1514 624 x - fm.width(y0label) - gap,
Chris@1514 625 y - fm.descent(),
Chris@1514 626 y0label, PaintAssistant::OutlinedText);
Chris@1514 627
Chris@1514 628 PaintAssistant::drawVisibleText
Chris@1514 629 (v, paint,
Chris@1514 630 x - fm.width(y1label) - gap,
Chris@1514 631 y + h + fm.ascent(),
Chris@1514 632 y1label, PaintAssistant::OutlinedText);
Chris@1514 633
Chris@1514 634 } else {
Chris@1514 635
Chris@1514 636 QString ylabel = QString("%1 %2 - %3 %4")
Chris@1547 637 .arg(r.first)
Chris@1514 638 .arg(getScaleUnits())
Chris@1547 639 .arg(r.second)
Chris@1514 640 .arg(getScaleUnits());
Chris@1514 641
Chris@1514 642 PaintAssistant::drawVisibleText
Chris@1514 643 (v, paint,
Chris@1514 644 x - fm.width(ylabel) - gap,
Chris@1514 645 y - fm.descent(),
Chris@1514 646 ylabel, PaintAssistant::OutlinedText);
Chris@1514 647 }
Chris@1514 648
Chris@1514 649 QString t0label = RealTime::frame2RealTime
Chris@1514 650 (p.getFrame(), model->getSampleRate()).toText(true).c_str();
Chris@1514 651
Chris@1514 652 QString t1label = RealTime::frame2RealTime
Chris@1514 653 (p.getFrame() + p.getDuration(), model->getSampleRate())
Chris@1514 654 .toText(true).c_str();
Chris@1514 655
Chris@1511 656 PaintAssistant::drawVisibleText
Chris@1514 657 (v, paint, x, y + fm.ascent() + gap,
Chris@1514 658 t0label, PaintAssistant::OutlinedText);
Chris@1514 659
Chris@1514 660 if (w > fm.width(t0label) + fm.width(t1label) + gap * 3) {
Chris@1514 661
Chris@1514 662 PaintAssistant::drawVisibleText
Chris@1514 663 (v, paint,
Chris@1514 664 x + w - fm.width(t1label),
Chris@1514 665 y + fm.ascent() + gap,
Chris@1514 666 t1label, PaintAssistant::OutlinedText);
Chris@1514 667
Chris@1514 668 } else {
Chris@1514 669
Chris@1514 670 PaintAssistant::drawVisibleText
Chris@1514 671 (v, paint,
Chris@1514 672 x + w - fm.width(t1label),
Chris@1514 673 y + fm.ascent() + fm.height() + gap,
Chris@1514 674 t1label, PaintAssistant::OutlinedText);
Chris@1514 675 }
Chris@1511 676 }
Chris@1511 677
Chris@1511 678 paint.drawRect(x, y, w, h);
Chris@1511 679 }
Chris@1511 680
Chris@1511 681 for (EventVector::const_iterator i = points.begin();
Chris@1511 682 i != points.end(); ++i) {
Chris@1511 683
Chris@1511 684 const Event &p(*i);
Chris@1511 685
Chris@1514 686 QString label = p.getLabel();
Chris@1514 687 if (label == "") continue;
Chris@1514 688
Chris@1514 689 if (shouldIlluminate && illuminatePoint == p) {
Chris@1514 690 continue; // already handled this in illumination special case
Chris@1514 691 }
Chris@1514 692
Chris@1511 693 int x = v->getXForFrame(p.getFrame());
Chris@1511 694 int w = v->getXForFrame(p.getFrame() + p.getDuration()) - x;
Chris@1511 695 int y = getYForValue(v, p.getValue());
Chris@1511 696
Chris@1514 697 int labelWidth = fm.width(label);
Chris@1511 698
Chris@1511 699 int gap = v->scalePixelSize(2);
Chris@1511 700
Chris@1511 701 if (x + w < x0 || x - labelWidth - gap > x1) {
Chris@1511 702 continue;
Chris@1511 703 }
Chris@1511 704
Chris@1514 705 int labelX, labelY;
Chris@1511 706
Chris@1514 707 labelX = x - labelWidth - gap;
Chris@1514 708 labelY = y - fm.descent();
Chris@1511 709
Chris@1514 710 PaintAssistant::drawVisibleText(v, paint, labelX, labelY, label,
Chris@1514 711 PaintAssistant::OutlinedText);
Chris@1511 712 }
Chris@1511 713
Chris@1511 714 paint.restore();
Chris@1511 715 }
Chris@1511 716
Chris@1511 717 int
Chris@1518 718 BoxLayer::getVerticalScaleWidth(LayerGeometryProvider *v,
Chris@1511 719 bool, QPainter &paint) const
Chris@1511 720 {
Chris@1518 721 auto model = ModelById::getAs<BoxModel>(m_model);
Chris@1511 722 if (!model || m_verticalScale == AutoAlignScale) {
Chris@1511 723 return 0;
Chris@1511 724 } else {
Chris@1511 725 if (m_verticalScale == LogScale) {
Chris@1511 726 return LogNumericalScale().getWidth(v, paint);
Chris@1511 727 } else {
Chris@1511 728 return LinearNumericalScale().getWidth(v, paint);
Chris@1511 729 }
Chris@1511 730 }
Chris@1511 731 }
Chris@1511 732
Chris@1511 733 void
Chris@1518 734 BoxLayer::paintVerticalScale(LayerGeometryProvider *v,
Chris@1511 735 bool, QPainter &paint, QRect) const
Chris@1511 736 {
Chris@1518 737 auto model = ModelById::getAs<BoxModel>(m_model);
Chris@1511 738 if (!model || model->isEmpty()) return;
Chris@1511 739
Chris@1511 740 QString unit;
Chris@1511 741 double min, max;
Chris@1511 742 bool logarithmic;
Chris@1511 743
Chris@1511 744 int w = getVerticalScaleWidth(v, false, paint);
Chris@1511 745
Chris@1511 746 getScaleExtents(v, min, max, logarithmic);
Chris@1511 747
Chris@1511 748 if (logarithmic) {
Chris@1511 749 LogNumericalScale().paintVertical(v, this, paint, 0, min, max);
Chris@1511 750 } else {
Chris@1511 751 LinearNumericalScale().paintVertical(v, this, paint, 0, min, max);
Chris@1511 752 }
Chris@1511 753
Chris@1511 754 if (getScaleUnits() != "") {
Chris@1511 755 int mw = w - 5;
Chris@1511 756 paint.drawText(5,
Chris@1511 757 5 + paint.fontMetrics().ascent(),
Chris@1511 758 TextAbbrev::abbreviate(getScaleUnits(),
Chris@1511 759 paint.fontMetrics(),
Chris@1511 760 mw));
Chris@1511 761 }
Chris@1511 762 }
Chris@1511 763
Chris@1511 764 void
Chris@1518 765 BoxLayer::drawStart(LayerGeometryProvider *v, QMouseEvent *e)
Chris@1511 766 {
Chris@1518 767 auto model = ModelById::getAs<BoxModel>(m_model);
Chris@1511 768 if (!model) return;
Chris@1511 769
Chris@1511 770 sv_frame_t frame = v->getFrameForX(e->x());
Chris@1511 771 if (frame < 0) frame = 0;
Chris@1511 772 frame = frame / model->getResolution() * model->getResolution();
Chris@1511 773
Chris@1518 774 double value = getValueForY(v, e->y());
Chris@1511 775
Chris@1518 776 m_editingPoint = Event(frame, float(value), 0, "");
Chris@1511 777 m_originalPoint = m_editingPoint;
Chris@1511 778
Chris@1511 779 if (m_editingCommand) finish(m_editingCommand);
Chris@1511 780 m_editingCommand = new ChangeEventsCommand(m_model.untyped,
Chris@1518 781 tr("Draw Box"));
Chris@1511 782 m_editingCommand->add(m_editingPoint);
Chris@1511 783
Chris@1511 784 m_editing = true;
Chris@1511 785 }
Chris@1511 786
Chris@1511 787 void
Chris@1518 788 BoxLayer::drawDrag(LayerGeometryProvider *v, QMouseEvent *e)
Chris@1511 789 {
Chris@1518 790 auto model = ModelById::getAs<BoxModel>(m_model);
Chris@1511 791 if (!model || !m_editing) return;
Chris@1511 792
Chris@1514 793 sv_frame_t dragFrame = v->getFrameForX(e->x());
Chris@1514 794 if (dragFrame < 0) dragFrame = 0;
Chris@1514 795 dragFrame = dragFrame / model->getResolution() * model->getResolution();
Chris@1511 796
Chris@1514 797 sv_frame_t eventFrame = m_originalPoint.getFrame();
Chris@1514 798 sv_frame_t eventDuration = dragFrame - eventFrame;
Chris@1514 799 if (eventDuration < 0) {
Chris@1514 800 eventFrame = eventFrame + eventDuration;
Chris@1514 801 eventDuration = -eventDuration;
Chris@1514 802 } else if (eventDuration == 0) {
Chris@1514 803 eventDuration = model->getResolution();
Chris@1514 804 }
Chris@1511 805
Chris@1518 806 double dragValue = getValueForY(v, e->y());
Chris@1514 807
Chris@1518 808 double eventValue = m_originalPoint.getValue();
Chris@1518 809 double eventFreqDiff = dragValue - eventValue;
Chris@1514 810 if (eventFreqDiff < 0) {
Chris@1518 811 eventValue = eventValue + eventFreqDiff;
Chris@1514 812 eventFreqDiff = -eventFreqDiff;
Chris@1511 813 }
Chris@1511 814
Chris@1511 815 m_editingCommand->remove(m_editingPoint);
Chris@1511 816 m_editingPoint = m_editingPoint
Chris@1514 817 .withFrame(eventFrame)
Chris@1514 818 .withDuration(eventDuration)
Chris@1518 819 .withValue(float(eventValue))
Chris@1514 820 .withLevel(float(eventFreqDiff));
Chris@1511 821 m_editingCommand->add(m_editingPoint);
Chris@1511 822 }
Chris@1511 823
Chris@1511 824 void
Chris@1518 825 BoxLayer::drawEnd(LayerGeometryProvider *, QMouseEvent *)
Chris@1511 826 {
Chris@1518 827 auto model = ModelById::getAs<BoxModel>(m_model);
Chris@1511 828 if (!model || !m_editing) return;
Chris@1511 829 finish(m_editingCommand);
Chris@1511 830 m_editingCommand = nullptr;
Chris@1511 831 m_editing = false;
Chris@1511 832 }
Chris@1511 833
Chris@1511 834 void
Chris@1518 835 BoxLayer::eraseStart(LayerGeometryProvider *v, QMouseEvent *e)
Chris@1511 836 {
Chris@1518 837 auto model = ModelById::getAs<BoxModel>(m_model);
Chris@1511 838 if (!model) return;
Chris@1511 839
Chris@1547 840 if (!getLocalPoint(v, e->x(), e->y(), m_editingPoint)) return;
Chris@1511 841
Chris@1511 842 if (m_editingCommand) {
Chris@1511 843 finish(m_editingCommand);
Chris@1511 844 m_editingCommand = nullptr;
Chris@1511 845 }
Chris@1511 846
Chris@1511 847 m_editing = true;
Chris@1511 848 }
Chris@1511 849
Chris@1511 850 void
Chris@1518 851 BoxLayer::eraseDrag(LayerGeometryProvider *, QMouseEvent *)
Chris@1511 852 {
Chris@1511 853 }
Chris@1511 854
Chris@1511 855 void
Chris@1518 856 BoxLayer::eraseEnd(LayerGeometryProvider *v, QMouseEvent *e)
Chris@1511 857 {
Chris@1518 858 auto model = ModelById::getAs<BoxModel>(m_model);
Chris@1511 859 if (!model || !m_editing) return;
Chris@1511 860
Chris@1511 861 m_editing = false;
Chris@1511 862
Chris@1511 863 Event p(0);
Chris@1547 864 if (!getLocalPoint(v, e->x(), e->y(), p)) return;
Chris@1511 865 if (p.getFrame() != m_editingPoint.getFrame() ||
Chris@1511 866 p.getValue() != m_editingPoint.getValue()) return;
Chris@1511 867
Chris@1511 868 m_editingCommand = new ChangeEventsCommand
Chris@1518 869 (m_model.untyped, tr("Erase Box"));
Chris@1511 870
Chris@1511 871 m_editingCommand->remove(m_editingPoint);
Chris@1511 872
Chris@1511 873 finish(m_editingCommand);
Chris@1511 874 m_editingCommand = nullptr;
Chris@1511 875 m_editing = false;
Chris@1511 876 }
Chris@1511 877
Chris@1511 878 void
Chris@1518 879 BoxLayer::editStart(LayerGeometryProvider *v, QMouseEvent *e)
Chris@1511 880 {
Chris@1518 881 auto model = ModelById::getAs<BoxModel>(m_model);
Chris@1511 882 if (!model) return;
Chris@1511 883
Chris@1547 884 if (!getLocalPoint(v, e->x(), e->y(), m_editingPoint)) {
Chris@1511 885 return;
Chris@1511 886 }
Chris@1511 887
Chris@1511 888 m_dragPointX = v->getXForFrame(m_editingPoint.getFrame());
Chris@1511 889 m_dragPointY = getYForValue(v, m_editingPoint.getValue());
Chris@1511 890
Chris@1511 891 m_originalPoint = m_editingPoint;
Chris@1511 892
Chris@1511 893 if (m_editingCommand) {
Chris@1511 894 finish(m_editingCommand);
Chris@1511 895 m_editingCommand = nullptr;
Chris@1511 896 }
Chris@1511 897
Chris@1511 898 m_editing = true;
Chris@1511 899 m_dragStartX = e->x();
Chris@1511 900 m_dragStartY = e->y();
Chris@1511 901 }
Chris@1511 902
Chris@1511 903 void
Chris@1518 904 BoxLayer::editDrag(LayerGeometryProvider *v, QMouseEvent *e)
Chris@1511 905 {
Chris@1518 906 auto model = ModelById::getAs<BoxModel>(m_model);
Chris@1511 907 if (!model || !m_editing) return;
Chris@1511 908
Chris@1511 909 int xdist = e->x() - m_dragStartX;
Chris@1511 910 int ydist = e->y() - m_dragStartY;
Chris@1511 911 int newx = m_dragPointX + xdist;
Chris@1511 912 int newy = m_dragPointY + ydist;
Chris@1511 913
Chris@1511 914 sv_frame_t frame = v->getFrameForX(newx);
Chris@1511 915 if (frame < 0) frame = 0;
Chris@1511 916 frame = frame / model->getResolution() * model->getResolution();
Chris@1511 917
Chris@1511 918 double value = getValueForY(v, newy);
Chris@1511 919
Chris@1511 920 if (!m_editingCommand) {
Chris@1511 921 m_editingCommand = new ChangeEventsCommand
Chris@1511 922 (m_model.untyped,
Chris@1518 923 tr("Drag Box"));
Chris@1511 924 }
Chris@1511 925
Chris@1511 926 m_editingCommand->remove(m_editingPoint);
Chris@1511 927 m_editingPoint = m_editingPoint
Chris@1511 928 .withFrame(frame)
Chris@1511 929 .withValue(float(value));
Chris@1511 930 m_editingCommand->add(m_editingPoint);
Chris@1511 931 }
Chris@1511 932
Chris@1511 933 void
Chris@1518 934 BoxLayer::editEnd(LayerGeometryProvider *, QMouseEvent *)
Chris@1511 935 {
Chris@1518 936 auto model = ModelById::getAs<BoxModel>(m_model);
Chris@1511 937 if (!model || !m_editing) return;
Chris@1511 938
Chris@1511 939 if (m_editingCommand) {
Chris@1511 940
Chris@1511 941 QString newName = m_editingCommand->getName();
Chris@1511 942
Chris@1511 943 if (m_editingPoint.getFrame() != m_originalPoint.getFrame()) {
Chris@1511 944 if (m_editingPoint.getValue() != m_originalPoint.getValue()) {
Chris@1518 945 newName = tr("Edit Box");
Chris@1511 946 } else {
Chris@1518 947 newName = tr("Relocate Box");
Chris@1511 948 }
Chris@1511 949 } else {
Chris@1511 950 newName = tr("Change Point Value");
Chris@1511 951 }
Chris@1511 952
Chris@1511 953 m_editingCommand->setName(newName);
Chris@1511 954 finish(m_editingCommand);
Chris@1511 955 }
Chris@1511 956
Chris@1511 957 m_editingCommand = nullptr;
Chris@1511 958 m_editing = false;
Chris@1511 959 }
Chris@1511 960
Chris@1511 961 bool
Chris@1518 962 BoxLayer::editOpen(LayerGeometryProvider *v, QMouseEvent *e)
Chris@1511 963 {
Chris@1518 964 auto model = ModelById::getAs<BoxModel>(m_model);
Chris@1511 965 if (!model) return false;
Chris@1511 966
Chris@1511 967 Event region(0);
Chris@1547 968 if (!getLocalPoint(v, e->x(), e->y(), region)) return false;
Chris@1511 969
Chris@1515 970 ItemEditDialog::LabelOptions labelOptions;
Chris@1518 971 labelOptions.valueLabel = tr("Minimum Value");
Chris@1518 972 labelOptions.levelLabel = tr("Value Extent");
Chris@1515 973 labelOptions.valueUnits = getScaleUnits();
Chris@1515 974 labelOptions.levelUnits = getScaleUnits();
Chris@1515 975
Chris@1511 976 ItemEditDialog *dialog = new ItemEditDialog
Chris@1511 977 (model->getSampleRate(),
Chris@1511 978 ItemEditDialog::ShowTime |
Chris@1511 979 ItemEditDialog::ShowDuration |
Chris@1511 980 ItemEditDialog::ShowValue |
Chris@1515 981 ItemEditDialog::ShowLevel |
Chris@1511 982 ItemEditDialog::ShowText,
Chris@1515 983 labelOptions);
Chris@1511 984
Chris@1511 985 dialog->setFrameTime(region.getFrame());
Chris@1511 986 dialog->setValue(region.getValue());
Chris@1515 987 dialog->setLevel(region.getLevel());
Chris@1511 988 dialog->setFrameDuration(region.getDuration());
Chris@1511 989 dialog->setText(region.getLabel());
Chris@1511 990
Chris@1511 991 if (dialog->exec() == QDialog::Accepted) {
Chris@1511 992
Chris@1518 993 Event newBox = region
Chris@1511 994 .withFrame(dialog->getFrameTime())
Chris@1511 995 .withValue(dialog->getValue())
Chris@1515 996 .withLevel(dialog->getLevel())
Chris@1511 997 .withDuration(dialog->getFrameDuration())
Chris@1511 998 .withLabel(dialog->getText());
Chris@1511 999
Chris@1511 1000 ChangeEventsCommand *command = new ChangeEventsCommand
Chris@1518 1001 (m_model.untyped, tr("Edit Box"));
Chris@1511 1002 command->remove(region);
Chris@1518 1003 command->add(newBox);
Chris@1511 1004 finish(command);
Chris@1511 1005 }
Chris@1511 1006
Chris@1511 1007 delete dialog;
Chris@1511 1008 return true;
Chris@1511 1009 }
Chris@1511 1010
Chris@1511 1011 void
Chris@1518 1012 BoxLayer::moveSelection(Selection s, sv_frame_t newStartFrame)
Chris@1511 1013 {
Chris@1518 1014 auto model = ModelById::getAs<BoxModel>(m_model);
Chris@1511 1015 if (!model) return;
Chris@1511 1016
Chris@1511 1017 ChangeEventsCommand *command =
Chris@1511 1018 new ChangeEventsCommand(m_model.untyped, tr("Drag Selection"));
Chris@1511 1019
Chris@1511 1020 EventVector points =
Chris@1511 1021 model->getEventsStartingWithin(s.getStartFrame(), s.getDuration());
Chris@1511 1022
Chris@1511 1023 for (EventVector::iterator i = points.begin();
Chris@1511 1024 i != points.end(); ++i) {
Chris@1511 1025
Chris@1511 1026 Event newPoint = (*i)
Chris@1511 1027 .withFrame(i->getFrame() + newStartFrame - s.getStartFrame());
Chris@1511 1028 command->remove(*i);
Chris@1511 1029 command->add(newPoint);
Chris@1511 1030 }
Chris@1511 1031
Chris@1511 1032 finish(command);
Chris@1511 1033 }
Chris@1511 1034
Chris@1511 1035 void
Chris@1518 1036 BoxLayer::resizeSelection(Selection s, Selection newSize)
Chris@1511 1037 {
Chris@1518 1038 auto model = ModelById::getAs<BoxModel>(m_model);
Chris@1511 1039 if (!model || !s.getDuration()) return;
Chris@1511 1040
Chris@1511 1041 ChangeEventsCommand *command =
Chris@1511 1042 new ChangeEventsCommand(m_model.untyped, tr("Resize Selection"));
Chris@1511 1043
Chris@1511 1044 EventVector points =
Chris@1511 1045 model->getEventsStartingWithin(s.getStartFrame(), s.getDuration());
Chris@1511 1046
Chris@1511 1047 double ratio = double(newSize.getDuration()) / double(s.getDuration());
Chris@1511 1048 double oldStart = double(s.getStartFrame());
Chris@1511 1049 double newStart = double(newSize.getStartFrame());
Chris@1511 1050
Chris@1511 1051 for (Event p: points) {
Chris@1511 1052
Chris@1511 1053 double newFrame = (double(p.getFrame()) - oldStart) * ratio + newStart;
Chris@1511 1054 double newDuration = double(p.getDuration()) * ratio;
Chris@1511 1055
Chris@1511 1056 Event newPoint = p
Chris@1511 1057 .withFrame(lrint(newFrame))
Chris@1511 1058 .withDuration(lrint(newDuration));
Chris@1511 1059 command->remove(p);
Chris@1511 1060 command->add(newPoint);
Chris@1511 1061 }
Chris@1511 1062
Chris@1511 1063 finish(command);
Chris@1511 1064 }
Chris@1511 1065
Chris@1511 1066 void
Chris@1518 1067 BoxLayer::deleteSelection(Selection s)
Chris@1511 1068 {
Chris@1518 1069 auto model = ModelById::getAs<BoxModel>(m_model);
Chris@1511 1070 if (!model) return;
Chris@1511 1071
Chris@1511 1072 ChangeEventsCommand *command =
Chris@1511 1073 new ChangeEventsCommand(m_model.untyped, tr("Delete Selected Points"));
Chris@1511 1074
Chris@1511 1075 EventVector points =
Chris@1511 1076 model->getEventsStartingWithin(s.getStartFrame(), s.getDuration());
Chris@1511 1077
Chris@1511 1078 for (EventVector::iterator i = points.begin();
Chris@1511 1079 i != points.end(); ++i) {
Chris@1511 1080
Chris@1511 1081 if (s.contains(i->getFrame())) {
Chris@1511 1082 command->remove(*i);
Chris@1511 1083 }
Chris@1511 1084 }
Chris@1511 1085
Chris@1511 1086 finish(command);
Chris@1511 1087 }
Chris@1511 1088
Chris@1511 1089 void
Chris@1518 1090 BoxLayer::copy(LayerGeometryProvider *v, Selection s, Clipboard &to)
Chris@1511 1091 {
Chris@1518 1092 auto model = ModelById::getAs<BoxModel>(m_model);
Chris@1511 1093 if (!model) return;
Chris@1511 1094
Chris@1511 1095 EventVector points =
Chris@1511 1096 model->getEventsStartingWithin(s.getStartFrame(), s.getDuration());
Chris@1511 1097
Chris@1511 1098 for (Event p: points) {
Chris@1511 1099 to.addPoint(p.withReferenceFrame(alignToReference(v, p.getFrame())));
Chris@1511 1100 }
Chris@1511 1101 }
Chris@1511 1102
Chris@1511 1103 bool
Chris@1533 1104 BoxLayer::paste(LayerGeometryProvider *v, const Clipboard &from,
Chris@1533 1105 sv_frame_t /* frameOffset */, bool /* interactive */)
Chris@1511 1106 {
Chris@1518 1107 auto model = ModelById::getAs<BoxModel>(m_model);
Chris@1511 1108 if (!model) return false;
Chris@1511 1109
Chris@1511 1110 const EventVector &points = from.getPoints();
Chris@1511 1111
Chris@1511 1112 bool realign = false;
Chris@1511 1113
Chris@1511 1114 if (clipboardHasDifferentAlignment(v, from)) {
Chris@1511 1115
Chris@1511 1116 QMessageBox::StandardButton button =
Chris@1511 1117 QMessageBox::question(v->getView(), tr("Re-align pasted items?"),
Chris@1511 1118 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 1119 QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel,
Chris@1511 1120 QMessageBox::Yes);
Chris@1511 1121
Chris@1511 1122 if (button == QMessageBox::Cancel) {
Chris@1511 1123 return false;
Chris@1511 1124 }
Chris@1511 1125
Chris@1511 1126 if (button == QMessageBox::Yes) {
Chris@1511 1127 realign = true;
Chris@1511 1128 }
Chris@1511 1129 }
Chris@1511 1130
Chris@1511 1131 ChangeEventsCommand *command =
Chris@1511 1132 new ChangeEventsCommand(m_model.untyped, tr("Paste"));
Chris@1511 1133
Chris@1511 1134 for (EventVector::const_iterator i = points.begin();
Chris@1511 1135 i != points.end(); ++i) {
Chris@1511 1136
Chris@1511 1137 sv_frame_t frame = 0;
Chris@1511 1138
Chris@1511 1139 if (!realign) {
Chris@1511 1140
Chris@1511 1141 frame = i->getFrame();
Chris@1511 1142
Chris@1511 1143 } else {
Chris@1511 1144
Chris@1511 1145 if (i->hasReferenceFrame()) {
Chris@1511 1146 frame = i->getReferenceFrame();
Chris@1511 1147 frame = alignFromReference(v, frame);
Chris@1511 1148 } else {
Chris@1511 1149 frame = i->getFrame();
Chris@1511 1150 }
Chris@1511 1151 }
Chris@1511 1152
Chris@1533 1153 Event p = i->withFrame(frame);
Chris@1533 1154
Chris@1511 1155 Event newPoint = p;
Chris@1511 1156 if (!p.hasValue()) {
Chris@1518 1157 newPoint = newPoint.withValue((model->getValueMinimum() +
Chris@1518 1158 model->getValueMaximum()) / 2);
Chris@1511 1159 }
Chris@1511 1160 if (!p.hasDuration()) {
Chris@1511 1161 sv_frame_t nextFrame = frame;
Chris@1511 1162 EventVector::const_iterator j = i;
Chris@1511 1163 for (; j != points.end(); ++j) {
Chris@1511 1164 if (j != i) break;
Chris@1511 1165 }
Chris@1511 1166 if (j != points.end()) {
Chris@1511 1167 nextFrame = j->getFrame();
Chris@1511 1168 }
Chris@1511 1169 if (nextFrame == frame) {
Chris@1511 1170 newPoint = newPoint.withDuration(model->getResolution());
Chris@1511 1171 } else {
Chris@1511 1172 newPoint = newPoint.withDuration(nextFrame - frame);
Chris@1511 1173 }
Chris@1511 1174 }
Chris@1511 1175
Chris@1511 1176 command->add(newPoint);
Chris@1511 1177 }
Chris@1511 1178
Chris@1511 1179 finish(command);
Chris@1511 1180 return true;
Chris@1511 1181 }
Chris@1511 1182
Chris@1511 1183 void
Chris@1518 1184 BoxLayer::toXml(QTextStream &stream,
Chris@1515 1185 QString indent, QString extraAttributes) const
Chris@1511 1186 {
Chris@1511 1187 QString s;
Chris@1511 1188
Chris@1515 1189 s += QString("verticalScale=\"%1\" ").arg(m_verticalScale);
Chris@1511 1190
Chris@1511 1191 SingleColourLayer::toXml(stream, indent, extraAttributes + " " + s);
Chris@1511 1192 }
Chris@1511 1193
Chris@1511 1194 void
Chris@1518 1195 BoxLayer::setProperties(const QXmlAttributes &attributes)
Chris@1511 1196 {
Chris@1511 1197 SingleColourLayer::setProperties(attributes);
Chris@1511 1198
Chris@1511 1199 bool ok;
Chris@1511 1200 VerticalScale scale = (VerticalScale)
Chris@1511 1201 attributes.value("verticalScale").toInt(&ok);
Chris@1511 1202 if (ok) setVerticalScale(scale);
Chris@1511 1203 }
Chris@1511 1204
Chris@1511 1205