Xcos MVC: implement model.sim and anticipating on future model properties.
[scilab.git] / scilab / modules / scicos / src / cpp / view_scilab / ports_management.hxx
1 /*
2  *  Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3  *  Copyright (C) 2014 - Scilab Enterprises - Paul Bignier
4  *  Copyright (C) 2014 - Scilab Enterprises - Clement DAVID
5  *
6  *  This file must be used under the terms of the CeCILL.
7  *  This source file is licensed as described in the file COPYING, which
8  *  you should have received as part of this distribution.  The terms
9  *  are also available at
10  *  http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt
11  *
12  */
13
14 #ifndef PORTS_MANAGEMENT_HXX_
15 #define PORTS_MANAGEMENT_HXX_
16
17 #include <string>
18 #include <vector>
19 #include <algorithm>
20
21 #include "internal.hxx"
22 #include "double.hxx"
23 #include "string.hxx"
24
25 #include "Controller.hxx"
26
27 namespace org_scilab_modules_scicos
28 {
29 namespace view_scilab
30 {
31
32 /*
33  * Return a Scilab encoded value for a property.
34  */
35 template<typename Adaptor>
36 types::InternalType* get_ports_property(const Adaptor& adaptor, object_properties_t port_kind, const Controller& controller, object_properties_t p)
37 {
38     static const wchar_t E[] = L"E";
39     static const wchar_t I[] = L"I";
40     model::Block* adaptee = adaptor.getAdaptee();
41
42     // Retrieve the identifiers
43     std::vector<ScicosID> ids;
44     controller.getObjectProperty(adaptee->id(), adaptee->kind(), port_kind, ids);
45
46     // Translate identifiers to return values
47     int i = 0;
48     switch (p)
49     {
50         case CONNECTED_SIGNALS:
51         {
52             double* v;
53             types::Double* o = new types::Double(ids.size(), 1, &v);
54
55             ScicosID diagram;
56             controller.getObjectProperty(adaptee->id(), adaptee->kind(), PARENT_DIAGRAM, diagram);
57
58             std::vector<ScicosID> children;
59             controller.getObjectProperty(diagram, DIAGRAM, CHILDREN, children);
60
61             for (std::vector<ScicosID>::iterator it = ids.begin(); it != ids.end(); ++it, ++i)
62             {
63                 ScicosID id;
64                 controller.getObjectProperty(*it, PORT, p, id);
65
66                 std::vector<ScicosID>::iterator found = std::find(children.begin(), children.end(), id);
67
68                 if (found != children.end())
69                 {
70                     v[i] = std::distance(found, children.begin());
71                 }
72                 else
73                 {
74                     v[i] = 0;
75                 }
76             }
77             return o;
78         }
79         case STYLE:
80         case LABEL:
81         {
82             types::String* o = new types::String(ids.size(), 1);
83             for (std::vector<ScicosID>::iterator it = ids.begin(); it != ids.end(); ++it, ++i)
84             {
85                 std::string s;
86                 controller.getObjectProperty(*it, PORT, p, s);
87                 o->set(i, s.data());
88             }
89             return o;
90         }
91         case IMPLICIT:
92         {
93             types::String* o = new types::String(ids.size(), 1);
94             for (std::vector<ScicosID>::iterator it = ids.begin(); it != ids.end(); ++it, ++i)
95             {
96                 bool v;
97                 controller.getObjectProperty(*it, PORT, p, v);
98                 o->set(i, (v == false) ? E : I);
99             }
100             return o;
101         }
102         case DATATYPE_ROWS:
103         case DATATYPE_COLS:
104         case DATATYPE_TYPE:
105         {
106             types::Double* o = new types::Double(ids.size(), 1);
107             for (std::vector<ScicosID>::iterator it = ids.begin(); it != ids.end(); ++it, ++i)
108             {
109                 double v;
110                 controller.getObjectProperty(*it, PORT, p, v);
111                 o[i] = v;
112             }
113             return o;
114         }
115         default:
116             return 0;
117     }
118 }
119
120 /*
121  * Set a Scilab encoded values as a property.
122  *
123  * \note this method will return false if one of the ports does not exist
124  */
125 template<typename Adaptor>
126 bool set_ports_property(const Adaptor& adaptor, object_properties_t port_kind, Controller& controller, object_properties_t p, types::InternalType* v)
127 {
128     static const wchar_t E[] = L"E";
129     static const wchar_t I[] = L"I";
130     model::Block* adaptee = adaptor.getAdaptee();
131
132     // Retrieve the ports identifiers
133     std::vector<ScicosID> ids;
134     controller.getObjectProperty(adaptee->id(), adaptee->kind(), port_kind, ids);
135
136     if (v->getType() == types::InternalType::ScilabString)
137     {
138         types::String* current = v->getAs<types::String>();
139         if (current->getCols() != 0 && current->getCols() != 1)
140         {
141             return false;
142         }
143
144         size_t rows = current->getRows();
145         if (rows != ids.size())
146         {
147             return false;
148         }
149
150         int i = 0;
151         switch (p)
152         {
153             case IMPLICIT:
154             {
155                 for (std::vector<ScicosID>::iterator it = ids.begin(); it != ids.end(); ++it, ++i)
156                 {
157                     if (current->get(i) == I)
158                     {
159                         controller.setObjectProperty(*it, PORT, p, true);
160                     }
161                     else if (current->get(i) == E)
162                     {
163                         controller.setObjectProperty(*it, PORT, p, false);
164                     }
165                     else
166                     {
167                         return false;
168                     }
169                 }
170                 return true;
171             }
172             case STYLE:
173             case LABEL:
174             {
175                 std::vector<std::string> style = std::vector<std::string>(current->getSize());
176                 for (std::vector<ScicosID>::iterator it = ids.begin(); it != ids.end(); ++it, ++i)
177                 {
178                     char* c_str = wide_string_to_UTF8(current->get(i));
179                     style[i] = std::string(c_str);
180                     FREE(c_str);
181                     controller.setObjectProperty(*it, PORT, p, style[i]);
182                 }
183                 return true;
184             }
185             default:
186                 return false;
187         }
188     }
189     else if (v->getType() == types::InternalType::ScilabDouble)
190     {
191         types::Double* current = v->getAs<types::Double>();
192         if (current->getRows() != 0 || current->getCols() != 0)
193         {
194             return false;
195         }
196         if (ids.size() != 0)
197         {
198             return false;
199         }
200         // Do nothing, because if the sizes match, then there are already zero concerned ports, so no ports to update
201         return true;
202     }
203     return false;
204 }
205
206 /**
207  * Update the ports with a specific property.
208  *
209  * Create ports if needed, remove ports if needed and set a default property on each port.
210  */
211 template<typename Adaptor>
212 bool update_ports_with_property(const Adaptor& adaptor, object_properties_t port_kind, Controller& controller, object_properties_t p, types::InternalType* v)
213 {
214     model::Block* adaptee = adaptor.getAdaptee();
215
216     if (v->getType() != types::InternalType::ScilabDouble)
217     {
218         return false;
219     }
220     types::Double* value = v->getAs<types::Double>();
221
222     ScicosID parentDiagram;
223     controller.getObjectProperty(adaptee->id(), BLOCK, PARENT_DIAGRAM, parentDiagram);
224
225     std::vector<ScicosID> children;
226     if (parentDiagram != 0)
227     {
228         controller.getObjectProperty(parentDiagram, DIAGRAM, CHILDREN, children);
229     }
230
231     std::vector<int> newPorts = std::vector<int>(value->getSize());
232
233     // retrieve old data
234     std::vector<ScicosID> oldPorts;
235     controller.getObjectProperty(adaptee->id(), adaptee->kind(), port_kind, oldPorts);
236     std::vector<ScicosID> previousPorts = oldPorts;
237
238     double* d = value->getReal();
239     if (p == CONNECTED_SIGNALS)
240     {
241         for (std::vector<int>::iterator it = newPorts.begin(); it != newPorts.end(); ++it, ++d)
242         {
243             if (0 > *d && *d >= children.size())
244             {
245                 return false;
246             }
247
248             *it = (int) * d;
249         }
250     }
251     else
252     {
253         // Here, we are modyfing either 'in', 'in2' or 'intyp', so the Port ids must remain the same as much as possible (copy the old ones),
254         // and set the new ones to zero (unconnected) if there are any (insert them at the beginning of newPorts).
255         if (newPorts.size() != 0)
256         {
257             //
258             if (newPorts.size() >= oldPorts.size())
259             {
260                 std::fill(newPorts.begin(), newPorts.begin() + newPorts.size() - oldPorts.size(), 0);
261                 std::copy(oldPorts.begin(), oldPorts.end(), newPorts.begin() + newPorts.size() - oldPorts.size() + 1);
262             }
263             else if (newPorts.size() < oldPorts.size())
264             {
265                 std::copy(oldPorts.begin(), oldPorts.begin() + newPorts.size(), newPorts.begin());
266             }
267         }
268     }
269     std::vector<ScicosID> deletedObjects;
270
271     // updated ports
272     while (!oldPorts.empty() && !newPorts.empty())
273     {
274         ScicosID oldPort = oldPorts.back();
275         oldPorts.pop_back();
276         int newPort = newPorts.back();
277         newPorts.pop_back();
278
279         ScicosID oldSignal;
280         controller.getObjectProperty(oldPort, PORT, CONNECTED_SIGNALS, oldSignal);
281         ScicosID newSignal = children[newPort];
282
283         if (p == CONNECTED_SIGNALS)
284         {
285             if (oldSignal != newSignal)
286             {
287                 // disconnect the old link
288                 ScicosID oldSignalSrc;
289                 controller.getObjectProperty(oldSignal, LINK, SOURCE_PORT, oldSignalSrc);
290                 ScicosID oldSignalDst;
291                 controller.getObjectProperty(oldSignal, LINK, DESTINATION_PORT, oldSignalDst);
292
293                 ScicosID unconnected = 0;
294                 if (oldSignalSrc == oldPort)
295                 {
296                     controller.setObjectProperty(oldSignalDst, PORT, CONNECTED_SIGNALS, unconnected);
297                 }
298                 else // oldSignalDst == oldPort
299                 {
300                     controller.setObjectProperty(oldSignalSrc, PORT, CONNECTED_SIGNALS, unconnected);
301                 }
302                 // Link de-association is not performed as the link will be removed
303
304                 // connect the new link
305                 controller.setObjectProperty(newSignal, LINK, SOURCE_PORT, 0);
306                 controller.setObjectProperty(oldPort, PORT, CONNECTED_SIGNALS, newSignal);
307
308                 children.erase(std::find(children.begin(), children.end(), oldSignal));
309                 deletedObjects.push_back(oldSignal);
310             }
311         }
312         else
313         {
314             // The common port ids (CONNECTED_SIGNALS property) remain the same, so just update the p property
315             controller.setObjectProperty(oldPort, PORT, p, d[newPorts.size() - 1]);
316         }
317     }
318
319     // removed ports
320     if (!oldPorts.empty())
321     {
322         previousPorts.erase(previousPorts.begin() + oldPorts.size(), previousPorts.end());
323
324         while (!oldPorts.empty())
325         {
326             ScicosID oldPort = oldPorts.back();
327             oldPorts.pop_back();
328
329             ScicosID signal;
330             controller.getObjectProperty(oldPort, PORT, CONNECTED_SIGNALS, signal);
331             if (signal != 0)
332             {
333                 // the link is connected, disconnect the other side
334                 ScicosID oldSignalSrc;
335                 controller.getObjectProperty(signal, LINK, SOURCE_PORT, oldSignalSrc);
336                 ScicosID oldSignalDst;
337                 controller.getObjectProperty(signal, LINK, DESTINATION_PORT, oldSignalDst);
338
339                 ScicosID unconnected = 0;
340                 if (oldSignalSrc == oldPort)
341                 {
342                     controller.setObjectProperty(oldSignalDst, PORT, CONNECTED_SIGNALS, unconnected);
343                 }
344                 else     // oldSignalDst == oldPort
345                 {
346                     controller.setObjectProperty(oldSignalSrc, PORT, CONNECTED_SIGNALS, unconnected);
347                 }
348
349                 children.erase(std::find(children.begin(), children.end(), signal));
350                 deletedObjects.push_back(signal);
351             }
352
353             deletedObjects.push_back(oldPort);
354         }
355
356         controller.setObjectProperty(adaptee->id(), BLOCK, port_kind, previousPorts);
357     }
358
359     // added ports
360     if (!newPorts.empty())
361     {
362         while (!newPorts.empty())
363         {
364             int newPort = newPorts.back();
365             oldPorts.pop_back();
366
367             ScicosID id = controller.createObject(PORT);
368             controller.setObjectProperty(id, PORT, SOURCE_BLOCK, adaptee->id());
369             if (p != CONNECTED_SIGNALS)
370             {
371                 // In addition to setting the signal to 0 (the 0s at the start of newPorts), set the requested property
372                 controller.setObjectProperty(id, PORT, p, d[newPorts.size() - 1]);
373             }
374             // set the connected signal if applicable
375             if (newPort != 0)
376             {
377                 ScicosID signal = children[newPort];
378                 controller.setObjectProperty(id, PORT, CONNECTED_SIGNALS, signal);
379             }
380
381             previousPorts.push_back(id);
382         }
383
384         controller.setObjectProperty(adaptee->id(), BLOCK, port_kind, previousPorts);
385     }
386
387     // remove objects from the model after de-association
388     if (parentDiagram != 0)
389     {
390         controller.setObjectProperty(parentDiagram, DIAGRAM, CHILDREN, children);
391     }
392     for (std::vector<ScicosID>::iterator it = deletedObjects.begin(); it != deletedObjects.end(); ++it)
393     {
394         controller.deleteObject(*it);
395     }
396
397     return true;
398 }
399
400
401 } /* view_scilab */
402 } /* namespace org_scilab_modules_scicos */
403
404 #endif /* PORTS_MANAGEMENT_HXX_ */