ScenarioCreation_FromState.hpp
1 #pragma once
2 #include "ScenarioCreationState.hpp"
3 
4 #include <Scenario/Application/ScenarioEditionSettings.hpp>
5 #include <Scenario/Commands/Scenario/Creations/CreateInterval.hpp>
6 #include <Scenario/Commands/Scenario/Creations/CreateState.hpp>
7 #include <Scenario/Commands/Scenario/Displacement/MoveNewEvent.hpp>
8 #include <Scenario/Commands/Scenario/Displacement/MoveNewState.hpp>
9 #include <Scenario/Document/TimeSync/TimeSyncModel.hpp>
10 #include <Scenario/Palette/Tools/ScenarioRollbackStrategy.hpp>
11 #include <Scenario/Palette/Transitions/AnythingTransitions.hpp>
12 #include <Scenario/Palette/Transitions/EventTransitions.hpp>
13 #include <Scenario/Palette/Transitions/IntervalTransitions.hpp>
14 #include <Scenario/Palette/Transitions/NothingTransitions.hpp>
15 #include <Scenario/Palette/Transitions/StateTransitions.hpp>
16 #include <Scenario/Palette/Transitions/TimeSyncTransitions.hpp>
17 #include <Scenario/Process/Algorithms/Accessors.hpp>
18 
19 #include <QApplication>
20 #include <QFinalState>
21 
22 namespace Scenario
23 {
24 template <typename Scenario_T, typename ToolPalette_T>
25 class Creation_FromState final : public CreationState<Scenario_T, ToolPalette_T>
26 {
27 public:
29  const ToolPalette_T& stateMachine, const Scenario_T& scenarioPath,
30  const score::CommandStackFacade& stack, QState* parent)
31  : CreationState<Scenario_T, ToolPalette_T>{
32  stateMachine, stack, std::move(scenarioPath), parent}
33  {
34  using namespace Scenario::Command;
35  auto finalState = new QFinalState{this};
36  QObject::connect(finalState, &QState::entered, [&]() { this->clearCreatedIds(); });
37 
38  auto mainState = new QState{this};
39  {
40  auto pressed = new QState{mainState};
41  auto released = new QState{mainState};
42  auto move_nothing = new StrongQState<MoveOnNothing>{mainState};
43  auto move_state = new StrongQState<MoveOnState>{mainState};
44  auto move_event = new StrongQState<MoveOnEvent>{mainState};
45  auto move_timesync = new StrongQState<MoveOnTimeSync>{mainState};
46 
47  // General setup
48  mainState->setInitialState(pressed);
49  released->addTransition(finalState);
50 
51  // Release
52  score::make_transition<ReleaseOnAnything_Transition>(mainState, released);
53 
54  // Pressed -> ...
55  score::make_transition<MoveOnNothing_Transition<Scenario_T>>(
56  pressed, move_state, *this);
57  score::make_transition<MoveOnNothing_Transition<Scenario_T>>(
58  pressed, move_nothing, *this);
59 
61  // MoveOnNothing -> MoveOnNothing.
62  score::make_transition<MoveOnNothing_Transition<Scenario_T>>(
63  move_nothing, move_nothing, *this);
64 
65  // MoveOnNothing -> MoveOnState.
66  this->add_transition(move_nothing, move_state, [&]() {
67  this->rollback();
68  createToState();
69  });
70 
71  // MoveOnNothing -> MoveOnEvent.
72  this->add_transition(move_nothing, move_event, [&]() {
73  this->rollback();
74  createToEvent();
75  });
76 
77  // MoveOnNothing -> MoveOnTimeSync
78  this->add_transition(move_nothing, move_timesync, [&]() {
79  this->rollback();
80  createToTimeSync();
81  });
82 
84  // MoveOnState -> MoveOnNothing
85  this->add_transition(move_state, move_nothing, [&]() {
86  this->rollback();
87  createToNothing();
88  });
89 
90  // MoveOnState -> MoveOnState
91  // We don't do anything, the interval should not move.
92 
93  // MoveOnState -> MoveOnEvent
94  this->add_transition(move_state, move_event, [&]() {
95  this->rollback();
96  createToEvent();
97  });
98 
99  // MoveOnState -> MoveOnTimeSync
100  this->add_transition(move_state, move_timesync, [&]() {
101  this->rollback();
102  createToTimeSync();
103  });
104 
106  // MoveOnEvent -> MoveOnNothing
107  this->add_transition(move_event, move_nothing, [&]() {
108  this->rollback();
109  createToNothing();
110  });
111 
112  // MoveOnEvent -> MoveOnState
113  this->add_transition(move_event, move_state, [&]() {
114  if(this->clickedState && this->hoveredState)
115  {
116  if(this->m_parentSM.model().state(*this->clickedState).eventId()
117  != this->m_parentSM.model().state(*this->hoveredState).eventId())
118  {
119  this->rollback();
120  createToState();
121  }
122  }
123  });
124 
125  // MoveOnEvent -> MoveOnEvent
126  score::make_transition<MoveOnEvent_Transition<Scenario_T>>(
127  move_event, move_event, *this);
128 
129  // MoveOnEvent -> MoveOnTimeSync
130  this->add_transition(move_event, move_timesync, [&]() {
131  this->rollback();
132  createToTimeSync();
133  });
134 
136  // MoveOnTimeSync -> MoveOnNothing
137  this->add_transition(move_timesync, move_nothing, [&]() {
138  this->rollback();
139  createToNothing();
140  });
141 
142  // MoveOnTimeSync -> MoveOnState
143  this->add_transition(move_timesync, move_state, [&]() {
144  this->rollback();
145  createToState();
146  });
147 
148  // MoveOnTimeSync -> MoveOnEvent
149  this->add_transition(move_timesync, move_event, [&]() {
150  this->rollback();
151  createToEvent();
152  });
153 
154  // MoveOnTimeSync -> MoveOnTimeSync
155  score::make_transition<MoveOnTimeSync_Transition<Scenario_T>>(
156  move_timesync, move_timesync, *this);
157 
158  // What happens in each state.
159  QObject::connect(pressed, &QState::entered, [&]() {
160  this->m_clickedPoint = this->currentPoint;
161  createToNothing();
162  });
163 
164  QObject::connect(move_nothing, &QState::entered, [&]() {
165  if(this->createdIntervals.empty() || this->createdEvents.empty())
166  {
167  this->rollback();
168  createToNothing();
169  return;
170  }
171 
172  Scenario::EditionSettings& settings = this->m_parentSM.editionSettings();
173  if(settings.tool() == Scenario::Tool::CreateGraph)
174  {
175  this->m_dispatcher.template submit<MoveNewEvent>(
176  this->m_scenario, this->createdIntervals.last(),
177  this->createdEvents.last(), this->currentPoint.date, this->currentPoint.y,
178  false);
179  return;
180  }
181 
182  this->currentPoint.date = stateMachine.magnetic().getPosition(
183  &stateMachine.model(), this->currentPoint.date);
184 
185  if(this->currentPoint.date <= this->m_clickedPoint.date)
186  {
187  this->currentPoint.date = this->m_clickedPoint.date + TimeVal::fromMsecs(10);
188  ;
189  }
190 
191  auto sequence = settings.tool() == Tool::CreateSequence;
192  if(sequence)
193  {
194  if(this->clickedState)
195  {
196  const auto& st = this->m_parentSM.model().state(*this->clickedState);
197  this->currentPoint.y = st.heightPercentage();
198  }
199  }
200 
201  this->m_dispatcher.template submit<MoveNewEvent>(
202  this->m_scenario, this->createdIntervals.last(), this->createdEvents.last(),
203  this->currentPoint.date, this->currentPoint.y, sequence);
204  });
205 
206  QObject::connect(move_event, &QState::entered, [&]() {
207  if(this->createdStates.empty())
208  {
209  this->rollback();
210  createToEvent();
211  return;
212  }
213 
214  if(this->currentPoint.date <= this->m_clickedPoint.date)
215  {
216  return;
217  }
218 
219  this->m_dispatcher.template submit<MoveNewState>(
220  this->m_scenario, this->createdStates.last(), this->currentPoint.y);
221  });
222 
223  QObject::connect(move_timesync, &QState::entered, [&]() {
224  if(this->createdStates.empty())
225  {
226  this->rollback();
227  createToTimeSync();
228  return;
229  }
230 
231  if(this->currentPoint.date <= this->m_clickedPoint.date)
232  {
233  return;
234  }
235 
236  this->m_dispatcher.template submit<MoveNewState>(
237  this->m_scenario, this->createdStates.last(), this->currentPoint.y);
238  });
239 
240  QObject::connect(released, &QState::entered, this, &Creation_FromState::commit);
241  }
242 
243  auto rollbackState = new QState{this};
244  score::make_transition<score::Cancel_Transition>(mainState, rollbackState);
245  rollbackState->addTransition(finalState);
246 
247  QObject::connect(
248  rollbackState, &QState::entered, this, &Creation_FromState::rollback);
249  this->setInitialState(mainState);
250  }
251 
252 private:
253  template <typename Fun>
254  void creationCheck(Fun&& fun)
255  {
256  const auto& scenar = this->m_parentSM.model();
257  if(!this->clickedState)
258  return;
259  Scenario::EditionSettings& settings = this->m_parentSM.editionSettings();
260  const bool sequence = settings.tool() == Tool::CreateSequence;
261  const bool new_event = qApp->keyboardModifiers() & Qt::ALT;
262  const bool graph_interval = settings.tool() == Scenario::Tool::CreateGraph;
263  auto& st = scenar.state(*this->clickedState);
264  auto& ev = Scenario::parentEvent(st, scenar);
265  if(ev.date() > this->currentPoint.date && !graph_interval)
266  return;
267 
268  if(new_event && !sequence)
269  {
270  // Create new event on the timesync
271  auto tn = ev.timeSync();
273  this->m_scenario, tn, this->currentPoint.y};
274  this->m_dispatcher.submit(cmd);
275 
276  this->createdEvents.append(cmd->createdEvent());
277  this->createdStates.append(cmd->createdState());
278  fun(this->createdStates.first());
279  }
280  else
281  {
282  if(!sequence)
283  {
284  // Create new state on the event
285  auto cmd = new Scenario::Command::CreateState{
286  this->m_scenario, st.eventId(), this->currentPoint.y};
287  this->m_dispatcher.submit(cmd);
288 
289  this->createdStates.append(cmd->createdState());
290  fun(this->createdStates.first());
291  }
292  else
293  {
294  if(!st.nextInterval()) // TODO & deltaY < deltaX
295  {
296  this->currentPoint.y = st.heightPercentage();
297  fun(*this->clickedState);
298  }
299  else
300  {
301  // Create new state on the event
302  auto cmd = new Scenario::Command::CreateState{
303  this->m_scenario, st.eventId(), this->currentPoint.y};
304  this->m_dispatcher.submit(cmd);
305 
306  this->createdStates.append(cmd->createdState());
307  fun(this->createdStates.first());
308  // create a single state on the same event (deltaY > deltaX)
309  }
310  }
311  }
312  }
313 
314  // Note : clickedEvent is set at startEvent if clicking in the background.
315  void createToNothing()
316  {
317  creationCheck([&](const Id<StateModel>& id) { this->createToNothing_base(id); });
318  }
319 
320  void createToTimeSync()
321  {
322  creationCheck([&](const Id<StateModel>& id) { this->createToTimeSync_base(id); });
323  }
324 
325  void createToEvent()
326  {
327  if(this->clickedState)
328  {
329  if(this->hoveredEvent
330  == this->m_parentSM.model().state(*this->clickedState).eventId())
331  {
332  creationCheck([&](const Id<StateModel>& id) {});
333  }
334  else
335  {
336  creationCheck([&](const Id<StateModel>& id) { this->createToEvent_base(id); });
337  }
338  }
339  }
340 
341  void createToState()
342  {
343  if(this->hoveredState)
344  {
345  auto& st = this->m_parentSM.model().states.at(*this->hoveredState);
346  if(!st.previousInterval())
347  {
348  // No previous interval -> we create a new interval and link it to
349  // this state
350  creationCheck([&](const Id<StateModel>& id) { this->createToState_base(id); });
351  }
352  else
353  {
354  // Previous interval -> we add a new state to the event and link to
355  // it.
356  this->hoveredEvent = st.eventId();
357  creationCheck([&](const Id<StateModel>& id) { this->createToEvent_base(id); });
358  }
359  }
360  }
361 };
362 }
Definition: CreateEvent_State.hpp:25
Definition: CreateState.hpp:22
Definition: ScenarioCreation_FromState.hpp:26
Creation_FromState(const ToolPalette_T &stateMachine, const Scenario_T &scenarioPath, const score::CommandStackFacade &stack, QState *parent)
Definition: ScenarioCreation_FromState.hpp:28
Definition: ScenarioCreationState.hpp:66
Definition: ScenarioEditionSettings.hpp:14
The id_base_t class.
Definition: Identifier.hpp:57
A small abstraction layer over the score::CommandStack.
Definition: CommandStackFacade.hpp:20
Main plug-in of score.
Definition: score-plugin-dataflow/Dataflow/PortItem.hpp:14