Loading...
Searching...
No Matches
MoveIntervalState.hpp
1#pragma once
2
3#include <Scenario/Commands/Scenario/Displacement/MoveEventMeta.hpp>
4#include <Scenario/Commands/Scenario/Merge/MergeEvents.hpp>
5#include <Scenario/Document/Event/EventPresenter.hpp>
6#include <Scenario/Document/Event/EventView.hpp>
7#include <Scenario/Document/Interval/Temporal/TemporalIntervalPresenter.hpp>
8#include <Scenario/Document/Interval/Temporal/TemporalIntervalView.hpp>
9#include <Scenario/Document/State/StatePresenter.hpp>
10#include <Scenario/Document/State/StateView.hpp>
11#include <Scenario/Document/TimeSync/TimeSyncPresenter.hpp>
12#include <Scenario/Document/TimeSync/TimeSyncView.hpp>
13#include <Scenario/Document/TimeSync/TriggerView.hpp>
14#include <Scenario/Palette/ScenarioPaletteBaseStates.hpp>
15#include <Scenario/Palette/Tools/ScenarioRollbackStrategy.hpp>
16#include <Scenario/Palette/Transitions/AnythingTransitions.hpp>
17#include <Scenario/Palette/Transitions/EventTransitions.hpp>
18#include <Scenario/Palette/Transitions/IntervalTransitions.hpp>
19#include <Scenario/Palette/Transitions/NothingTransitions.hpp>
20#include <Scenario/Palette/Transitions/TimeSyncTransitions.hpp>
21#include <Scenario/Process/Algorithms/Accessors.hpp>
22
23#include <score/command/Dispatchers/MultiOngoingCommandDispatcher.hpp>
24#include <score/locking/ObjectLocker.hpp>
25
26#include <QApplication>
27#include <QFinalState>
28
29namespace Scenario
30{
31template <typename T>
32class MoveIntervalState final : public StateBase<Scenario::ProcessModel>
33{
34public:
36 const T& stateMachine, const Scenario::ProcessModel& scenario,
38 QState* parent)
39 : StateBase<Scenario::ProcessModel>{scenario, parent}
40 , m_sm{stateMachine}
41 , m_movingDispatcher{stack}
42 {
43 this->setObjectName("MoveIntervalState");
44 using namespace Scenario::Command;
45 auto finalState = new QFinalState{this};
46
47 auto mainState = new QState{this};
48 {
49 auto pressed = new QState{mainState};
50 auto released = new QState{mainState};
51 auto moving = new QState{mainState};
52
53 // General setup
54 mainState->setInitialState(pressed);
55 released->addTransition(finalState);
56
57 auto t_pressed
58 = score::make_transition<MoveOnAnything_Transition<Scenario::ProcessModel>>(
59 pressed, moving, *this);
60 QObject::connect(t_pressed, &QAbstractTransition::triggered, [&]() {
61 auto& scenar = stateMachine.model();
62 m_initialClick = this->currentPoint;
63 if(!this->clickedInterval)
64 return;
65
66 auto& cst = scenario.interval(*this->clickedInterval);
67 auto& sev = Scenario::startEvent(cst, scenario);
68 auto& eev = Scenario::endEvent(cst, scenario);
69
70 m_intervalInitialPoint = {cst.date(), cst.heightPercentage()};
71 m_lastDate = m_intervalInitialPoint.date;
72
73 auto prev_csts = previousNonGraphIntervals(sev, scenar);
74 if(!prev_csts.empty())
75 {
76 // We find the one that starts the latest.
77 TimeVal t = TimeVal::zero();
78 for(const auto& cst_id : prev_csts)
79 {
80 const auto& other_date = scenar.interval(cst_id).date();
81 if(other_date > t)
82 t = other_date;
83 }
84
85 // These 10 milliseconds are here to prevent "squashing"
86 // processes to zero, which leads to problem (they can't scale back!)
87 this->m_pressedPrevious = t + TimeVal::fromMsecs(10);
88 }
89 else
90 {
91 this->m_pressedPrevious = std::nullopt;
92 }
93
94 this->m_startEventCanBeMerged = previousIntervals(sev, scenar).empty();
95 this->m_endEventCanBeMerged = nextIntervals(eev, scenar).empty();
96 });
97
98 score::make_transition<ReleaseOnAnything_Transition>(pressed, finalState);
99 score::make_transition<MoveOnAnything_Transition<Scenario::ProcessModel>>(
100 moving, moving, *this);
101 score::make_transition<ReleaseOnAnything_Transition>(moving, released);
102
103 QObject::connect(moving, &QState::entered, [&] {
104 auto& scenario = stateMachine.model();
105 if(!this->clickedInterval)
106 return;
107 auto& cst = scenario.interval(*this->clickedInterval);
108 if(cst.graphal())
109 return;
110 auto& sst = Scenario::startState(cst, scenario);
111 auto& sev = Scenario::parentEvent(sst, scenario);
112 auto& sts = Scenario::parentTimeSync(sev, scenario);
113
114 if(qApp->keyboardModifiers() & Qt::ShiftModifier)
115 {
116 if(&sts == &scenario.startTimeSync())
117 return;
118
119 m_lastDate = m_intervalInitialPoint.date
120 + (this->currentPoint.date - m_initialClick.date);
121 if(this->m_pressedPrevious)
122 m_lastDate = std::max(m_lastDate, *this->m_pressedPrevious);
123
124 m_lastDate = stateMachine.magnetic().getPosition(&sts, m_lastDate);
125 m_lastDate = std::max(m_lastDate, TimeVal{});
126 }
127 else
128 {
129 m_lastDate = m_intervalInitialPoint.date;
130 }
131
132 // If the start event does not have previous intervals
133 // we will try to merge the start sync with other syncs...
134 // Idea : if it's an empty interval we can maybe merge...
135 // and if we are dragging an interval we can maybe create an empty
136 // interval between...
138
139 this->m_movingDispatcher.template submit<Command::MoveEventMeta>(
140 this->m_scenario, sev.id(), m_lastDate,
141 m_intervalInitialPoint.y + (this->currentPoint.y - m_initialClick.y),
142 stateMachine.editionSettings().expandMode(),
143 stateMachine.editionSettings().lockMode(), cst.startState());
144 });
145
146 QObject::connect(released, &QState::entered, [&]() {
147 if(!this->clickedInterval)
148 return;
149 auto& cst = scenario.interval(*this->clickedInterval);
150 if(cst.graphal())
151 return;
152 auto& sst = Scenario::startState(cst, scenario);
153 auto& sev = Scenario::parentEvent(sst, scenario);
154 auto& sts = Scenario::parentTimeSync(sev, scenario);
155
156 if(qApp->keyboardModifiers() & Qt::ShiftModifier)
157 {
158 if(&sts == &scenario.startTimeSync())
159 return;
160
161 if(this->m_startEventCanBeMerged)
162 {
163 merge(cst, Scenario::startState(cst, m_scenario), m_lastDate);
164 }
165 /*
166 if(this->m_endEventCanBeMerged)
167 {
168 merge(cst, Scenario::endState(cst, m_scenario), m_lastDate +
169 cst.duration.defaultDuration());
170 }
171 */
172 }
173
174 m_movingDispatcher.template commit<Command::MoveIntervalMacro>();
175 m_pressedPrevious = {};
176 });
177 }
178
179 auto rollbackState = new QState{this};
180 score::make_transition<score::Cancel_Transition>(mainState, rollbackState);
181 rollbackState->addTransition(finalState);
182 QObject::connect(rollbackState, &QState::entered, [&]() {
183 this->rollback();
184 m_pressedPrevious = {};
185 });
186
187 this->setInitialState(mainState);
188 }
189
190private:
191 void rollback() { m_movingDispatcher.template rollback<DefaultRollbackStrategy>(); }
192
193 void merge(const IntervalModel& cst, const StateModel& st, TimeVal date)
194 {
195 auto& ev = Scenario::parentEvent(st, m_scenario);
196 auto& ts = Scenario::parentTimeSync(ev, m_scenario);
197
198 auto& sst_pres = m_sm.presenter().state(st.id());
199 auto& sev_pres = m_sm.presenter().event(ev.id());
200 auto& sts_pres = m_sm.presenter().timeSync(ts.id());
201 auto& itv_pres = m_sm.presenter().interval(cst.id());
202
203 std::vector<QGraphicsItem*> toIgnore;
204 toIgnore.push_back(sst_pres.view());
205 toIgnore.push_back(sev_pres.view());
206 toIgnore.push_back(sts_pres.view());
207 toIgnore.push_back(&sts_pres.trigger());
208 toIgnore.push_back(itv_pres.view());
209 QGraphicsItem* item = m_sm.itemAt({date, cst.heightPercentage()}, toIgnore);
210
211 if(auto stateToMerge = qgraphicsitem_cast<Scenario::StateView*>(item))
212 {
213 // this->rollback();
214 this->m_movingDispatcher.template submit<Command::MergeEvents>(
215 this->m_scenario, ev.id(),
216 Scenario::parentEvent(stateToMerge->presenter().model().id(), this->m_scenario)
217 .id());
218 }
219 else if(auto eventToMerge = qgraphicsitem_cast<Scenario::EventView*>(item))
220 {
221 // this->rollback();
222 this->m_movingDispatcher.template submit<Command::MergeEvents>(
223 this->m_scenario, ev.id(), eventToMerge->presenter().model().id());
224 }
225 else if(auto syncToMerge = qgraphicsitem_cast<Scenario::TimeSyncView*>(item))
226 {
227 // this->rollback();
228 this->m_movingDispatcher.template submit<Command::MergeTimeSyncs>(
229 this->m_scenario, ts.id(), syncToMerge->presenter().model().id());
230 }
231 }
232
233 const T& m_sm;
234 MultiOngoingCommandDispatcher m_movingDispatcher;
235
236 Scenario::Point m_initialClick{};
237 Scenario::Point m_intervalInitialPoint{};
238 std::optional<TimeVal> m_pressedPrevious;
239 TimeVal m_lastDate{};
240 bool m_startEventCanBeMerged{};
241 bool m_endEventCanBeMerged{};
242};
243
244}
The MultiOngoingCommandDispatcher class.
Definition MultiOngoingCommandDispatcher.hpp:33
Definition IntervalModel.hpp:50
Definition MoveIntervalState.hpp:33
MoveIntervalState(const T &stateMachine, const Scenario::ProcessModel &scenario, const score::CommandStackFacade &stack, score::ObjectLocker &locker, QState *parent)
Definition MoveIntervalState.hpp:35
The core hierarchical and temporal process of score.
Definition ScenarioModel.hpp:37
Definition ScenarioPaletteBaseStates.hpp:20
Definition StateModel.hpp:63
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