Loading...
Searching...
No Matches
DropOnCable.hpp
1#pragma once
2
3#include <Process/Commands/EditPort.hpp>
4#include <Process/Commands/LoadPresetCommandFactory.hpp>
5#include <Process/Dataflow/CableItem.hpp>
6#include <Process/Dataflow/NodeItem.hpp>
7#include <Process/Drop/ProcessDropHandler.hpp>
8#include <Process/Process.hpp>
9#include <Process/ProcessContext.hpp>
10
11#include <Scenario/Commands/CommandAPI.hpp>
12#include <Scenario/Commands/Interval/AddProcessToInterval.hpp>
13#include <Scenario/Document/Interval/IntervalModel.hpp>
14#include <Scenario/Document/ScenarioDocument/ProcessCreation.hpp>
15
16#include <score/application/GUIApplicationContext.hpp>
17#include <score/command/Dispatchers/CommandDispatcher.hpp>
18#include <score/command/Dispatchers/RuntimeDispatcher.hpp>
19#include <score/document/DocumentContext.hpp>
20namespace Scenario
21{
22class DropOnCable : public QObject
23{
24 // Only things that make sense to drop: processes and presets (maybe layers?)
25 // Find in which interval we wish to drop.
26 // If both ends are in the same hierarchy level we drop the process in the interval
27
28public:
29 const ScenarioDocumentModel& sm;
30 const Process::Context& m_context;
31 const Dataflow::CableItem& item;
32 const Process::Cable& cable = item.model();
33 const Process::Port& source = cable.source().find(m_context);
34 const Process::Port& sink = cable.sink().find(m_context);
35
36 Scenario::IntervalModel* m_interval{};
37
39 const Dataflow::CableItem& item, const ScenarioDocumentModel& sm,
40 const Process::Context& m_context)
41 : sm{sm}
42 , m_context{m_context}
43 , item{item}
44 {
45 }
46
47 void createPreset(QPointF pos, const QByteArray& presetData)
48 {
49 auto& procs = m_context.app.interfaces<Process::ProcessFactoryList>();
50 if(auto preset = Process::Preset::fromJson(procs, presetData))
51 {
52 Scenario::loadPresetInCable(m_context, sm, *preset, cable);
53 }
54 }
55
56 void createProcess(QPointF pos, const Process::ProcessDropHandler::ProcessDrop& proc)
57 {
58 Scenario::createProcessInCable(
59 m_context, sm, proc.creation, proc.duration, proc.setup, cable);
60 }
61
62 void drop(const QPointF& pos, const QMimeData& mime)
63 {
64 // FIXME put it closer to the source or sink depending on drop position
65 auto source_itv = Scenario::closestParentInterval(&source);
66 //auto sink_itv = Scenario::closestParentInterval(&sink);
67 if(!source_itv)
68 return;
69
70 m_interval = source_itv;
71
72 // Something tricky here is that we have to separate mime data processing and command execution:
73 // mime data processing has to happen synchronously as it gets deleted after the CableItem::dropEvent function returns.
74 // On the other hand, we cannot run the command synchronously as it may delete the cable, which causes
75 // the cableitem to be deleted too and thus it cannot finish the dropEvent without crashing.
76 const auto& handlers = m_context.app.interfaces<Process::ProcessDropHandlerList>();
77
78 if(mime.hasFormat(score::mime::layerdata()))
79 {
80 // TODO
81 }
82 else if(mime.hasFormat(score::mime::processpreset()))
83 {
84 const auto presetData = mime.data(score::mime::processpreset());
85
86 QMetaObject::invokeMethod(this, [pos, presetData, this]() {
87 createPreset(pos, presetData);
88 }, Qt::QueuedConnection);
89 }
90 else if(auto res = handlers.getDrop(mime, m_context); !res.empty())
91 {
92 if(res.size() >= 1)
93 {
94 QMetaObject::invokeMethod(this, [pos, proc = res.front(), this]() {
95 createProcess(pos, proc);
96 }, Qt::QueuedConnection);
97 }
98 }
99 else if(mime.hasUrls())
100 {
101 // TODO
102 }
103 }
104};
105
106class DropOnNode : public QObject
107{
108public:
109 const ScenarioDocumentModel& sm;
110 const Process::Context& m_context;
111 const Process::NodeItem& item;
112
113 Scenario::IntervalModel* m_interval{};
114
116 const Process::NodeItem& item, const ScenarioDocumentModel& sm,
117 const Process::Context& m_context)
118 : sm{sm}
119 , m_context{m_context}
120 , item{item}
121 {
122 }
123
124 void createPreset(const QByteArray& presetData)
125 {
126 auto& old = item.model();
127 auto& procs = m_context.app.interfaces<Process::ProcessFactoryList>();
128 if(auto preset = Process::Preset::fromJson(procs, presetData))
129 {
130 if(preset->key.key == old.concreteKey())
131 {
132 auto& load_preset_ifaces
133 = m_context.app.interfaces<Process::LoadPresetCommandFactoryList>();
134
135 auto cmd = load_preset_ifaces.make(
136 &Process::LoadPresetCommandFactory::make, old, *preset, m_context);
137 CommandDispatcher<> disp{m_context.commandStack};
138 disp.submit(cmd);
139 }
140 else
141 {
145 if(auto p = m.loadProcessFromPreset(*m_interval, *preset, old.position()))
146 {
147 linkNewProcess(p, m);
148 m.removeProcess(*m_interval, old.id());
149 m.commit();
150 }
151 }
152 }
153 }
154
155 void createProcess(const Process::ProcessDropHandler::ProcessDrop& proc)
156 {
157 auto& old = item.model();
161 if(auto p = m.createProcessInNewSlot(*m_interval, proc.creation, old.position()))
162 {
163 if(proc.setup)
164 proc.setup(*p, disp);
165
166 // Give the same connections that the previous process had
167 linkNewProcess(p, m);
168
169 // Remove the previous process
170 m.removeProcess(*m_interval, old.id());
171
172 m.commit();
173 }
174 }
175
176 void linkNewProcess(Process::ProcessModel* p, Scenario::Command::Macro& m)
177 {
178 auto& old = item.model();
179 if(p->inlets().size() > 0)
180 {
181 const auto dst = p->inlets()[0];
182 const auto type = dst->type();
183
184 if(old.inlets().size() > 0)
185 {
186 auto& old_dst = old.inlets()[0];
187 if(old_dst->type() == type && !qobject_cast<Process::ControlInlet*>(dst)
188 && !qobject_cast<Process::ControlInlet*>(old_dst))
189 {
190 for(auto& edge : old.inlets()[0]->cables())
191 {
192 auto& cable = edge.find(m_context);
193 auto& src = cable.source().find(m_context);
194 if(src.type() == type)
195 {
196 m.createCable(sm, src, *dst);
197 }
198 }
199 m.setProperty<Process::Port::p_address>(*dst, old_dst->address());
200 }
201 }
202 }
203
204 if(p->outlets().size() > 0)
205 {
206 const auto src = p->outlets()[0];
207 const auto type = src->type();
208
209 if(old.outlets().size() > 0)
210 {
211 auto& old_src = old.outlets()[0];
212 if(old_src->type() == type && !qobject_cast<Process::ControlInlet*>(src)
213 && !qobject_cast<Process::ControlInlet*>(old_src))
214 {
215 for(auto& edge : old.outlets()[0]->cables())
216 {
217 auto& cable = edge.find(m_context);
218 auto& dst = cable.sink().find(m_context);
219 if(dst.type() == type)
220 {
221 m.createCable(sm, *src, dst);
222 }
223 }
224 m.setProperty<Process::Port::p_address>(*src, old_src->address());
225 if(type == Process::PortType::Audio)
226 {
227 auto old_audio_src = safe_cast<Process::AudioOutlet*>(old_src);
228 auto audio_src = safe_cast<Process::AudioOutlet*>(src);
229 m.setProperty<Process::AudioOutlet::p_propagate>(
230 *audio_src, old_audio_src->propagate());
231 }
232
233 src->setAddress(old_src->address());
234 }
235 }
236 }
237 }
238
239 void drop(const QMimeData& mime)
240 {
241 // FIXME drop in nodal vs drop in scenario
242 auto& model = item.model();
243 m_interval = qobject_cast<Scenario::IntervalModel*>(model.parent());
244 if(!m_interval)
245 return;
246
247 // Something tricky here is that we have to separate mime data processing and command execution:
248 // mime data processing has to happen synchronously as it gets deleted after the NodeItem::dropEvent function returns.
249 // On the other hand, we cannot run the command synchronously as it may delete the cable, which causes
250 // the cableitem to be deleted too and thus it cannot finish the dropEvent without crashing.
251 const auto& handlers = m_context.app.interfaces<Process::ProcessDropHandlerList>();
252
253 if(mime.hasFormat(score::mime::layerdata()))
254 {
255 // TODO
256 }
257 else if(mime.hasFormat(score::mime::processpreset()))
258 {
259 const auto presetData = mime.data(score::mime::processpreset());
260 QMetaObject::invokeMethod(this, [presetData, this]() {
261 createPreset(presetData);
262 }, Qt::QueuedConnection);
263 }
264 else if(auto res = handlers.getDrop(mime, m_context); !res.empty())
265 {
266 if(res.size() >= 1)
267 {
268 QMetaObject::invokeMethod(this, [proc = res.front(), this]() {
269 createProcess(proc);
270 }, Qt::QueuedConnection);
271 }
272 }
273 else if(mime.hasUrls())
274 {
275 // TODO
276 }
277 }
278};
279}
The CommandDispatcher class.
Definition CommandDispatcher.hpp:13
Definition CableItem.hpp:37
Definition Cable.hpp:38
Definition LoadPresetCommandFactory.hpp:32
Definition NodeItem.hpp:39
Definition Port.hpp:102
Definition ProcessDropHandler.hpp:75
Definition ProcessList.hpp:10
The Process class.
Definition score-lib-process/Process/Process.hpp:61
Definition AddProcessToInterval.hpp:72
Definition CommandAPI.hpp:28
Definition DropOnCable.hpp:23
Definition DropOnCable.hpp:107
Definition IntervalModel.hpp:50
Definition ScenarioDocumentModel.hpp:29
auto make(Fun f, Args &&... args) const noexcept
Apply a function on the correct factory according to a set of parameter.
Definition InterfaceList.hpp:169
Main plug-in of score.
Definition score-plugin-dataflow/Dataflow/PortItem.hpp:13
Definition ProcessContext.hpp:12
Definition ProcessDropHandler.hpp:29
Definition RuntimeDispatcher.hpp:14