Loading...
Searching...
No Matches
LoadPresetCommand.hpp
1#pragma once
2#include <State/Value.hpp>
3#include <State/ValueSerialization.hpp>
4
5#include <Process/Commands/LoadPresetCommandFactory.hpp>
6#include <Process/Dataflow/Port.hpp>
7#include <Process/Process.hpp>
8
9#include <Scenario/Commands/ScenarioCommandFactory.hpp>
10
11#include <Dataflow/Commands/CableHelpers.hpp>
12
13#include <score/model/path/PathSerialization.hpp>
14#include <score/tools/Debug.hpp>
15
16#include <ossia/detail/algorithms.hpp>
17
18#include <QDebug>
19
20#include <algorithm>
21#include <vector>
22
23#include <score_plugin_scenario_export.h>
24
25namespace Scenario::Command
26{
29{
30 std::vector<std::pair<int, Process::PortType>> inlets, outlets;
31 bool operator==(const PortSignature&) const noexcept = default;
32
35 bool keeps(const PortSignature& before) const noexcept
36 {
37 const auto prefix = [](const auto& now, const auto& old) {
38 return now.size() >= old.size() && std::equal(old.begin(), old.end(), now.begin());
39 };
40 return prefix(inlets, before.inlets) && prefix(outlets, before.outlets);
41 }
42};
43
44inline PortSignature portSignature(const Process::ProcessModel& proc)
45{
46 PortSignature sig;
47 for(auto* p : proc.inlets())
48 sig.inlets.emplace_back(p->id().val(), p->type());
49 for(auto* p : proc.outlets())
50 sig.outlets.emplace_back(p->id().val(), p->type());
51 return sig;
52}
53
56class LoadPreset final : public score::Command
57{
58 SCORE_COMMAND_DECL(
59 Scenario::Command::CommandFactoryName(), LoadPreset, "Load a preset")
60
61public:
63 : m_path{obj}
64 , m_old{obj.savePreset()}
65 , m_new{std::move(newval)}
66 {
67 }
68
69private:
70 void undo(const score::DocumentContext& ctx) const final override
71 {
72 load(m_path.find(ctx), m_old);
73 }
74
75 void redo(const score::DocumentContext& ctx) const final override
76 {
77 load(m_path.find(ctx), m_new);
78 }
79
80 static void load(Process::ProcessModel& proc, const Process::Preset& p)
81 {
82 const auto before = portSignature(proc);
83 proc.loadPreset(p);
84 if(!portSignature(proc).keeps(before))
85 {
86 // Its cables may now be on ports of another type
87 qWarning() << "Loading a preset changed the ports of" << proc.prettyName()
88 << ": its presetChangesPorts() must say so";
89 SCORE_SOFT_ASSERT(false);
90 }
91 }
92
93 void serializeImpl(DataStreamInput& stream) const final override
94 {
95 stream << m_path << m_old << m_new;
96 }
97 void deserializeImpl(DataStreamOutput& stream) final override
98 {
99 stream >> m_path >> m_old >> m_new;
100 }
101
103 Process::Preset m_old;
104 Process::Preset m_new;
105};
106
107// Note: see ScriptEditCommand.hpp which is very similar!
108class SCORE_PLUGIN_SCENARIO_TEST_EXPORT LoadPresetWithCablesBackup final : public score::Command
109{
110 SCORE_COMMAND_DECL(
111 Scenario::Command::CommandFactoryName(), LoadPresetWithCablesBackup,
112 "Load a preset")
113
114public:
116 const Process::ProcessModel& model, Process::Preset newval,
117 const score::DocumentContext& ctx)
118 : m_path{model}
119 , m_old{model.savePreset()}
120 , m_new{std::move(newval)}
121 {
122 m_oldCables
123 = Dataflow::saveCables({const_cast<Process::ProcessModel*>(&model)}, ctx);
124
125 for(auto& port : model.inlets())
126 m_oldInlets.emplace_back(
127 Dataflow::SavedPort{port->name(), port->type(), port->saveData()});
128 for(auto& port : model.outlets())
129 m_oldOutlets.emplace_back(
130 Dataflow::SavedPort{port->name(), port->type(), port->saveData()});
131 }
132
133private:
134 void undo(const score::DocumentContext& ctx) const final override
135 {
136 auto& cmt = m_path.find(ctx);
137 // Remove all the cables that could have been added during
138 // the creation
139 Dataflow::removeCables(m_oldCables, ctx);
140
141 // Set the old preset
142 cmt.loadPreset(m_old);
143
144 // We expect the inputs / outputs to revert back to the
145 // exact same state
146 SCORE_ASSERT(m_oldInlets.size() == cmt.inlets().size());
147 SCORE_ASSERT(m_oldOutlets.size() == cmt.outlets().size());
148
149 // So we can reload their data identically
150 for(std::size_t i = 0; i < m_oldInlets.size(); i++)
151 {
152 cmt.inlets()[i]->loadData(m_oldInlets[i].data);
153 }
154 for(std::size_t i = 0; i < m_oldOutlets.size(); i++)
155 {
156 cmt.outlets()[i]->loadData(m_oldOutlets[i].data);
157 }
158
159 // Recreate the old cables; the ports at the other end lost them too
160 auto cables = Dataflow::restoreCablesWithoutTouchingPorts(m_oldCables, ctx);
161 Dataflow::reattachCablesToPorts(cables, ctx);
162 cmt.inletsChanged();
163 cmt.outletsChanged();
164 Dataflow::notifyAddedCables(cables, ctx);
165 }
166
167 void redo(const score::DocumentContext& ctx) const final override
168 {
169 Dataflow::removeCables(m_oldCables, ctx);
170
171 auto& cmt = m_path.find(ctx);
172 std::vector<int> inletIds, outletIds;
173 inletIds.reserve(cmt.inlets().size());
174 outletIds.reserve(cmt.outlets().size());
175 for(auto* port : cmt.inlets())
176 inletIds.push_back(port->id().val());
177 for(auto* port : cmt.outlets())
178 outletIds.push_back(port->id().val());
179 cmt.loadPreset(m_new);
180
181 // Cables and addresses only: the values are the preset's now, and a
182 // controller given its old value back would resize the ports again.
183 auto cables = Dataflow::reloadPortsInNewProcess(
184 m_oldInlets, m_oldOutlets, m_oldCables, cmt, Process::PortLoadDataFlags{}, ctx,
185 inletIds, outletIds);
186
187 cmt.inletsChanged();
188 cmt.outletsChanged();
189 Dataflow::notifyAddedCables(cables, ctx);
190 }
191
192 void serializeImpl(DataStreamInput& stream) const final override
193 {
194 stream << m_path << m_old << m_new << m_oldInlets << m_oldOutlets << m_oldCables;
195 }
196 void deserializeImpl(DataStreamOutput& stream) final override
197 {
198 stream >> m_path >> m_old >> m_new >> m_oldInlets >> m_oldOutlets >> m_oldCables;
199 }
200
202 Process::Preset m_old;
203 Process::Preset m_new;
204
205 std::vector<Dataflow::SavedPort> m_oldInlets, m_oldOutlets;
206
207 Dataflow::SerializedCables m_oldCables;
208};
209
211{
212 SCORE_CONCRETE("b04a9d8a-1374-4223-a258-0f90e8e0aef6")
213public:
214 bool matches(
216 const score::DocumentContext& ctx) const noexcept override
217 {
218 return true;
219 }
220 score::Command* make(
221 const Process::ProcessModel& obj, Process::Preset newval,
222 const score::DocumentContext& ctx) const override
223 {
224 // Cables are taken off and matched again only around a load that can
225 // move the ports they are on
226 if(obj.presetChangesPorts(newval))
227 return new LoadPresetWithCablesBackup{obj, std::move(newval), ctx};
228 else
229 return new LoadPreset{obj, std::move(newval)};
230 }
231};
232}
The Path class is a typesafe wrapper around ObjectPath.
Definition Path.hpp:52
Definition LoadPresetCommandFactory.hpp:19
The Process class.
Definition score-lib-process/Process/Process.hpp:82
virtual bool presetChangesPorts(const Preset &preset) const noexcept
Definition score-lib-process/Process/Process.cpp:264
Definition LoadPresetCommand.hpp:211
Definition LoadPresetCommand.hpp:57
Definition LoadPresetCommand.hpp:109
The Command class.
Definition Command.hpp:34
Definition DataStreamHelpers.hpp:99
Definition DataStreamHelpers.hpp:103
Definition CableHelpers.hpp:102
Definition Preset.hpp:32
The ids and types of a process's inlets and outlets, in order.
Definition LoadPresetCommand.hpp:29
bool keeps(const PortSignature &before) const noexcept
Definition LoadPresetCommand.hpp:35
Definition ObjectMatches.hpp:6
Definition DocumentContext.hpp:18
Definition Unused.hpp:3