• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

ossia / score / 30712175708

01 Aug 2026 06:17PM UTC coverage: 15.231% (-0.03%) from 15.263%
30712175708

Pull #2169

github

web-flow
Merge c60ea2870 into 3ceec338e
Pull Request #2169: 3rdparty: stop vendored xtensor/xtl/xsimd from polluting score's install

30395 of 199565 relevant lines covered (15.23%)

1075.35 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

32.7
/src/plugins/score-plugin-scenario/Scenario/Document/Interval/IntervalModel.cpp
1
// This is an open source non-commercial project. Dear PVS-Studio, please check
2
// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
3

4
#include "IntervalModel.hpp"
5

6
#include <Process/Process.hpp>
7
#include <Process/ProcessList.hpp>
8
#include <Process/Style/ScenarioStyle.hpp>
9
#include <Process/TimeValue.hpp>
10

11
#include <Curve/CurveModel.hpp>
12
#include <Curve/Segment/Linear/LinearSegment.hpp>
13

14
#include <Scenario/Document/BaseScenario/BaseScenario.hpp>
15
#include <Scenario/Document/Interval/IntervalDurations.hpp>
16
#include <Scenario/Document/Interval/Slot.hpp>
17
#include <Scenario/Document/ScenarioDocument/ScenarioDocumentModel.hpp>
18
#include <Scenario/Document/ScenarioDocument/ScenarioDocumentPresenter.hpp>
19
#include <Scenario/Document/Tempo/TempoProcess.hpp>
20
#include <Scenario/Settings/ScenarioSettingsModel.hpp>
21

22
#include <score/application/ApplicationContext.hpp>
23
#include <score/application/GUIApplicationContext.hpp>
24
#include <score/document/DocumentContext.hpp>
25
#include <score/document/DocumentInterface.hpp>
26
#include <score/model/EntitySerialization.hpp>
27
#include <score/model/ModelMetadata.hpp>
28
#include <score/tools/Bind.hpp>
29
#include <score/tools/IdentifierGeneration.hpp>
30

31
#include <core/document/Document.hpp>
32
#include <core/document/DocumentPresenter.hpp>
33

34
#include <ossia/detail/ssize.hpp>
35

36
#include <wobjectimpl.h>
37

38
#include <utility>
39
W_OBJECT_IMPL(Scenario::IntervalModel)
370✔
40

41
namespace Scenario
42
{
43
class StateModel;
44
class TimeSyncModel;
45
IntervalModel::IntervalModel(
36✔
46
    const Id<IntervalModel>& id, double yPos, const score::DocumentContext& ctx,
47
    QObject* parent)
48
    : Entity{id, Metadata<ObjectKey_k, IntervalModel>::get(), parent}
18✔
49
    , inlet{std::make_unique<Process::AudioInlet>(
36✔
50
          "Audio In", Id<Process::Port>(0), this)}
18✔
51
    , outlet{std::make_unique<Process::AudioOutlet>(
36✔
52
          "Audio Out", Id<Process::Port>(0), this)}
18✔
53
    , m_context{ctx}
54
    , m_executionState{}
55
    , m_viewMode{ViewMode::Temporal}
56
    , m_smallViewShown{}
57
    , m_muted{}
58
    , m_executing{}
59
    , m_hasSignature{}
60
    , m_graphal{}
61
{
36✔
62
  initConnections();
18✔
63
  metadata().setInstanceName(*this);
18✔
64
  const score::Brush& defaultBrush
18✔
65
      = Process::Style::instance().IntervalDefaultBackground();
18✔
66
  metadata().setColor(&defaultBrush);
18✔
67
  setHeightPercentage(yPos);
18✔
68

69
  outlet->setPropagate(true);
18✔
70
}
×
71

72
IntervalModel::~IntervalModel()
20✔
73
{
×
74
  static_assert(
75
      identified_entity<IntervalModel> && !abstract_base<IntervalModel>
76
          && !is_custom_serialized<IntervalModel>::value,
77
      "");
78
  processes.clear();
20✔
79
  identified_object_destroying(this);
20✔
80
}
20✔
81
void IntervalModel::initConnections()
20✔
82
{
83
  con(this->duration, &IntervalDurations::speedChanged, this,
40✔
84
      &IntervalModel::ancestorTempoChanged);
20✔
85
  processes.mutable_added.connect<&IntervalModel::on_addProcess>(this);
20✔
86
  processes.removing.connect<&IntervalModel::on_removingProcess>(this);
20✔
87
}
20✔
88

89
IntervalModel::IntervalModel(
6✔
90
    DataStream::Deserializer& vis, const score::DocumentContext& ctx, QObject* parent)
91
    : Entity{vis, parent}
2✔
92
    , m_context{ctx}
93
    , m_executionState{}
94
    , m_viewMode{ViewMode::Temporal}
95
    , m_smallViewShown{}
96
    , m_muted{}
97
    , m_executing{}
98
    , m_hasSignature{}
99
    , m_graphal{}
100
{
4✔
101
  initConnections();
2✔
102
  vis.writeTo(*this);
2✔
103
}
×
104

105
IntervalModel::IntervalModel(
×
106
    JSONObject::Deserializer& vis, const score::DocumentContext& ctx, QObject* parent)
107
    : Entity{vis, parent}
×
108
    , m_context{ctx}
109
    , m_executionState{}
110
    , m_viewMode{ViewMode::Temporal}
111
    , m_smallViewShown{}
112
    , m_muted{}
113
    , m_executing{}
114
    , m_hasSignature{}
115
    , m_graphal{}
116
{
×
117
  initConnections();
×
118
  vis.writeTo(*this);
×
119
}
×
120

121
IntervalModel::IntervalModel(
×
122
    DataStream::Deserializer&& vis, const score::DocumentContext& ctx, QObject* parent)
123
    : Entity{vis, parent}
×
124
    , m_context{ctx}
125
    , m_executionState{}
126
    , m_viewMode{ViewMode::Temporal}
127
    , m_smallViewShown{}
128
    , m_muted{}
129
    , m_executing{}
130
    , m_hasSignature{}
131
    , m_graphal{}
132
{
×
133
  initConnections();
×
134
  vis.writeTo(*this);
×
135
}
×
136

137
IntervalModel::IntervalModel(
×
138
    JSONObject::Deserializer&& vis, const score::DocumentContext& ctx, QObject* parent)
139
    : Entity{vis, parent}
×
140
    , m_context{ctx}
141
    , m_executionState{}
142
    , m_viewMode{ViewMode::Temporal}
143
    , m_smallViewShown{}
144
    , m_muted{}
145
    , m_executing{}
146
    , m_hasSignature{}
147
    , m_graphal{}
148
{
×
149
  initConnections();
×
150
  vis.writeTo(*this);
×
151
}
×
152

153
void IntervalModel::setHasTimeSignature(bool b)
18✔
154
{
155
  if(b != m_hasSignature)
18✔
156
  {
157
    m_hasSignature = b;
18✔
158
    if(b && m_signatures.empty())
18✔
159
    {
160
      m_signatures[TimeVal::zero()] = {4, 4};
×
161
    }
×
162
    hasTimeSignatureChanged(b);
18✔
163
  }
18✔
164
}
18✔
165

166
TimeVal IntervalModel::contentDuration() const noexcept
×
167
{
168
  TimeVal min_time
169
      = (duration.isMaxInfinite() ? duration.defaultDuration() : duration.maxDuration());
×
170

171
  for(Process::ProcessModel& proc : processes)
×
172
  {
173
    if(!(proc.flags() & Process::TimeIndependent))
×
174
    {
175
      if(auto d = proc.contentDuration(); d > min_time)
×
176
        min_time = d;
×
177
    }
×
178
  }
179

180
  return min_time * 1.1;
×
181
}
182

183
TempoProcess* IntervalModel::tempoCurve() const noexcept
1✔
184
{
185
  for(auto& proc : processes)
2✔
186
  {
187
    if(auto tempo = qobject_cast<TempoProcess*>(&proc))
1✔
188
    {
189
      return tempo;
×
190
    }
191
  }
192
  return nullptr;
1✔
193
}
1✔
194

195
void IntervalModel::ancestorStartDateChanged()
×
196
{
197
  for(auto& proc : processes)
×
198
    proc.ancestorStartDateChanged();
×
199
}
×
200

201
void IntervalModel::ancestorTempoChanged()
×
202
{
203
  if(!tempoCurve())
×
204
  {
205
    for(auto& proc : processes)
×
206
      proc.ancestorTempoChanged();
×
207
  }
×
208
}
×
209

210
void IntervalModel::addSignature(TimeVal t, ossia::time_signature sig)
18✔
211
{
212
  m_signatures[t] = sig;
18✔
213
  timeSignaturesChanged(m_signatures);
18✔
214
}
18✔
215

216
void IntervalModel::removeSignature(TimeVal t)
×
217
{
218
  m_signatures.erase(t);
×
219
  timeSignaturesChanged(m_signatures);
×
220
}
×
221
const TimeSignatureMap& IntervalModel::timeSignatureMap() const noexcept
67✔
222
{
223
  return m_signatures;
67✔
224
}
225

226
void IntervalModel::setTimeSignatureMap(const TimeSignatureMap& map)
×
227
{
228
  // qDebug() << "New map";
229
  // for(auto& [a,b]: map)
230
  //   qDebug() << a.msec() << b.upper << "/" << b.lower;
231
  if(map != m_signatures)
×
232
  {
233
    m_signatures = map;
×
234
    timeSignaturesChanged(map);
×
235
  }
×
236
}
×
237

238
ossia::musical_sync IntervalModel::quantizationRate() const noexcept
×
239
{
240
  return m_quantRate;
×
241
}
242

243
void IntervalModel::setQuantizationRate(ossia::musical_sync b)
×
244
{
245
  if(b != m_quantRate)
×
246
  {
247
    m_quantRate = b;
×
248
    quantizationRateChanged(b);
×
249
  }
×
250
}
×
251

252
const Id<StateModel>& IntervalModel::startState() const noexcept
36✔
253
{
254
  return m_startState;
36✔
255
}
256

257
void IntervalModel::setStartState(const Id<StateModel>& e)
18✔
258
{
259
  m_startState = e;
18✔
260
}
18✔
261

262
const Id<StateModel>& IntervalModel::endState() const noexcept
36✔
263
{
264
  return m_endState;
36✔
265
}
266

267
void IntervalModel::setEndState(const Id<StateModel>& endState)
18✔
268
{
269
  m_endState = endState;
18✔
270
}
18✔
271

272
const TimeVal& IntervalModel::date() const noexcept
1✔
273
{
274
  return m_date;
1✔
275
}
276

277
void IntervalModel::setStartDate(const TimeVal& start)
×
278
{
279
  m_date = start;
×
280

281
  for(auto& proc : processes)
×
282
    proc.ancestorStartDateChanged();
×
283

284
  dateChanged(start);
×
285
}
×
286

287
void IntervalModel::translate(const TimeVal& deltaTime)
×
288
{
289
  setStartDate(m_date + deltaTime);
×
290
}
×
291

292
// Simple getters and setters
293

294
double IntervalModel::heightPercentage() const noexcept
×
295
{
296
  return m_heightPercentage;
×
297
}
298

299
// Should go in an "execution" object.
300
void IntervalModel::startExecution()
×
301
{
302
  for(Process::ProcessModel& proc : processes)
×
303
  {
304
    proc.setExecuting(true);
×
305
    proc.startExecution(); // prevents editing
×
306
  }
307
}
×
308
void IntervalModel::stopExecution()
×
309
{
310
  duration.setPlayPercentage(0);
×
311
  if(!m_hasSignature)
×
312
    duration.setSpeed(1.0);
×
313

314
  for(Process::ProcessModel& proc : processes)
×
315
  {
316
    proc.setExecuting(false);
×
317
    proc.stopExecution();
×
318
  }
319
  setExecuting(false);
×
320
}
×
321

322
void IntervalModel::reset()
×
323
{
324
  duration.setPlayPercentage(0);
×
325

326
  if(!m_hasSignature)
×
327
    duration.setSpeed(1.0);
×
328

329
  for(Process::ProcessModel& proc : processes)
×
330
  {
331
    proc.setExecuting(false);
×
332
    proc.resetExecution();
×
333
    proc.stopExecution();
×
334
  }
335

336
  setExecutionState(IntervalExecutionState::Enabled);
×
337
  setExecuting(false);
×
338
}
×
339

340
void IntervalModel::setHeightPercentage(double arg)
18✔
341
{
342
  if(m_heightPercentage != arg)
18✔
343
  {
344
    m_heightPercentage = arg;
18✔
345
    heightPercentageChanged(arg);
18✔
346
  }
18✔
347
}
18✔
348

349
void IntervalModel::setExecutionState(IntervalExecutionState s)
×
350
{
351
  if(s != m_executionState)
×
352
  {
353
    m_executionState = s;
×
354
    executionStateChanged(executionState());
×
355
  }
×
356
}
×
357

358
IntervalExecutionState IntervalModel::executionState() const
×
359
{
360
  switch(m_executionState)
×
361
  {
362
    case IntervalExecutionState::Enabled:
363
      return m_muted ? IntervalExecutionState::Muted : IntervalExecutionState::Enabled;
×
364
    default:
365
      return m_executionState;
×
366
  }
367
}
×
368

369
IntervalModel::ViewMode IntervalModel::viewMode() const noexcept
12✔
370
{
371
  return m_viewMode;
12✔
372
}
373

374
void IntervalModel::setViewMode(IntervalModel::ViewMode v)
6✔
375
{
376
  if(v != m_viewMode)
6✔
377
  {
378
    m_viewMode = v;
×
379
    viewModeChanged(v);
×
380
  }
×
381
}
6✔
382

383
QPointF IntervalModel::nodalOffset() const noexcept
2✔
384
{
385
  return m_nodalOffset;
2✔
386
}
387

388
void IntervalModel::setNodalOffset(QPointF offs)
×
389
{
390
  if(m_nodalOffset != offs)
×
391
  {
392
    m_nodalOffset = offs;
×
393
    nodalOffsetChanged(m_nodalOffset);
×
394
  }
×
395
}
×
396

397
double IntervalModel::nodalScale() const noexcept
2✔
398
{
399
  return m_nodalScale;
2✔
400
}
401

402
void IntervalModel::setNodalScale(double zoom)
×
403
{
404
  if(m_nodalScale != zoom)
×
405
  {
406
    m_nodalScale = zoom;
×
407
    nodalScaleChanged(m_nodalScale);
×
408
  }
×
409
}
×
410

411
ZoomRatio IntervalModel::zoom() const noexcept
24✔
412
{
413
  return m_zoom;
24✔
414
}
415

416
void IntervalModel::setZoom(const ZoomRatio& zoom)
11✔
417
{
418
  m_zoom = zoom;
11✔
419
}
11✔
420

421
TimeVal IntervalModel::midTime() const noexcept
12✔
422
{
423
  return m_center;
12✔
424
}
425

426
void IntervalModel::setMidTime(const TimeVal& value)
9✔
427
{
428
  m_center = value;
9✔
429
}
9✔
430

431
void IntervalModel::setSmallViewVisible(bool v)
2✔
432
{
433
  if(v != m_smallViewShown)
2✔
434
  {
435
    m_smallViewShown = v;
×
436
    smallViewVisibleChanged(v);
×
437
  }
×
438
}
2✔
439

440
bool IntervalModel::smallViewVisible() const noexcept
×
441
{
442
  return m_smallViewShown;
×
443
}
444

445
void IntervalModel::clearSmallView()
×
446
{
447
  m_smallView.clear();
×
448
  rackChanged(Slot::SmallView);
×
449
}
×
450

451
void IntervalModel::clearFullView()
×
452
{
453
  m_fullView.clear();
×
454
  rackChanged(Slot::FullView);
×
455
}
×
456

457
void IntervalModel::replaceSmallView(const Rack& other)
×
458
{
459
  m_smallView = other;
×
460
  rackChanged(Slot::SmallView);
×
461
}
×
462

463
void IntervalModel::replaceFullView(const FullRack& other)
×
464
{
465
  m_fullView = other;
×
466
  rackChanged(Slot::FullView);
×
467
}
×
468

469
void IntervalModel::addLayer(int slot, Id<Process::ProcessModel> id)
×
470
{
471
  auto& procs = m_smallView.at(slot).processes;
×
472
  SCORE_ASSERT(ossia::find(procs, id) == procs.end());
×
473
  procs.push_back(id);
×
474

475
  layerAdded({slot, Slot::SmallView}, id);
×
476

477
  putLayerToFront(slot, id);
×
478
}
×
479

480
void IntervalModel::removeLayer(int slot, Id<Process::ProcessModel> id)
×
481
{
482
  auto& procs = m_smallView.at(slot).processes;
×
483
  const auto N = procs.size();
×
484
  ossia::remove_erase(procs, id);
×
485

486
  if(procs.size() < N)
×
487
  {
488
    layerRemoved({slot, Slot::SmallView}, id);
×
489

490
    if(!procs.empty())
×
491
      putLayerToFront(slot, procs.front());
×
492
    else
493
      putLayerToFront(slot, std::nullopt);
×
494
  }
×
495
}
×
496

497
void IntervalModel::putLayerToFront(int slot, Id<Process::ProcessModel> id)
×
498
{
499
  m_smallView.at(slot).frontProcess = id;
×
500
  frontLayerChanged(slot, id);
×
501
}
×
502

503
void IntervalModel::putLayerToFront(int slot, std::nullopt_t)
×
504
{
505
  m_smallView.at(slot).frontProcess = std::nullopt;
×
506
  frontLayerChanged(slot, std::nullopt);
×
507
}
×
508

509
void IntervalModel::addSlot(Slot s, int pos)
×
510
{
511
  SCORE_ASSERT(std::ssize(m_smallView) >= pos);
×
512
  m_smallView.insert(m_smallView.begin() + pos, std::move(s));
×
513
  slotAdded({pos, Slot::SmallView});
×
514

515
  if(m_smallView.size() == 1)
×
516
    setSmallViewVisible(true);
×
517
}
×
518

519
void IntervalModel::addSlot(Slot s)
×
520
{
521
  addSlot(std::move(s), m_smallView.size());
×
522
}
×
523

524
void IntervalModel::removeSlot(int pos)
2✔
525
{
526
  if(std::ssize(m_smallView) > pos)
2✔
527
  {
528
    m_smallView.erase(m_smallView.begin() + pos);
2✔
529
    slotRemoved({pos, Slot::SmallView});
2✔
530

531
    if(m_smallView.empty())
2✔
532
      setSmallViewVisible(false);
2✔
533
  }
2✔
534
}
2✔
535

536
const Slot* IntervalModel::findSmallViewSlot(int slot) const
×
537
{
538
  if(slot < std::ssize(m_smallView))
×
539
    return &m_smallView[slot];
×
540

541
  return nullptr;
×
542
}
×
543

544
const Slot& IntervalModel::getSmallViewSlot(int slot) const
×
545
{
546
  return m_smallView.at(slot);
×
547
}
548

549
Slot& IntervalModel::getSmallViewSlot(int slot)
×
550
{
551
  return m_smallView.at(slot);
×
552
}
553

554
const FullSlot* IntervalModel::findFullViewSlot(int slot) const
×
555
{
556
  if(slot < std::ssize(m_fullView))
×
557
    return &m_fullView[slot];
×
558

559
  return nullptr;
×
560
}
×
561

562
const FullSlot& IntervalModel::getFullViewSlot(int slot) const
×
563
{
564
  return m_fullView.at(slot);
×
565
}
566

567
FullSlot& IntervalModel::getFullViewSlot(int slot)
×
568
{
569
  return m_fullView.at(slot);
×
570
}
571

572
void IntervalModel::setMuted(bool m)
×
573
{
574
  if(m != m_muted)
×
575
  {
576
    m_muted = m;
×
577
    mutedChanged(m);
×
578
    executionStateChanged(executionState());
×
579
  }
×
580
}
×
581

582
void IntervalModel::setGraphal(bool m)
×
583
{
584
  if(m != m_graphal)
×
585
  {
586
    SCORE_ASSERT(!m_graphal); // once an interval is set graphal it cannot go back
×
587
    // TODO this should be a ctor thing instead...
588
    m_graphal = m;
×
589
    inlet.reset();
×
590
    outlet.reset();
×
591
    graphalChanged(m);
×
592
  }
×
593
}
×
594

595
void IntervalModel::setExecuting(bool m)
×
596
{
597
  if(m != m_executing)
×
598
  {
599
    m_executing = m;
×
600
    executingChanged(m);
×
601
    if(m_executing)
×
602
      startExecution();
×
603
    else
604
      stopExecution();
×
605
  }
×
606
}
×
607

608
void IntervalModel::setStartMarker(TimeVal t)
×
609
{
610
  if(t != m_startMarker)
×
611
  {
612
    m_startMarker = t;
×
613
    startMarkerChanged(t);
×
614
  }
×
615
}
×
616

617
TimeVal IntervalModel::startMarker() const noexcept
42✔
618
{
619
  return m_startMarker;
42✔
620
}
621

622
double IntervalModel::getSlotHeight(const SlotId& slot) const
6✔
623
{
624
  if(slot.fullView())
6✔
625
  {
626
    auto& slt = m_fullView.at(slot.index);
6✔
627
    if(slt.nodal)
6✔
628
    {
629
      return m_nodalFullViewSlotHeight;
6✔
630
    }
631
    else
632
    {
633
      auto it = processes.find(slt.process);
×
634
      if(it != processes.end())
×
635
        return it->getSlotHeight();
×
636
      else
637
        return 0.;
×
638
    }
639
  }
640
  else
641
  {
642
    return m_smallView.at(slot.index).height;
×
643
  }
644
}
6✔
645

646
double IntervalModel::getSlotHeightForProcess(const Id<Process::ProcessModel>& p) const
×
647
{
648
  for(auto& slt : m_smallView)
×
649
  {
650
    for(auto& proc : slt.processes)
×
651
    {
652
      if(proc == p)
×
653
        return slt.height;
×
654
    }
655
  }
656

657
  return 0.;
×
658
}
×
659

660
void IntervalModel::setSlotHeight(const SlotId& slot, double height)
×
661
{
662
  height = std::max(height, 20.);
×
663
  if(slot.fullView())
×
664
  {
665
    auto& slt = m_fullView.at(slot.index);
×
666
    if(slt.nodal)
×
667
    {
668
      m_nodalFullViewSlotHeight = height;
×
669
    }
×
670
    else
671
    {
672
      auto it = processes.find(slt.process);
×
673
      if(it != processes.end())
×
674
        it->setSlotHeight(height);
×
675
    }
676
  }
×
677
  else
678
  {
679
    getSmallViewSlot(slot.index).height = height;
×
680
  }
681
  slotResized(slot);
×
682
}
×
683

684
double IntervalModel::getHeight() const noexcept
×
685
{
686
  double h = 0.;
×
687
  const double slotSize = (this->smallViewVisible() ? 1. : 0.);
×
688
  for(const auto& slot : m_smallView)
×
689
  {
690
    h += slot.height * slotSize + SlotHeader::headerHeight()
×
691
         + SlotFooter::footerHeight();
×
692
  }
693
  return h;
×
694
}
695

696
void swap(Scenario::Slot& lhs, Scenario::Slot& rhs)
×
697
{
698
  Scenario::Slot tmp = std::move(lhs);
×
699
  lhs = std::move(rhs);
×
700
  rhs = std::move(tmp);
×
701
}
×
702

703
void IntervalModel::swapSlots(int pos1, int pos2, Slot::RackView v)
×
704
{
705
  SCORE_ASSERT(pos1 >= 0);
×
706
  SCORE_ASSERT(pos2 >= 0);
×
707
  if(v == Slot::FullView)
×
708
  {
709
    auto& v = m_fullView;
×
710
    int N = std::ssize(v);
×
711
    if(pos1 < N && pos2 < N)
×
712
    {
713
      if(pos1 < pos2)
×
714
      {
715
        auto val = *(v.begin() + pos1);
×
716

717
        v.insert(v.begin() + pos2, val);
×
718
        v.erase(v.begin() + pos1);
×
719
      }
×
720
      else if(pos1 > pos2)
×
721
      {
722
        auto val = *(v.begin() + pos1);
×
723

724
        v.insert(v.begin() + pos2, val);
×
725
        v.erase(v.begin() + pos1 + 1);
×
726
      }
×
727
      // std::swap(*(v.begin() + pos1), *(v.begin() + pos2));
728
    }
×
729
    else if(pos1 < N && pos2 >= N)
×
730
    {
731
      auto it = v.begin() + pos1;
×
732
      std::rotate(it, it + 1, v.end());
×
733
    }
×
734
    else if(pos2 < N && pos1 >= N)
×
735
    {
736
      auto it = v.begin() + pos2;
×
737
      std::rotate(it, it + 1, v.end());
×
738
    }
×
739
  }
×
740
  else
741
  {
742
    auto& v = m_smallView;
×
743
    int N = std::ssize(v);
×
744
    if(pos1 < N && pos2 < N)
×
745
    {
746
      if(pos1 < pos2)
×
747
      {
748
        auto val = *(v.begin() + pos1);
×
749

750
        v.insert(v.begin() + pos2, val);
×
751
        v.erase(v.begin() + pos1);
×
752
      }
×
753
      else if(pos1 > pos2)
×
754
      {
755
        auto val = *(v.begin() + pos1);
×
756

757
        v.insert(v.begin() + pos2, val);
×
758
        v.erase(v.begin() + pos1 + 1);
×
759
      }
×
760
      // std::swap(*(v.begin() + pos1), *(v.begin() + pos2));
761
    }
×
762
    else if(pos1 < N && pos2 >= N)
×
763
    {
764
      auto it = v.begin() + pos1;
×
765
      std::rotate(it, it + 1, v.end());
×
766
    }
×
767
    else if(pos2 < N && pos1 >= N)
×
768
    {
769
      auto it = v.begin() + pos2;
×
770
      std::rotate(it, it + 1, v.end());
×
771
    }
×
772
  }
773
  slotsSwapped(pos1, pos2, v);
×
774
}
×
775

776
void IntervalModel::on_addProcess(Process::ProcessModel& p)
26✔
777
{
778
  if(!(p.flags() & Process::ProcessFlags::TimeIndependent))
26✔
779
  {
780
    m_fullView.push_back(FullSlot{p.id()});
24✔
781
    slotAdded({m_fullView.size() - 1, Slot::FullView});
782
  }
783
  else
784
  {
785
    const auto smallNodalSlot
786
        = ossia::find_if(m_smallView, [](const auto& slt) { return slt.nodal; });
2✔
787
    if(smallNodalSlot == m_smallView.end())
2✔
788
    {
789
      Slot slt;
2✔
790
      slt.nodal = true;
2✔
791
      slt.height = context().app.settings<Scenario::Settings::Model>().getSlotHeight();
2✔
792
      slt.processes.push_back(p.id());
2✔
793
      m_smallView.push_back(std::move(slt));
2✔
794
      slotAdded({m_smallView.size() - 1, Slot::SmallView});
2✔
795
    }
2✔
796
    else
797
    {
798
      smallNodalSlot->processes.push_back(p.id());
×
799
    }
800

801
    const bool fullNodalSlot
2✔
802
        = ossia::any_of(m_fullView, [](const auto& slt) { return slt.nodal; });
4✔
803
    if(!fullNodalSlot)
2✔
804
    {
805
      FullSlot slt;
2✔
806
      slt.nodal = true;
2✔
807
      m_fullView.push_back(std::move(slt));
2✔
808
      slotAdded({m_fullView.size() - 1, Slot::FullView});
2✔
809
    }
2✔
810
    else
811
    {
812
      // todo ! ?
813
    }
814
  }
815

816
  con(metadata(), &score::ModelMetadata::ColorChanged, &p.metadata(),
52✔
817
      &score::ModelMetadata::setColor);
26✔
818
  p.metadata().setColor(metadata().getColor());
26✔
819
}
26✔
820

821
void IntervalModel::on_removingProcess(const Process::ProcessModel& p)
26✔
822
{
823
  if(!(p.flags() & Process::ProcessFlags::TimeIndependent))
26✔
824
  {
825
    const auto& pid = p.id();
24✔
826
    for(int i = 0; i < std::ssize(m_smallView); i++)
24✔
827
    {
828
      removeLayer(i, pid);
×
829
    }
×
830

831
    auto it = ossia::find_if(
24✔
832
        m_fullView, [&](const FullSlot& slot) { return slot.process == pid; });
51✔
833
    if(it != m_fullView.end())
24✔
834
    {
835
      int N = std::distance(m_fullView.begin(), it);
24✔
836
      m_fullView.erase(it);
24✔
837
      slotRemoved(SlotId{N, Slot::FullView});
24✔
838
    }
24✔
839
  }
24✔
840
  else
841
  {
842
    {
843
      const auto smallNodalSlot
844
          = ossia::find_if(m_smallView, [](const auto& slt) { return slt.nodal; });
4✔
845

846
      if(smallNodalSlot != m_smallView.end())
2✔
847
      {
848
        SCORE_ASSERT(m_smallView.size() > 0);
2✔
849
        SCORE_ASSERT(!m_smallView.empty());
2✔
850
        if(smallNodalSlot->processes.size() > 1)
2✔
851
        {
852
          ossia::remove_erase(smallNodalSlot->processes, p.id());
×
853
        }
×
854
        else
855
        {
856
          int N = std::distance(m_smallView.begin(), smallNodalSlot);
2✔
857
          removeSlot(N);
2✔
858
        }
859
      }
2✔
860
    }
861
    {
862
      const auto fullNodalSlot
863
          = ossia::find_if(m_fullView, [](const auto& slt) { return slt.nodal; });
6✔
864

865
      if(fullNodalSlot != m_fullView.end())
2✔
866
      {
867
        SCORE_ASSERT(m_fullView.size() > 0);
2✔
868
        int numTimeIndependent = ossia::count_if(processes, [](const auto& proc) {
6✔
869
          return proc.flags() & Process::ProcessFlags::TimeIndependent;
4✔
870
        });
871
        if(numTimeIndependent <= 1)
2✔
872
        {
873
          int N = std::distance(m_fullView.begin(), fullNodalSlot);
2✔
874
          m_fullView.erase(fullNodalSlot);
2✔
875
          slotRemoved(SlotId{N, Slot::FullView});
2✔
876
        }
2✔
877
        /* TODO
878
        if(fullNodalSlot->processes.size() > 1)
879
        {
880
          ossia::remove_erase(fullNodalSlot->processes, p.id());
881
        }
882
        else
883
        {
884
          int N = std::distance(m_fullView.begin(), fullNodalSlot);
885
          m_fullView.erase(fullNodalSlot);
886
          slotRemoved(SlotId{N, Slot::fullView});
887
        }
888
        */
889
      }
2✔
890
    }
891
  }
892
}
26✔
893

894
bool isInFullView(const IntervalModel& cstr) noexcept
×
895
{
896
  if(qobject_cast<BaseScenario*>(cstr.parent()))
×
897
    return true;
×
898

899
  auto& doc = score::IDocument::documentContext(cstr);
×
900
  if(auto pres = doc.document.presenter())
×
901
  {
902
    auto sub
×
903
        = qobject_cast<Scenario::ScenarioDocumentPresenter*>(pres->presenterDelegate());
×
904
    if(sub)
×
905
      return &sub->displayedElements.interval() == &cstr;
×
906
    return false;
×
907
  }
908
  return false;
×
909
}
×
910

911
bool isInFullView(const Process::ProcessModel& cstr) noexcept
×
912
{
913
  return isInFullView(*static_cast<IntervalModel*>(cstr.parent()));
×
914
}
915

916
const Scenario::Slot& SlotPath::find(const score::DocumentContext& ctx) const
×
917
{
918
  return interval.find(ctx).getSmallViewSlot(index);
×
919
}
920

921
const Scenario::Slot* SlotPath::try_find(const score::DocumentContext& ctx) const
×
922
{
923
  if(auto cst = interval.try_find(ctx))
×
924
    return cst->findSmallViewSlot(index);
×
925
  else
926
    return nullptr;
×
927
}
×
928

929
bool isBus(const IntervalModel& model, const score::DocumentContext& ctx) noexcept
×
930
{
931
  auto& buses = score::IDocument::get<Scenario::ScenarioDocumentModel>(ctx.document)
×
932
                    .busIntervals;
×
933
  return ossia::contains(buses, &model);
×
934
}
935

936
ParentTimeInfo closestParentWithMusicalMetrics(const IntervalModel* self)
25✔
937
{
938
  TimeVal delta = TimeVal::zero();
25✔
939
  if(self->hasTimeSignature())
25✔
940
    return {self, self, delta};
25✔
941

942
  delta += self->date();
×
943
  auto p = self->parent();
×
944
  if(p)
×
945
  {
946
    p = p->parent();
×
947
  }
×
948

949
  const IntervalModel* lastFound = self;
×
950
  while(p)
×
951
  {
952
    if(auto pi = qobject_cast<const IntervalModel*>(p))
×
953
    {
954
      lastFound = pi;
×
955
      if(pi->hasTimeSignature())
×
956
      {
957
        return {pi, pi, delta};
×
958
      }
959
      else
960
      {
961
        delta += pi->date();
×
962
        if((p = p->parent()))
×
963
          p = p->parent();
×
964
      }
965
    }
×
966
    else
967
    {
968
      if((p = p->parent()))
×
969
        p = p->parent();
×
970
    }
971
  }
972
  return {nullptr, lastFound, {}};
×
973
}
25✔
974

975
ParentTimeInfo closestParentWithQuantification(const IntervalModel* self)
×
976
{
977
  TimeVal delta = TimeVal::zero();
×
978
  if(self->quantizationRate() >= 0.)
×
979
    return {self, self, delta};
×
980

981
  delta += self->date();
×
982
  auto p = self->parent();
×
983
  if(p)
×
984
  {
985
    p = p->parent();
×
986
  }
×
987

988
  const IntervalModel* lastFound = self;
×
989
  while(p)
×
990
  {
991
    if(auto pi = qobject_cast<const IntervalModel*>(p))
×
992
    {
993
      lastFound = pi;
×
994
      if(pi->quantizationRate() >= 0.)
×
995
      {
996
        return {pi, pi, delta};
×
997
      }
998
      else
999
      {
1000
        delta += pi->date();
×
1001
        if((p = p->parent()))
×
1002
          p = p->parent();
×
1003
      }
1004
    }
×
1005
    else
1006
    {
1007
      if((p = p->parent()))
×
1008
        p = p->parent();
×
1009
    }
1010
  }
1011
  return {nullptr, lastFound, {}};
×
1012
}
×
1013

1014
ParentTimeInfo closestParentWithTempo(const IntervalModel* self)
1✔
1015
{
1016
  TimeVal delta = TimeVal::zero();
1✔
1017
  if(self->tempoCurve())
1✔
1018
    return {self, self, delta};
×
1019

1020
  delta += self->date();
1✔
1021
  auto p = self->parent();
1✔
1022
  if(p)
1✔
1023
  {
1024
    p = p->parent();
1✔
1025
  }
1✔
1026

1027
  const IntervalModel* lastFound = self;
1✔
1028
  ;
1029
  while(p)
4✔
1030
  {
1031
    if(auto pi = qobject_cast<const IntervalModel*>(p))
3✔
1032
    {
1033
      lastFound = pi;
×
1034
      if(pi->tempoCurve())
×
1035
      {
1036
        return {pi, pi, delta};
×
1037
      }
1038
      else
1039
      {
1040
        delta += pi->date();
×
1041
        if((p = p->parent()))
×
1042
          p = p->parent();
×
1043
      }
1044
    }
×
1045
    else
1046
    {
1047
      if((p = p->parent()))
3✔
1048
        p = p->parent();
3✔
1049
    }
1050
  }
1051

1052
  return {nullptr, lastFound, {}};
1✔
1053
}
1✔
1054

1055
QPointF newProcessPosition(const IntervalModel& cst) noexcept
22✔
1056
{
1057
  static ossia::flat_set<double> autoPos;
22✔
1058
  autoPos.clear();
22✔
1059
  autoPos.reserve(100);
22✔
1060
  for(const Process::ProcessModel& proc : cst.processes)
26✔
1061
  {
1062
    const auto p = proc.position();
4✔
1063
    if(p.x() - p.y() < 5)
4✔
1064
    {
1065
      autoPos.insert((p.x() + p.y()) / 2);
4✔
1066
    }
4✔
1067
  }
1068

1069
  double start = 40.;
22✔
1070
  auto it = autoPos.lower_bound(start);
22✔
1071
  double distance = 0.;
22✔
1072
  if(it != autoPos.end())
22✔
1073
  {
1074
    distance = std::abs(*it - start);
4✔
1075
    if(distance < 10)
4✔
1076
    {
1077
      do
4✔
1078
      {
1079
        start += 10.;
4✔
1080
        it = autoPos.lower_bound(start);
4✔
1081
        if(it != autoPos.end())
4✔
1082
          distance = std::abs(*it - start);
×
1083
      } while(it != autoPos.end() && distance < 10);
4✔
1084

1085
      if(distance < 10)
4✔
1086
        start += 10;
4✔
1087
    }
4✔
1088
  }
4✔
1089
  return {start, start};
22✔
1090
}
1091

1092
// TODO refactor by grepping for _cast.*IntervalModel
1093
IntervalModel* closestParentInterval(const QObject* parentObj) noexcept
×
1094
{
1095
  while(parentObj && !qobject_cast<const Scenario::IntervalModel*>(parentObj))
×
1096
  {
1097
    parentObj = parentObj->parent();
×
1098
  }
1099
  return static_cast<IntervalModel*>(const_cast<QObject*>(parentObj));
×
1100
}
1101

1102
TimeVal timeDelta(const IntervalModel* self, const IntervalModel* parent)
×
1103
{
1104
  TimeVal delta = TimeVal::zero();
×
1105
  if(self == parent)
×
1106
    return delta;
×
1107

1108
  delta += self->date();
×
1109
  auto p = self->parent();
×
1110
  if(p)
×
1111
  {
1112
    p = p->parent();
×
1113
  }
×
1114

1115
  while(p)
×
1116
  {
1117
    if(auto pi = qobject_cast<const IntervalModel*>(p))
×
1118
    {
1119
      if(pi == parent)
×
1120
      {
1121
        return delta;
×
1122
      }
1123
      else
1124
      {
1125
        delta += pi->date();
×
1126
        if((p = p->parent()))
×
1127
          p = p->parent();
×
1128
      }
1129
    }
×
1130
    else
1131
    {
1132
      if((p = p->parent()))
×
1133
        p = p->parent();
×
1134
    }
1135
  }
1136

1137
  return TimeVal{};
×
1138
}
×
1139

1140
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc