Loading...
Searching...
No Matches
AutomationDropHandler.cpp
1// This is an open source non-commercial project. Dear PVS-Studio, please check
2// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
3#include "AutomationDropHandler.hpp"
4
5#include <Device/Node/NodeListMimeSerialization.hpp>
6
7#include <Process/ProcessMimeSerialization.hpp>
8
9#include <Scenario/Commands/Cohesion/CreateCurves.hpp>
10#include <Scenario/Commands/CommandAPI.hpp>
11#include <Scenario/Document/ScenarioEditor.hpp>
12#include <Scenario/Commands/Scenario/PasteAnchors.hpp>
13#include <Scenario/Commands/Interval/AddLayerInNewSlot.hpp>
14#include <Scenario/Commands/Interval/AddProcessToInterval.hpp>
15#include <Scenario/Commands/Interval/ResizeInterval.hpp>
16#include <Scenario/Commands/Metadata/ChangeElementName.hpp>
17#include <Scenario/Commands/Scenario/ScenarioPasteElements.hpp>
18#include <Scenario/Document/ScenarioDocument/ScenarioDocumentModel.hpp>
19#include <Scenario/Process/ScenarioModel.hpp>
20#include <Scenario/Process/ScenarioPresenter.hpp>
21
22#include <Dataflow/Commands/CableHelpers.hpp>
23
24#include <score/document/DocumentContext.hpp>
25
26#include <ossia/detail/thread.hpp>
27#include <ossia/network/value/value_traits.hpp>
28
29#include <QFileInfo>
30#include <QUrl>
31namespace Scenario
32{
33
34DropScenario::DropScenario()
35{
36 // TODO give them a mime type ?
37 m_acceptableSuffixes.push_back("scenario");
38}
39
40bool DropScenario::drop(
41 const ScenarioPresenter& pres, QPointF pos, const QMimeData& mime)
42{
43 if(mime.hasUrls())
44 {
45 const auto& doc = pres.context().context;
46 auto& sm = pres.model();
47 auto path = mime.urls().first().toLocalFile();
48 if(QFile f{path}; QFileInfo{f}.suffix() == "scenario" && f.open(QIODevice::ReadOnly))
49 {
50 CommandDispatcher<> d{doc.commandStack};
52 sm, readJson(f.readAll()), pres.toScenarioPoint(pos)));
53 return true;
54 }
55 }
56
57 return false;
58}
59
60DropScoreInScenario::DropScoreInScenario()
61{
62 m_acceptableSuffixes.push_back("score");
63 m_acceptableSuffixes.push_back("scorebin");
64}
65
66bool DropScoreInScenario::drop(
67 const ScenarioPresenter& pres, QPointF pos, const QMimeData& mime)
68{
69 if(mime.hasUrls())
70 {
71 const auto& doc = pres.context().context;
72 auto& sm = pres.model();
73 auto path = mime.urls().first().toLocalFile();
74 if(QFile f{path}; QFileInfo{f}.suffix() == "score" && f.open(QIODevice::ReadOnly))
75 {
76 rapidjson::Document res;
77 res.SetObject();
78
79 auto obj = readJson(f.readAll());
80 auto& docobj = obj["Document"];
81
82 // ScenarioPasteElements expects the cable address to start from the topmost interval in the copied
83 // content
84 res.AddMember("Cables", docobj["Cables"], res.GetAllocator());
85 for(auto& c : res["Cables"].GetArray())
86 {
87 auto source = c["Source"].GetArray();
88 source.Erase(source.Begin());
89 source.Erase(source.Begin());
90 auto sink = c["Sink"].GetArray();
91 sink.Erase(sink.Begin());
92 sink.Erase(sink.Begin());
93 }
94
95 auto& scenar = docobj["BaseScenario"];
96
97 {
98 rapidjson::Value arr(rapidjson::kArrayType);
99 arr.PushBack(scenar["Constraint"], obj.GetAllocator());
100 res.AddMember("Intervals", arr, res.GetAllocator());
101 }
102 {
103 rapidjson::Value arr(rapidjson::kArrayType);
104 arr.PushBack(scenar["StartState"], obj.GetAllocator());
105 arr.PushBack(scenar["EndState"], obj.GetAllocator());
106 res.AddMember("States", arr, res.GetAllocator());
107 }
108 {
109 rapidjson::Value arr(rapidjson::kArrayType);
110 arr.PushBack(scenar["StartEvent"], obj.GetAllocator());
111 arr.PushBack(scenar["EndEvent"], obj.GetAllocator());
112 res.AddMember("Events", arr, res.GetAllocator());
113 }
114 {
115 rapidjson::Value arr(rapidjson::kArrayType);
116 arr.PushBack(scenar["StartTimeNode"], obj.GetAllocator());
117 arr.PushBack(scenar["EndTimeNode"], obj.GetAllocator());
118 res.AddMember("TimeNodes", arr, res.GetAllocator());
119 }
120
121 // Anchors between elements of the file's base scenario move to the pasted ones
122 {
123 auto& alloc = res.GetAllocator();
124 rapidjson::Value origin(rapidjson::kArrayType);
125 auto id = [&](const char* name, int i) {
126 rapidjson::Value v(rapidjson::kObjectType);
127 v.AddMember("ObjectName", rapidjson::StringRef(name), alloc);
128 v.AddMember("ObjectId", i, alloc);
129 origin.PushBack(v, alloc);
130 };
131 id("Scenario::ScenarioDocumentModel", 1);
132 id("Scenario::BaseScenario", 0);
133 res.AddMember("Origin", origin, alloc);
134 }
135
136 CommandDispatcher<> d{doc.commandStack};
138 sm, res, pres.toScenarioPoint(pos)));
139 return true;
140 }
141 }
142
143 return false;
144}
145
146static void getAddressesRecursively(
147 const Device::Node& node, State::Address curAddr,
148 std::vector<Device::FullAddressSettings>& addresses)
149{
150 // TODO refactor with CreateCurves and AddressAccessorEditWidget
151 if(node.is<Device::AddressSettings>())
152 {
153 const Device::AddressSettings& addr = node.get<Device::AddressSettings>();
154 // FIXME see https://github.com/ossia/libossia/issues/291
155 if(ossia::is_numeric(addr.value) || ossia::is_array(addr.value))
156 {
158 static_cast<Device::AddressSettingsCommon&>(as) = addr;
159 as.address = curAddr;
160 addresses.push_back(std::move(as));
161 }
162 // TODO interpolation
163 }
164
165 for(auto& child : node)
166 {
167 const Device::AddressSettings& addr = child.get<Device::AddressSettings>();
168
169 State::Address newAddr{curAddr};
170 newAddr.path.append(addr.name);
171 getAddressesRecursively(child, newAddr, addresses);
172 }
173}
174
175bool AutomationDropHandler::drop(
176 const score::DocumentContext& ctx, const IntervalModel& cst, QPointF p,
177 const QMimeData& mime)
178{
179 // TODO refactor with AddressEditWidget
180 if(mime.hasFormat(score::mime::nodelist()))
181 {
183 Device::FreeNodeList nl = des.deserialize();
184 if(nl.empty())
185 return false;
186
187 std::vector<Device::FullAddressSettings> addresses;
188 for(auto& np : nl)
189 {
190 getAddressesRecursively(np.second, np.first, addresses);
191 }
192
193 if(addresses.empty())
194 return false;
195
196 CreateCurvesFromAddresses({&cst}, addresses, ctx.commandStack);
197
198 return true;
199 }
200 else
201 {
202 return false;
203 }
204}
205
206bool DropScoreInInterval::drop(
207 const score::DocumentContext& doc, const IntervalModel& interval, QPointF p,
208 const QMimeData& mime)
209{
210 if(mime.hasUrls())
211 {
212 auto path = mime.urls().first().toLocalFile();
213 if(QFile f{path}; QFileInfo{f}.suffix() == "score" && f.open(QIODevice::ReadOnly))
214 {
215 auto obj = readJson(f.readAll());
216 auto& docobj = obj["Document"];
217 auto& scenar = docobj["BaseScenario"];
218 auto& itv = scenar["Constraint"];
219
220 Scenario::Command::Macro m{new Command::DropProcessInIntervalMacro, doc};
221 const ObjectPath old_path{
222 {"Scenario::ScenarioDocumentModel", 1},
223 {"Scenario::BaseScenario", 0},
224 {"Scenario::IntervalModel", 0}};
225
226 // Anchors between objects of the file's base interval move to the copies
227 std::vector<std::pair<int32_t, int32_t>> proc_id_map;
228 std::vector<ObjectPath> pasted;
229 {
230 rapidjson::Document copy;
231 copy.SetObject();
232 auto& alloc = copy.GetAllocator();
233 copy.AddMember("Processes", rapidjson::Value(itv["Processes"], alloc), alloc);
234 JSONReader r;
235 r.readFrom(old_path);
236 const auto parent = readJson(r.toByteArray());
237 copy.AddMember("ProcessesParent", rapidjson::Value(parent, alloc), alloc);
238 pasted = loadCopiedProcesses(m, interval, copy, proc_id_map, doc);
239 }
240
241 // Reload cables
242 {
243 auto new_path = score::IDocument::path(interval).unsafePath();
244 auto cables = Dataflow::serializedCablesFromCableJson(
245 old_path, docobj["Cables"].GetArray());
246
247 auto& document
248 = score::IDocument::get<Scenario::ScenarioDocumentModel>(doc.document);
249 for(auto& c : cables)
250 {
251 c.first = getStrongId(document.cables);
252 // The copies may have new identifiers
253 auto remap = [&](ObjectPath& path) {
254 auto& vec = path.vec();
255 if(vec.empty())
256 return;
257 for(auto& [old_id, new_id] : proc_id_map)
258 if(vec.front().id() == old_id)
259 {
260 vec.front() = ObjectIdentifier{vec.front().objectName(), new_id};
261 break;
262 }
263 };
264 remap(c.second.source.unsafePath());
265 remap(c.second.sink.unsafePath());
266 }
267 m.loadCables(new_path, cables);
268 }
269 if(!pasted.empty())
270 m.submit(new Command::FollowPasted{std::move(pasted)});
271
272 // Finally we show the newly created rack
273 m.showRack(interval);
274
275 m.commit();
276
277 return true;
278 }
279 }
280
281 return false;
282}
283
284}
The CommandDispatcher class.
Definition CommandDispatcher.hpp:13
Definition JSONVisitor.hpp:52
void readFrom(const score::Entity< T > &obj)
Definition EntitySerialization.hpp:7
The ObjectIdentifier class.
Definition ObjectIdentifier.hpp:21
The ObjectPath class.
Definition ObjectPath.hpp:37
Definition CommandAPI.hpp:28
Definition ScenarioPasteElements.hpp:26
Main plug-in of score.
Definition score-plugin-dataflow/Dataflow/PortItem.hpp:13
std::vector< ObjectPath > loadCopiedProcesses(Scenario::Command::Macro &m, const Scenario::IntervalModel &interval, const rapidjson::Value &copy, std::vector< std::pair< int32_t, int32_t > > &proc_id_map, const score::DocumentContext &ctx)
Definition ScenarioEditor.cpp:66
Definition AddressSettings.hpp:24
Definition AddressSettings.hpp:49
Definition AddressSettings.hpp:62
Definition MimeVisitor.hpp:9
The Address struct.
Definition Address.hpp:81
Definition DocumentContext.hpp:18