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