ScriptEditCommand.hpp
1 #pragma once
2 #include <Process/Dataflow/Port.hpp>
3 #include <Process/Process.hpp>
4 #include <Process/Script/ScriptEditor.hpp>
5 #include <Process/Script/ScriptProcess.hpp>
6 
7 #include <Scenario/Document/ScenarioDocument/ScenarioDocumentModel.hpp>
8 
9 #include <Dataflow/Commands/CableHelpers.hpp>
10 
11 #include <ossia/detail/algorithms.hpp>
12 namespace Scenario
13 {
14 using SavedPort = Dataflow::SavedPort;
15 
16 template <typename Process_T, typename Property_T>
17 class EditScript : public score::Command
18 {
19 public:
20  using param_type = typename Property_T::param_type;
21  using score::Command::Command;
22  EditScript(
23  const Process_T& model, param_type newScript, const score::DocumentContext& ctx)
24  : m_path{model}
25  , m_newScript{std::move(newScript)}
26  , m_oldScript{(model.*Property_T::get)()}
27  {
28  m_oldCables = Dataflow::saveCables({const_cast<Process_T*>(&model)}, ctx);
29 
30  for(auto& port : model.inlets())
31  m_oldInlets.emplace_back(SavedPort{port->name(), port->type(), port->saveData()});
32  for(auto& port : model.outlets())
33  m_oldOutlets.emplace_back(SavedPort{port->name(), port->type(), port->saveData()});
34  }
35 
36 private:
37  void undo(const score::DocumentContext& ctx) const override
38  {
39  auto& cmt = m_path.find(ctx);
40  // Remove all the cables that could have been added during
41  // the creation
42  Dataflow::removeCables(m_oldCables, ctx);
43 
44  // Set the old script
45  Process::ScriptChangeResult res = (cmt.*Property_T::set)(m_oldScript);
46  cmt.programChanged();
47 
48  // We expect the inputs / outputs to revert back to the
49  // exact same state
50  SCORE_ASSERT(m_oldInlets.size() == cmt.inlets().size());
51  SCORE_ASSERT(m_oldOutlets.size() == cmt.outlets().size());
52 
53  // So we can reload their data identically
54  for(std::size_t i = 0; i < m_oldInlets.size(); i++)
55  {
56  cmt.inlets()[i]->loadData(m_oldInlets[i].data);
57  }
58  for(std::size_t i = 0; i < m_oldOutlets.size(); i++)
59  {
60  cmt.outlets()[i]->loadData(m_oldOutlets[i].data);
61  }
62 
63  // Recreate the old cables
64  Dataflow::restoreCables(m_oldCables, ctx);
65  cmt.inletsChanged();
66  cmt.outletsChanged();
67  if constexpr(requires { cmt.isGpu(); })
68  if(cmt.isGpu())
69  cmt.programChanged();
70  }
71 
72  void redo(const score::DocumentContext& ctx) const override
73  {
74  Dataflow::removeCables(m_oldCables, ctx);
75 
76  auto& cmt = m_path.find(ctx);
77  Process::ScriptChangeResult res = (cmt.*Property_T::set)(m_newScript);
78  cmt.programChanged();
79 
80  Dataflow::reloadPortsInNewProcess(m_oldInlets, m_oldOutlets, m_oldCables, cmt, ctx);
81 
82  cmt.inletsChanged();
83  cmt.outletsChanged();
84  if constexpr(requires { cmt.isGpu(); })
85  if(cmt.isGpu())
86  cmt.programChanged();
87  // FIXME if we have it only here, then changing cables fails for the exec nodes
88  // as in the cable loading, in SetupContext::connectCable(Process::Cable& cable)
89  // auto it = outlets.find(port_src); fails because the new outlet hasn't yet been created by the component
90  // but if we have it only above, the JS GPU node fails
91  }
92 
93  void serializeImpl(DataStreamInput& s) const override
94  {
95  s << m_path << m_newScript << m_oldScript << m_oldInlets << m_oldOutlets
96  << m_oldCables;
97  }
98 
99  void deserializeImpl(DataStreamOutput& s) override
100  {
101  s >> m_path >> m_newScript >> m_oldScript >> m_oldInlets >> m_oldOutlets
102  >> m_oldCables;
103  }
104 
105  Path<Process_T> m_path;
106  param_type m_newScript;
107  param_type m_oldScript;
108 
109  std::vector<SavedPort> m_oldInlets, m_oldOutlets;
110 
111  Dataflow::SerializedCables m_oldCables;
112 };
113 }
114 
115 template <>
116 struct is_custom_serialized<Scenario::SavedPort> : std::true_type
117 {
118 };
119 
120 template <>
121 struct TSerializer<DataStream, Scenario::SavedPort>
122 {
123  static void readFrom(DataStream::Serializer& s, const Scenario::SavedPort& tv)
124  {
125  s.stream() << tv.name << tv.type << tv.data;
126  }
127 
128  static void writeTo(DataStream::Deserializer& s, Scenario::SavedPort& tv)
129  {
130  s.stream() >> tv.name >> tv.type >> tv.data;
131  }
132 };
Definition: VisitorInterface.hpp:53
Definition: DataStreamVisitor.hpp:27
Definition: DataStreamVisitor.hpp:202
Definition: ScriptEditCommand.hpp:18
The Command class.
Definition: Command.hpp:34
Main plug-in of score.
Definition: score-plugin-dataflow/Dataflow/PortItem.hpp:14
Base toolkit upon which the software is built.
Definition: Application.cpp:90
Definition: DataStreamHelpers.hpp:99
Definition: DataStreamHelpers.hpp:103
Definition: CableHelpers.hpp:66
Definition: ScriptProcess.hpp:14
Definition: VisitorInterface.hpp:13
Definition: DocumentContext.hpp:18