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