Xcos MVC: implement from / to
[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.1-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, object_properties_t p>
36 types::InternalType* get_ports_property(const Adaptor& adaptor, object_properties_t port_kind, const Controller& controller)
37 {
38     model::Block* adaptee = adaptor.getAdaptee();
39
40     // Retrieve the identifiers
41     std::vector<ScicosID> ids;
42     controller.getObjectProperty(adaptee->id(), adaptee->kind(), port_kind, ids);
43
44     // Translate identifiers: shared variables
45     int i = 0;
46     size_t datatypeIndex = -1;
47     // Translate identifiers to return values
48     switch (p)
49     {
50         case STYLE:
51         case LABEL:
52         {
53             types::String* o = new types::String(ids.size(), 1);
54             for (std::vector<ScicosID>::iterator it = ids.begin(); it != ids.end(); ++it, ++i)
55             {
56                 std::string s;
57                 controller.getObjectProperty(*it, PORT, p, s);
58                 o->set(i, s.data());
59             }
60             return o;
61         }
62         case DATATYPE_TYPE:
63             datatypeIndex++;
64             // no break
65         case DATATYPE_COLS:
66             datatypeIndex++;
67             // no break
68         case DATATYPE_ROWS:
69         {
70             datatypeIndex++;
71             double* data;
72             types::Double* o = new types::Double(ids.size(), 1, &data);
73             for (std::vector<ScicosID>::iterator it = ids.begin(); it != ids.end(); ++it, ++i)
74             {
75                 std::vector<int> v;
76                 controller.getObjectProperty(*it, PORT, DATATYPE, v);
77                 data[i] = v[datatypeIndex];
78             }
79             return o;
80         }
81         case IMPLICIT:
82         {
83             static const wchar_t E[] = L"E";
84             static const wchar_t I[] = L"I";
85             types::String* o = new types::String(ids.size(), 1);
86             for (std::vector<ScicosID>::iterator it = ids.begin(); it != ids.end(); ++it, ++i)
87             {
88                 bool v;
89                 controller.getObjectProperty(*it, PORT, p, v);
90                 o->set(i, (v == false) ? E : I);
91             }
92             return o;
93         }
94         case CONNECTED_SIGNALS:
95         {
96             double* v;
97             types::Double* o = new types::Double(ids.size(), 1, &v);
98
99             ScicosID diagram;
100             controller.getObjectProperty(adaptee->id(), adaptee->kind(), PARENT_DIAGRAM, diagram);
101
102             std::vector<ScicosID> children;
103             if (diagram != 0)
104             {
105                 controller.getObjectProperty(diagram, DIAGRAM, CHILDREN, children);
106             }
107
108             for (std::vector<ScicosID>::iterator it = ids.begin(); it != ids.end(); ++it, ++i)
109             {
110                 ScicosID id;
111                 controller.getObjectProperty(*it, PORT, p, id);
112
113                 std::vector<ScicosID>::iterator found = std::find(children.begin(), children.end(), id);
114
115                 if (found != children.end())
116                 {
117                     v[i] = std::distance(found, children.begin());
118                 }
119                 else
120                 {
121                     v[i] = 0;
122                 }
123             }
124             return o;
125         }
126         default:
127             return 0;
128     }
129 }
130
131 /*
132  * Set a Scilab encoded values as a property.
133  *
134  * \note this method will return false if one of the ports does not exist
135  */
136 template<typename Adaptor, object_properties_t p>
137 bool set_ports_property(const Adaptor& adaptor, object_properties_t port_kind, Controller& controller, types::InternalType* v)
138 {
139     model::Block* adaptee = adaptor.getAdaptee();
140
141     // Retrieve the ports identifiers
142     std::vector<ScicosID> ids;
143     controller.getObjectProperty(adaptee->id(), adaptee->kind(), port_kind, ids);
144
145     if (v->getType() == types::InternalType::ScilabString)
146     {
147         types::String* current = v->getAs<types::String>();
148         if (current->getCols() != 0 && current->getCols() != 1)
149         {
150             return false;
151         }
152
153         size_t rows = current->getRows();
154         if (rows != ids.size())
155         {
156             return false;
157         }
158
159         // Translate identifiers: shared variables
160         int i = 0;
161         // Translate identifiers from values
162         switch (p)
163         {
164             case STYLE:
165             case LABEL:
166             {
167                 for (std::vector<ScicosID>::iterator it = ids.begin(); it != ids.end(); ++it, ++i)
168                 {
169                     char* c_str = wide_string_to_UTF8(current->get(i));
170                     controller.setObjectProperty(*it, PORT, p, std::string(c_str));
171                     FREE(c_str);
172                 }
173                 return true;
174             }
175             case IMPLICIT:
176             {
177                 static const std::wstring E = L"E";
178                 static const std::wstring I = L"I";
179
180                 for (std::vector<ScicosID>::iterator it = ids.begin(); it != ids.end(); ++it, ++i)
181                 {
182                     if (current->get(i) == I)
183                     {
184                         controller.setObjectProperty(*it, PORT, p, true);
185                     }
186                     else if (current->get(i) == E)
187                     {
188                         controller.setObjectProperty(*it, PORT, p, false);
189                     }
190                     else
191                     {
192                         return false;
193                     }
194                 }
195                 return true;
196             }
197             default:
198                 return false;
199         }
200     }
201     else if (v->getType() == types::InternalType::ScilabDouble)
202     {
203         types::Double* current = v->getAs<types::Double>();
204         if (current->getCols() != 0 && current->getCols() != 1)
205         {
206             return false;
207         }
208
209         size_t rows = current->getRows();
210         if (rows != ids.size())
211         {
212             return false;
213         }
214
215         // Translate identifiers: shared variables
216         int i = 0;
217         size_t datatypeIndex = -1;
218         // Translate identifiers from values
219         switch (p)
220         {
221             case FIRING:
222                 for (std::vector<ScicosID>::iterator it = ids.begin(); it != ids.end(); ++it, ++i)
223                 {
224                     double firing = current->get(i);
225
226                     controller.setObjectProperty(*it, PORT, p, firing);
227                 }
228                 return true;
229             case STYLE:
230             case LABEL:
231                 // Do nothing, because if the sizes match, then there are already zero concerned ports, so no ports to update
232                 return true;
233
234             case DATATYPE_TYPE:
235                 datatypeIndex++;
236                 // no break
237             case DATATYPE_COLS:
238                 datatypeIndex++;
239                 // no break
240             case DATATYPE_ROWS:
241             {
242                 datatypeIndex++;
243
244                 for (std::vector<ScicosID>::iterator it = ids.begin(); it != ids.end(); ++it, ++i)
245                 {
246                     std::vector<int> v;
247                     controller.getObjectProperty(*it, PORT, DATATYPE, v);
248
249                     double data = current->get(i);
250                     if (std::floor(data) != data)
251                     {
252                         return false;
253                     }
254
255                     v[datatypeIndex] = static_cast<int>(data);
256                     controller.setObjectProperty(*it, PORT, DATATYPE, v);
257                 }
258                 return true;
259             }
260
261             case IMPLICIT:
262                 // Do nothing, because if the sizes match, then there are already zero concerned ports, so no ports to update
263                 return true;
264         }
265
266     }
267     return false;
268 }
269
270 template<typename Adaptor, object_properties_t p>
271 bool fillNewPorts(std::vector<int>& newPorts, const std::vector<ScicosID>& children, double* d)
272 {
273     for (std::vector<int>::iterator it = newPorts.begin(); it != newPorts.end(); ++it, ++d)
274     {
275
276         if (p == CONNECTED_SIGNALS)   // the associated link must exist
277         {
278             if (0 > *d && *d >= children.size())
279             {
280                 return false;
281             }
282         } // no check is performed for other properties as newPorts will contains value not index
283
284         *it = static_cast<int>(*d);
285     }
286     return true;
287 }
288
289 template<typename Adaptor, object_properties_t p>
290 void updateNewPort(ScicosID oldPort, int newPort, Controller& controller,
291                    std::vector<ScicosID>& children, std::vector<ScicosID>& deletedObjects)
292 {
293     if (p == CONNECTED_SIGNALS)
294     {
295         // update signal and manage deconnection, using newPort as a children index
296         ScicosID oldSignal;
297         controller.getObjectProperty(oldPort, PORT, CONNECTED_SIGNALS, oldSignal);
298         ScicosID newSignal = children[newPort];
299         if (oldSignal != newSignal)
300         {
301             // disconnect the old link
302             ScicosID oldSignalSrc;
303             controller.getObjectProperty(oldSignal, LINK, SOURCE_PORT, oldSignalSrc);
304             ScicosID oldSignalDst;
305             controller.getObjectProperty(oldSignal, LINK, DESTINATION_PORT, oldSignalDst);
306             ScicosID unconnected = 0;
307             if (oldSignalSrc == oldPort)
308             {
309                 controller.setObjectProperty(oldSignalDst, PORT, CONNECTED_SIGNALS, unconnected);
310             }
311             else   // oldSignalDst == oldPort
312             {
313                 controller.setObjectProperty(oldSignalSrc, PORT, CONNECTED_SIGNALS, unconnected);
314             }
315             // Link de-association is not performed as the link will be removed
316             // connect the new link
317             controller.setObjectProperty(newSignal, LINK, SOURCE_PORT, 0);
318             controller.setObjectProperty(oldPort, PORT, CONNECTED_SIGNALS, newSignal);
319             children.erase(std::find(children.begin(), children.end(), oldSignal));
320             deletedObjects.push_back(oldSignal);
321         }
322     }
323     else
324     {
325         // update the p property, using newPort as a value
326         int datatypeIndex = -1;
327         switch (p)
328         {
329             case DATATYPE_TYPE:
330                 datatypeIndex++;
331             case DATATYPE_COLS:
332                 datatypeIndex++;
333             case DATATYPE_ROWS:
334             {
335                 datatypeIndex++;
336                 std::vector<int> datatype;
337                 controller.getObjectProperty(oldPort, PORT, DATATYPE, datatype);
338                 datatype[datatypeIndex] = newPort;
339                 controller.setObjectProperty(oldPort, PORT, DATATYPE, datatype);
340                 return;
341             }
342             default:
343                 controller.setObjectProperty(oldPort, PORT, p, newPort);
344         }
345     }
346 }
347
348 template<typename Adaptor, object_properties_t p>
349 bool addNewPort(ScicosID newPortID, int newPort, const std::vector<ScicosID>& children, Controller& controller)
350 {
351     bool status = true;
352     if (p == CONNECTED_SIGNALS)
353     {
354         // set the connected signal if applicable, using newPort as a children index
355         if (newPort != 0)
356         {
357             ScicosID signal = children[newPort];
358             status = controller.setObjectProperty(newPortID, PORT, CONNECTED_SIGNALS, signal);
359         }
360     }
361     else
362     {
363         // set the requested property, using newPort as a value
364         int datatypeIndex = -1;
365         switch (p)
366         {
367             case DATATYPE_TYPE:
368                 datatypeIndex++;
369             case DATATYPE_COLS:
370                 datatypeIndex++;
371             case DATATYPE_ROWS:
372             {
373                 datatypeIndex++;
374                 std::vector<int> datatype;
375                 controller.getObjectProperty(newPortID, PORT, DATATYPE, datatype);
376                 datatype[datatypeIndex] = newPort;
377                 return controller.setObjectProperty(newPortID, PORT, DATATYPE, datatype);
378             }
379             default:
380                 return controller.setObjectProperty(newPortID, PORT, p, newPort);
381         }
382     }
383
384     return status;
385 }
386
387 /**
388  * Update the ports with a specific property.
389  *
390  * Create ports if needed, remove ports if needed and set a default property on each port.
391  */
392 template<typename Adaptor, object_properties_t p>
393 bool update_ports_property(const Adaptor& adaptor, object_properties_t port_kind,  Controller& controller, types::InternalType* v)
394 {
395     model::Block* adaptee = adaptor.getAdaptee();
396
397     if (v->getType() != types::InternalType::ScilabDouble)
398     {
399         return false;
400     }
401     types::Double* value = v->getAs<types::Double>();
402
403     ScicosID parentDiagram;
404     controller.getObjectProperty(adaptee->id(), BLOCK, PARENT_DIAGRAM, parentDiagram);
405
406     std::vector<ScicosID> children;
407     if (parentDiagram != 0)
408     {
409         controller.getObjectProperty(parentDiagram, DIAGRAM, CHILDREN, children);
410     }
411
412     std::vector<int> newPorts = std::vector<int>(value->getSize());
413
414     // retrieve old data
415     std::vector<ScicosID> oldPorts;
416     controller.getObjectProperty(adaptee->id(), adaptee->kind(), port_kind, oldPorts);
417     std::vector<ScicosID> previousPorts = oldPorts;
418
419     double* d = value->getReal();
420     if (!fillNewPorts<Adaptor, p>(newPorts, children, d))
421     {
422         return false;
423     }
424
425     std::vector<ScicosID> deletedObjects;
426
427     // updated ports
428     while (!oldPorts.empty() && !newPorts.empty())
429     {
430         ScicosID oldPort = oldPorts.back();
431         oldPorts.pop_back();
432         int newPort = newPorts.back();
433         newPorts.pop_back();
434
435         updateNewPort<Adaptor, p>(oldPort, newPort, controller, children, deletedObjects);
436     }
437
438     // removed ports
439     if (!oldPorts.empty())
440     {
441         previousPorts.erase(previousPorts.begin() + oldPorts.size(), previousPorts.end());
442
443         while (!oldPorts.empty())
444         {
445             ScicosID oldPort = oldPorts.back();
446             oldPorts.pop_back();
447
448             ScicosID signal;
449             controller.getObjectProperty(oldPort, PORT, CONNECTED_SIGNALS, signal);
450             if (signal != 0)
451             {
452                 // the link is connected, disconnect the other side
453                 ScicosID oldSignalSrc;
454                 controller.getObjectProperty(signal, LINK, SOURCE_PORT, oldSignalSrc);
455                 ScicosID oldSignalDst;
456                 controller.getObjectProperty(signal, LINK, DESTINATION_PORT, oldSignalDst);
457
458                 ScicosID unconnected = 0;
459                 if (oldSignalSrc == oldPort)
460                 {
461                     controller.setObjectProperty(oldSignalDst, PORT, CONNECTED_SIGNALS, unconnected);
462                 }
463                 else     // oldSignalDst == oldPort
464                 {
465                     controller.setObjectProperty(oldSignalSrc, PORT, CONNECTED_SIGNALS, unconnected);
466                 }
467
468                 children.erase(std::find(children.begin(), children.end(), signal));
469                 deletedObjects.push_back(signal);
470             }
471
472             deletedObjects.push_back(oldPort);
473         }
474
475         controller.setObjectProperty(adaptee->id(), BLOCK, port_kind, previousPorts);
476     }
477
478     // added ports
479     if (!newPorts.empty())
480     {
481         while (!newPorts.empty())
482         {
483             int newPort = newPorts.back();
484             newPorts.pop_back();
485
486             ScicosID id = controller.createObject(PORT);
487             controller.setObjectProperty(id, PORT, SOURCE_BLOCK, adaptee->id());
488             addNewPort<Adaptor, p>(id, newPort, children, controller);
489             previousPorts.push_back(id);
490         }
491
492         controller.setObjectProperty(adaptee->id(), BLOCK, port_kind, previousPorts);
493     }
494
495     // remove objects from the model after de-association
496     if (parentDiagram != 0)
497     {
498         controller.setObjectProperty(parentDiagram, DIAGRAM, CHILDREN, children);
499     }
500     for (std::vector<ScicosID>::iterator it = deletedObjects.begin(); it != deletedObjects.end(); ++it)
501     {
502         controller.deleteObject(*it);
503     }
504
505     return true;
506 }
507
508
509 } /* namespace view_scilab */
510 } /* namespace org_scilab_modules_scicos */
511
512 #endif /* PORTS_MANAGEMENT_HXX_ */