Loading...
Searching...
No Matches
MoveAndMergeState.hpp
1#pragma once
2
3#include <Scenario/Commands/Scenario/Displacement/MoveEventMeta.hpp>
4#include <Scenario/Commands/Scenario/Merge/MergeEvents.hpp>
5#include <Scenario/Commands/Scenario/Merge/MergeTimeSyncs.hpp>
6#include <Scenario/Document/Event/EventPresenter.hpp>
7#include <Scenario/Document/Event/EventView.hpp>
8#include <Scenario/Document/State/StatePresenter.hpp>
9#include <Scenario/Document/State/StateView.hpp>
10#include <Scenario/Document/TimeSync/TimeSyncPresenter.hpp>
11#include <Scenario/Document/TimeSync/TimeSyncView.hpp>
12#include <Scenario/Document/TimeSync/TriggerView.hpp>
13#include <Scenario/Palette/ScenarioPaletteBaseStates.hpp>
14#include <Scenario/Palette/Tools/ScenarioRollbackStrategy.hpp>
15#include <Scenario/Palette/Transitions/AnythingTransitions.hpp>
16#include <Scenario/Palette/Transitions/EventTransitions.hpp>
17#include <Scenario/Palette/Transitions/NothingTransitions.hpp>
18#include <Scenario/Palette/Transitions/StateTransitions.hpp>
19#include <Scenario/Palette/Transitions/TimeSyncTransitions.hpp>
20#include <Scenario/Process/Algorithms/Accessors.hpp>
21
22#include <Magnetism/MagnetismAdjuster.hpp>
23
24#include <score/command/Dispatchers/MultiOngoingCommandDispatcher.hpp>
25#include <score/locking/ObjectLocker.hpp>
26//#include <Scenario/Application/ScenarioValidity.hpp>
27#include <QFinalState>
28
29namespace Scenario
30{
31class ToolPalette;
32/*
33template <typename TheCommand>
34class BugfixDispatcher final : public ICommandDispatcher
35{
36public:
37 const Scenario::ProcessModel& scenario;
38 BugfixDispatcher(const score::CommandStackFacade& stack
39 , const Scenario::ProcessModel& p)
40 : ICommandDispatcher{stack}
41 , scenario{p}
42 {
43 }
44
45 template <typename... Args>
46 void submit(Args&&... args)
47 {
48 if (!m_cmd)
49 {
50 stack().disableActions();
51 m_cmd = std::make_unique<TheCommand>(std::forward<Args>(args)...);
52 ScenarioValidityChecker::checkValidity(scenario);
53 m_cmd->redo(stack().context());
54 ScenarioValidityChecker::checkValidity(scenario);
55 }
56 else
57 {
58 m_cmd->update(std::forward<Args>(args)...);
59 ScenarioValidityChecker::checkValidity(scenario);
60 m_cmd->redo(stack().context());
61 ScenarioValidityChecker::checkValidity(scenario);
62 }
63 }
64
65 void commit()
66 {
67 if (m_cmd)
68 {
69 SendStrategy::Quiet::send(stack(), m_cmd.release());
70 stack().enableActions();
71 ScenarioValidityChecker::checkValidity(scenario);
72 }
73 }
74
75 void rollback()
76 {
77 ScenarioValidityChecker::checkValidity(scenario);
78 if (m_cmd)
79 {
80 m_cmd->undo(stack().context());
81 ScenarioValidityChecker::checkValidity(scenario);
82 stack().enableActions();
83 }
84 m_cmd.reset();
85 }
86
87private:
88 std::unique_ptr<TheCommand> m_cmd;
89};
90*/
91template <
92 typename MoveEventCommand_T, // MoveEventMeta
93 typename Scenario_T, typename ToolPalette_T>
94class MoveEventState final : public StateBase<Scenario_T>
95{
96public:
98 const ToolPalette_T& stateMachine, const Scenario_T& scenarioPath,
100 QState* parent)
101 : StateBase<Scenario_T>{scenarioPath, parent}
102 , m_sm{stateMachine}
103 , m_movingDispatcher{stack}
104 {
105 this->setObjectName("MoveEventState");
106 using namespace Scenario::Command;
107 auto finalState = new QFinalState{this};
108
109 auto mainState = new QState{this};
110 {
111 auto pressed = new QState{mainState};
112 auto released = new QState{mainState};
113
114 auto onlyMoving = new QState{mainState};
115
116 // General setup
117 mainState->setInitialState(pressed);
118 released->addTransition(finalState);
119
120 // ***************************************
121 // transitions
122
123 // press
124 score::make_transition<MoveOnAnything_Transition<Scenario_T>>(
125 pressed, onlyMoving, *this);
126 score::make_transition<ReleaseOnAnything_Transition>(pressed, finalState);
127
128 // update commands
129 score::make_transition<MoveOnAnything_Transition<Scenario_T>>(
130 onlyMoving, onlyMoving, *this);
131
132 // commit merging
133 score::make_transition<ReleaseOnAnything_Transition>(onlyMoving, released);
134
135 // ********************************************
136 // What happens in each state.
137
138 QObject::connect(pressed, &QState::entered, [&]() {
139 auto& scenar = stateMachine.model();
140 auto evId{this->clickedEvent};
141 if(!bool(evId) && bool(this->clickedState))
142 {
143 const Scenario::StateModel& st = scenar.state(*this->clickedState);
144 evId = st.eventId();
145 m_origPos.y = st.heightPercentage();
146 }
147
148 if(!evId)
149 return;
150
151 const Scenario::EventModel& ev = scenar.event(*evId);
152 m_origPos.date = ev.date();
153
154 auto prev_csts = previousNonGraphIntervals(ev, scenar);
155 if(!prev_csts.empty())
156 {
157 // We find the one that starts the latest.
158 TimeVal t = TimeVal::zero();
159 for(const auto& cst_id : prev_csts)
160 {
161 const auto& other_date = scenar.interval(cst_id).date();
162 if(other_date > t)
163 t = other_date;
164 }
165
166 // These 10 milliseconds are here to prevent "squashing"
167 // processes to zero, which leads to problem (they can't scale back!)
168 this->m_pressedPrevious = t + TimeVal::fromMsecs(10);
169 }
170 else
171 {
172 this->m_pressedPrevious = std::nullopt;
173 }
174
175 this->m_pressPos = this->currentPoint;
176 });
177
178 QObject::connect(onlyMoving, &QState::entered, [&]() {
179 auto& scenar = stateMachine.model();
180 // If we came here through a state.
181 auto evId = this->clickedEvent;
182 if(!bool(evId) && bool(this->clickedState))
183 {
184 evId = scenar.state(*this->clickedState).eventId();
185 }
186
187 if(!evId)
188 return;
189
190 TimeVal adjDate
191 = this->m_origPos.date + (this->currentPoint.date - this->m_pressPos.date);
192 m_lastDate = this->m_pressedPrevious
193 ? std::max(adjDate, *this->m_pressedPrevious)
194 : adjDate;
195
196 auto [magPos, snap] = stateMachine.magnetic().getPosition(
197 &Scenario::parentTimeSync(*evId, stateMachine.model()), m_lastDate);
198 m_lastDate = magPos;
199 stateMachine.presenter().setSnapLine(magPos, snap);
200
201 m_lastDate = std::max(m_lastDate, TimeVal{});
202
203 if(this->clickedState)
204 {
205 auto new_y = m_origPos.y + (this->currentPoint.y - this->m_pressPos.y);
206 this->m_movingDispatcher.template submit<MoveEventCommand_T>(
207 this->m_scenario, *evId, m_lastDate, new_y,
208 stateMachine.editionSettings().expandMode(),
209 stateMachine.editionSettings().lockMode(), *this->clickedState);
210 }
211 else
212 {
213 this->m_movingDispatcher.template submit<MoveEventCommand_T>(
214 this->m_scenario, *evId, m_lastDate, this->currentPoint.y,
215 stateMachine.editionSettings().expandMode(),
216 stateMachine.editionSettings().lockMode());
217 }
218 });
219
220 QObject::connect(released, &QState::entered, [&] {
221 if constexpr(std::is_same_v<Scenario::ToolPalette, ToolPalette_T>)
222 {
223 if(this->clickedState)
224 {
225 auto& st = this->m_scenario.state(*this->clickedState);
226 merge(st, this->m_lastDate);
227 }
228 }
229
230 m_movingDispatcher.template commit<Command::MoveStateMacro>();
231 this->m_pressPos = {};
232 this->m_pressedPrevious = {};
233 stateMachine.presenter().setSnapLine({}, false);
234 });
235 }
236
237 auto rollbackState = new QState{this};
238 score::make_transition<score::Cancel_Transition>(mainState, rollbackState);
239 rollbackState->addTransition(finalState);
240 QObject::connect(rollbackState, &QState::entered, [&] {
241 this->rollback();
242 this->m_pressPos = {};
243 this->m_pressedPrevious = {};
244 stateMachine.presenter().setSnapLine({}, false);
245 });
246
247 this->setInitialState(mainState);
248 }
249
250 void rollback() { m_movingDispatcher.template rollback<DefaultRollbackStrategy>(); }
251
252 void merge(const StateModel& st, TimeVal date)
253 {
254 auto& ev = Scenario::parentEvent(st, this->m_scenario);
255 auto& ts = Scenario::parentTimeSync(ev, this->m_scenario);
256
257 TimeSyncPresenter& sts_pres = m_sm.presenter().timeSync(ts.id());
258
259 std::vector<QGraphicsItem*> toIgnore;
260 toIgnore.push_back(sts_pres.view());
261 toIgnore.push_back(&sts_pres.trigger());
262 for(auto& ev_id : sts_pres.model().events())
263 {
264 auto& ev = m_sm.presenter().event(ev_id);
265 toIgnore.push_back(ev.view());
266 for(auto& st_id : ev.model().states())
267 {
268 auto& st = m_sm.presenter().state(st_id);
269 toIgnore.push_back(st.view());
270 }
271 }
272
273 for(auto& itv : Scenario::previousIntervals(sts_pres.model(), this->m_scenario))
274 {
275 auto& sst_pres
276 = m_sm.presenter().state(this->m_scenario.interval(itv).startState());
277 auto& sev_pres = m_sm.presenter().event(sst_pres.model().eventId());
278 TimeSyncPresenter& sts_pres
279 = m_sm.presenter().timeSync(sev_pres.model().timeSync());
280
281 toIgnore.push_back(sts_pres.view());
282 for(auto& ev : sts_pres.events())
283 {
284 toIgnore.push_back(ev->view());
285 for(auto& st : ev->states())
286 {
287 toIgnore.push_back(st->view());
288 }
289 }
290
291 toIgnore.push_back(&sts_pres.trigger());
292 }
293
294 for(auto& itv : Scenario::nextIntervals(sts_pres.model(), this->m_scenario))
295 {
296 auto& sst_pres = m_sm.presenter().state(this->m_scenario.interval(itv).endState());
297 auto& sev_pres = m_sm.presenter().event(sst_pres.model().eventId());
298 auto& sts_pres = m_sm.presenter().timeSync(sev_pres.model().timeSync());
299
300 toIgnore.push_back(sts_pres.view());
301 for(auto& ev : sts_pres.events())
302 {
303 toIgnore.push_back(ev->view());
304 for(auto& st : ev->states())
305 {
306 toIgnore.push_back(st->view());
307 }
308 }
309
310 toIgnore.push_back(&sts_pres.trigger());
311 }
312
313 QGraphicsItem* item = m_sm.itemAt({date, st.heightPercentage()}, toIgnore);
314
315 if(auto stateToMerge = qgraphicsitem_cast<Scenario::StateView*>(item))
316 {
317 // this->rollback();
318 this->m_movingDispatcher.template submit<Command::MergeEvents>(
319 this->m_scenario, ev.id(),
320 Scenario::parentEvent(stateToMerge->presenter().model().id(), this->m_scenario)
321 .id());
322 }
323 else if(auto eventToMerge = qgraphicsitem_cast<Scenario::EventView*>(item))
324 {
325 // this->rollback();
326 this->m_movingDispatcher.template submit<Command::MergeEvents>(
327 this->m_scenario, ev.id(), eventToMerge->presenter().model().id());
328 }
329 else if(auto syncToMerge = qgraphicsitem_cast<Scenario::TimeSyncView*>(item))
330 {
331 // this->rollback();
332 this->m_movingDispatcher.template submit<Command::MergeTimeSyncs>(
333 this->m_scenario, ts.id(), syncToMerge->presenter().model().id());
334 }
335 }
336
337 const ToolPalette_T& m_sm;
338 MultiOngoingCommandDispatcher m_movingDispatcher;
339 Scenario::Point m_pressPos{}; // where the click landed in the scenario
340 Scenario::Point m_origPos{}; // original position of the object being moved
341 std::optional<TimeVal> m_pressedPrevious;
342 TimeVal m_lastDate;
343 bool m_startEventCanBeMerged{};
344 bool m_endEventCanBeMerged{};
345};
346
348// * Specialization for the ScenarioModel allows merging
349// */
350
351// template <
352// typename MoveEventCommand_T, // MoveEventMeta
353// typename ToolPalette_T>
354// class MoveEventState<MoveEventCommand_T, Scenario::ProcessModel,
355// ToolPalette_T>
356// final : public StateBase<Scenario::ProcessModel>
357//{
358// public:
359// MoveEventState(
360// const ToolPalette_T& stateMachine,
361// const Scenario::ProcessModel& scenarioPath,
362// const score::CommandStackFacade& stack,
363// score::ObjectLocker& locker,
364// QState* parent)
365// : StateBase<Scenario::ProcessModel>{scenarioPath, parent}
366// , m_movingDispatcher{stack}
367// , m_mergingTnDispatcher{stack}
368// , m_mergingEventDispatcher{stack}
369// {
370// this->setObjectName("MoveEventState");
371// using namespace Scenario::Command;
372// auto finalState = new QFinalState{this};
373
374// auto mainState = new QState{this};
375// {
376// auto pressed = new QState{mainState};
377// auto released = new QState{mainState};
378
379// auto onlyMoving = new QState{mainState};
380// auto mergingOnTimeSync = new QState{mainState};
381// auto mergingOnEvent = new QState{mainState};
382
383// auto rollbackTnMerging = new QState{mainState};
384// auto rollbackEventMerging = new QState{mainState};
385
386// // General setup
387// mainState->setInitialState(pressed);
388// released->addTransition(finalState);
389
390// rollbackTnMerging->addTransition(onlyMoving);
391// rollbackEventMerging->addTransition(onlyMoving);
392
393// // ***************************************
394// // transitions
395
396// // press
397// score::
398// make_transition<MoveOnAnything_Transition<Scenario::ProcessModel>>(
399// pressed, onlyMoving, *this);
400// score::make_transition<ReleaseOnAnything_Transition>(
401// pressed, finalState);
402
403// // update commands
404// // score::make_transition<MoveOnAnything_Transition<Scenario_T>>(
405// // onlyMoving, onlyMoving, *this);
406// //*
407// score::
408// make_transition<MoveOnAnythingButPonctual_Transition<Scenario::
409// ProcessModel>>(
410// onlyMoving, onlyMoving, *this);
411
412// score::
413// make_transition<MoveOnTimeSync_Transition<Scenario::ProcessModel>>(
414// onlyMoving, mergingOnTimeSync, *this);
415
416// score::make_transition<MoveOnEvent_Transition<Scenario::ProcessModel>>(
417// onlyMoving, mergingOnEvent, *this);
418// score::make_transition<MoveOnEvent_Transition<Scenario::ProcessModel>>(
419// mergingOnEvent, mergingOnEvent, *this);
420
421// score::make_transition<MoveOnState_Transition<Scenario::ProcessModel>>(
422// onlyMoving, mergingOnEvent, *this);
423// score::make_transition<MoveOnState_Transition<Scenario::ProcessModel>>(
424// mergingOnEvent, mergingOnEvent, *this);
425// //*/
426// // rollback merging
427// score::
428// make_transition<MoveOnAnythingButTimeSync_Transition<Scenario::
429// ProcessModel>>(
430// mergingOnTimeSync, rollbackTnMerging, *this);
431// score::
432// make_transition<MoveOnAnythingButEvent_Transition<Scenario::
433// ProcessModel>>(
434// mergingOnEvent, rollbackEventMerging, *this);
435
436// // commit merging
437// score::make_transition<ReleaseOnAnything_Transition>(
438// mergingOnTimeSync, released);
439// score::make_transition<ReleaseOnAnything_Transition>(
440// mergingOnEvent, released);
441// score::make_transition<ReleaseOnAnything_Transition>(
442// onlyMoving, released);
443
444// // ********************************************
445// // What happens in each state.
446
447// QObject::connect(mergingOnTimeSync, &QState::entered, [&]() {
448// qDebug("mergingOnTimenode");
449
450// auto& scenar = stateMachine.model();
451// // If we came here through a state.
452// auto evId = this->clickedEvent;
453// if (!bool(evId) && bool(this->clickedState))
454// {
455// evId = scenar.state(*this->clickedState).eventId();
456// }
457// if (!evId)
458// return;
459
460// auto tnId = scenar.event(*evId).timeSync();
461
462// if (this->currentPoint.date <= this->m_clickedPoint.date)
463// {
464// auto& tn = scenar.timeSync(tnId);
465// const auto& prev = Scenario::previousIntervals(tn, scenar);
466// if(!prev.empty())
467// return;
468// }
469
470// if (this->hoveredTimeSync && tnId != this->hoveredTimeSync)
471// {
472// this->m_movingDispatcher.rollback();
473// this->m_mergingEventDispatcher.rollback();
474
475// this->m_mergingTnDispatcher.submit(
476// this->m_scenario,
477// tnId,
478// *this->hoveredTimeSync);
479// }
480// else
481// {
482// qDebug("stuck 1");
483// }
484
485// });
486// QObject::connect(rollbackTnMerging, &QState::entered, [&]() {
487// qDebug("rollbackTnMerging");
488// m_mergingTnDispatcher.rollback();
489// });
490
491// QObject::connect(mergingOnEvent, &QState::entered, [&]() {
492// qDebug("mergingOnEvent");
493
494// auto& scenar = stateMachine.model();
495// // If we came here through a state.
496// auto clickedEvId = this->clickedEvent;
497// if (!bool(clickedEvId) && bool(this->clickedState))
498// {
499// clickedEvId = scenar.state(*this->clickedState).eventId();
500// qDebug("event 1");
501// }
502// else
503// {
504// qDebug("event 2")
505// }
506// if (!clickedEvId) { qDebug("bye 1");
507// return;
508// }
509
510// auto destinationEvId = this->hoveredEvent;
511// if (!bool(destinationEvId) && bool(this->hoveredState))
512// {
513// destinationEvId = scenar.state(*this->hoveredState).eventId();
514// }
515
516// if (!destinationEvId) { qDebug("bye 2");
517// return;
518// }
519// auto tnId = scenar.event(*destinationEvId).timeSync();
520
521// if (this->currentPoint.date <= this->m_clickedPoint.date)
522// {
523// auto& tn = scenar.timeSync(tnId);
524// const auto& prev = Scenario::previousIntervals(tn, scenar);
525// if(!prev.empty()) { qDebug("bye 3");
526// return;
527// }
528// }
529
530// qDebug() << *clickedEvId << *destinationEvId;
531// if (*clickedEvId != *destinationEvId)
532// {
533// m_movingDispatcher.rollback();
534// m_mergingTnDispatcher.rollback();
535
536// m_mergingEventDispatcher.submit(
537// m_scenario,
538// *clickedEvId,
539// *destinationEvId);
540// }
541// else
542// {
543// m_mergingEventDispatcher.rollback();
544// m_mergingTnDispatcher.rollback();
545
546// TimeVal date
547// = this->m_pressedPrevious
548// ? max(this->currentPoint.date, *this->m_pressedPrevious)
549// : this->currentPoint.date;
550
551// if(this->clickedState)
552// {
553// this->m_movingDispatcher.submit(
554// m_scenario,
555// *clickedEvId,
556// date,
557// this->currentPoint.y,
558// stateMachine.editionSettings().expandMode(),
559// *this->clickedState);
560// }
561// else
562// {
563// this->m_movingDispatcher.submit(
564// m_scenario,
565// *clickedEvId,
566// date,
567// this->currentPoint.y,
568// stateMachine.editionSettings().expandMode());
569// }
570// }
571// });
572// QObject::connect(rollbackEventMerging, &QState::entered, [&]() {
573// qDebug("rollbackEventMerging");
574// m_mergingEventDispatcher.rollback();
575// });
576
577// QObject::connect(onlyMoving, &QState::entered, [&]() {
578// qDebug("onlyMoving");
579// auto& scenar = stateMachine.model();
580// // If we came here through a state.
581// auto evId = this->clickedEvent;
582// if (!bool(evId) && bool(this->clickedState))
583// {
584// evId = scenar.state(*this->clickedState).eventId();
585// }
586// if (!evId)
587// return;
588
589// TimeVal date
590// = this->m_pressedPrevious
591// ? std::max(this->currentPoint.date,
592// *this->m_pressedPrevious) : this->currentPoint.date;
593
594// date = std::max(date, TimeVal{});
595// if(this->clickedState)
596// {
597// this->m_movingDispatcher.submit(
598// m_scenario,
599// *evId,
600// date,
601// this->currentPoint.y,
602// stateMachine.editionSettings().expandMode(),
603// *this->clickedState);
604// }
605// else
606// {
607// this->m_movingDispatcher.submit(
608// m_scenario,
609// *evId,
610// date,
611// this->currentPoint.y,
612// stateMachine.editionSettings().expandMode());
613// }
614// });
615
616// QObject::connect(pressed, &QState::entered, [&]() {
617// this->m_clickedPoint = this->currentPoint;
618
619// const Scenario::ProcessModel& scenar = stateMachine.model();
620
621// // TODO refactor this part, it's used everywhere
622// auto evId = this->clickedEvent;
623// if (!bool(evId) && bool(this->clickedState))
624// {
625// evId = scenar.state(*this->clickedState).eventId();
626// }
627// if (!evId)
628// return;
629
630// auto prev_csts = Scenario::previousIntervals(scenar.event(*evId),
631// scenar); if (!prev_csts.empty())
632// {
633// // We find the one that starts the latest.
634// TimeVal t = TimeVal::zero();
635// for (const auto& cst_id : prev_csts)
636// {
637// const auto& other_date = scenar.interval(cst_id).date();
638// if (other_date > t)
639// t = other_date;
640// }
641
642// // These 10 milliseconds are here to prevent "squashing"
643// // processes to zero, which leads to problem (they can't scale
644// back!) this->m_pressedPrevious = t + TimeVal::fromMsecs(10);
645// }
646// else
647// {
648// this->m_pressedPrevious = std::nullopt;
649// }
650
651// });
652
653// QObject::connect(released, &QState::entered, [&]() {
654// m_movingDispatcher.commit();
655// m_mergingEventDispatcher.commit();
656// m_mergingTnDispatcher.commit();
657// m_pressedPrevious = std::nullopt;
658// });
659// }
660
661// auto rollbackState = new QState{this};
662// score::make_transition<score::Cancel_Transition>(
663// mainState, rollbackState);
664// rollbackState->addTransition(finalState);
665// QObject::connect(rollbackState, &QState::entered, [&]() {
666// m_movingDispatcher.rollback();
667// m_mergingTnDispatcher.rollback();
668// m_mergingEventDispatcher.rollback();
669// });
670
671// this->setInitialState(mainState);
672// }
673
674// SingleOngoingCommandDispatcher<MoveEventCommand_T> m_movingDispatcher;
675// SingleOngoingCommandDispatcher<Command::MergeTimeSyncs>
676// m_mergingTnDispatcher; SingleOngoingCommandDispatcher<Command::MergeEvents>
677// m_mergingEventDispatcher;
678
679// std::optional<TimeVal> m_pressedPrevious;
680// Scenario::Point m_clickedPoint;
681//};
682}
The MultiOngoingCommandDispatcher class.
Definition MultiOngoingCommandDispatcher.hpp:33
Definition EventModel.hpp:36
Definition MoveAndMergeState.hpp:95
Definition ScenarioPaletteBaseStates.hpp:20
Definition StateModel.hpp:63
Definition TimeSyncPresenter.hpp:29
A small abstraction layer over the score::CommandStack.
Definition CommandStackFacade.hpp:20
The ObjectLocker class.
Definition ObjectLocker.hpp:21
Main plug-in of score.
Definition score-plugin-dataflow/Dataflow/PortItem.hpp:13
Definition ScenarioPoint.hpp:13
Definition TimeValue.hpp:21