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