Xcos MVC: do not link new ports with old links
[scilab.git] / scilab / modules / scicos / src / cpp / Controller.cpp
1 /*
2  *  Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3  *  Copyright (C) 2014-2016 - Scilab Enterprises - Clement DAVID
4  *
5  * Copyright (C) 2012 - 2016 - Scilab Enterprises
6  *
7  * This file is hereby licensed under the terms of the GNU GPL v2.0,
8  * pursuant to article 5.3.4 of the CeCILL v.2.1.
9  * This file was originally licensed under the terms of the CeCILL v2.1,
10  * and continues to be available under such terms.
11  * For more information, see the COPYING file which you should have received
12  * along with this program.
13  *
14  */
15
16 #include <atomic>
17 #include <string>
18 #include <vector>
19 #include <map>
20 #include <utility>
21 #include <algorithm>
22 #include <iterator>
23
24 #include "utilities.hxx"
25
26 #include "Controller.hxx"
27 #include "model/BaseObject.hxx"
28
29 #include "LoggerView.hxx"
30
31 extern "C" {
32 #include "scicos.h"
33 }
34
35 namespace org_scilab_modules_scicos
36 {
37
38 /*
39  * Implement SharedData methods
40  */
41 Controller::SharedData::SharedData() :
42     onModelStructuralModification(), model(),
43     onViewsStructuralModification(), allNamedViews(), allViews()
44 {
45     unlock(&onModelStructuralModification);
46     unlock(&onViewsStructuralModification);
47 }
48
49 Controller::SharedData::~SharedData()
50 {
51     lock(&onViewsStructuralModification);
52     for (view_set_t::iterator iter = allViews.begin(); iter != allViews.end(); ++iter)
53     {
54         delete *iter;
55     }
56     unlock(&onViewsStructuralModification);
57 }
58
59 Controller::SharedData Controller::m_instance;
60
61 View* Controller::register_view(const std::string& name, View* v)
62 {
63     lock(&m_instance.onViewsStructuralModification);
64
65     m_instance.allNamedViews.push_back(name);
66     m_instance.allViews.push_back(v);
67
68     unlock(&m_instance.onViewsStructuralModification);
69     return v;
70 }
71
72 void Controller::unregister_view(View* v)
73 {
74     lock(&m_instance.onViewsStructuralModification);
75
76     view_set_t::iterator it = std::find(m_instance.allViews.begin(), m_instance.allViews.end(), v);
77     if (it != m_instance.allViews.end())
78     {
79         size_t d = std::distance(m_instance.allViews.begin(), it);
80         m_instance.allNamedViews.erase(m_instance.allNamedViews.begin() + d);
81         m_instance.allViews.erase(m_instance.allViews.begin() + d);
82     }
83
84     unlock(&m_instance.onViewsStructuralModification);
85 }
86
87 View* Controller::unregister_view(const std::string& name)
88 {
89     View* view = nullptr;
90
91     lock(&m_instance.onViewsStructuralModification);
92
93     view_name_set_t::iterator it = std::find(m_instance.allNamedViews.begin(), m_instance.allNamedViews.end(), name);
94     if (it != m_instance.allNamedViews.end())
95     {
96         size_t d = std::distance(m_instance.allNamedViews.begin(), it);
97         view = *(m_instance.allViews.begin() + d);
98         m_instance.allNamedViews.erase(m_instance.allNamedViews.begin() + d);
99         m_instance.allViews.erase(m_instance.allViews.begin() + d);
100     }
101     unlock(&m_instance.onViewsStructuralModification);
102
103     return view;
104 }
105
106 View* Controller::look_for_view(const std::string& name)
107 {
108     View* view = nullptr;
109
110     lock(&m_instance.onViewsStructuralModification);
111     view_name_set_t::iterator it = std::find(m_instance.allNamedViews.begin(), m_instance.allNamedViews.end(), name);
112     if (it != m_instance.allNamedViews.end())
113     {
114         size_t d = std::distance(m_instance.allNamedViews.begin(), it);
115         view = *(m_instance.allViews.begin() + d);
116     }
117     unlock(&m_instance.onViewsStructuralModification);
118
119     return view;
120 }
121
122 void Controller::end_simulation()
123 {
124     end_scicos_sim();
125 }
126
127 Controller::Controller()
128 {
129 }
130
131 Controller::~Controller()
132 {
133 }
134
135 ScicosID Controller::createObject(kind_t k)
136 {
137     lock(&m_instance.onModelStructuralModification);
138     ScicosID uid = m_instance.model.createObject(k);
139     unlock(&m_instance.onModelStructuralModification);
140
141     lock(&m_instance.onViewsStructuralModification);
142     for (view_set_t::iterator iter = m_instance.allViews.begin(); iter != m_instance.allViews.end(); ++iter)
143     {
144         (*iter)->objectCreated(uid, k);
145     }
146     unlock(&m_instance.onViewsStructuralModification);
147
148     return uid;
149 }
150
151 unsigned Controller::referenceObject(const ScicosID uid) const
152 {
153     lock(&m_instance.onModelStructuralModification);
154
155     unsigned refCount = m_instance.model.referenceObject(uid);
156
157     auto o = m_instance.model.getObject(uid);
158     unlock(&m_instance.onModelStructuralModification);
159     if (o == nullptr)
160     {
161         // defensive programming
162         return 0u;
163     }
164
165     lock(&m_instance.onViewsStructuralModification);
166     for (view_set_t::iterator iter = m_instance.allViews.begin(); iter != m_instance.allViews.end(); ++iter)
167     {
168         (*iter)->objectReferenced(uid, o->kind(), refCount);
169     }
170     unlock(&m_instance.onViewsStructuralModification);
171
172     return refCount;
173 }
174
175 void Controller::deleteObject(ScicosID uid)
176 {
177     // if this object is the empty uid, ignore it : is is not stored in the model
178     if (uid == ScicosID())
179     {
180         return;
181     }
182
183     lock(&m_instance.onModelStructuralModification);
184
185     auto initial = m_instance.model.getObject(uid);
186     if (initial == nullptr)
187     {
188         // defensive programming
189         unlock(&m_instance.onModelStructuralModification);
190         return;
191     }
192     const kind_t k = initial->kind();
193
194     // if this object has been referenced somewhere else do not delete it but decrement the reference counter
195     unsigned& refCount = m_instance.model.referenceCount(uid);
196     unlock(&m_instance.onModelStructuralModification);
197     if (refCount > 0)
198     {
199         --refCount;
200
201         lock(&m_instance.onViewsStructuralModification);
202         for (view_set_t::iterator iter = m_instance.allViews.begin(); iter != m_instance.allViews.end(); ++iter)
203         {
204             (*iter)->objectUnreferenced(uid, k, refCount);
205         }
206         unlock(&m_instance.onViewsStructuralModification);
207         return;
208     }
209
210     // We need to delete this object and cleanup all the referenced model object
211
212     // disconnect / remove references of weak connected objects and decrement the reference count of all strongly connected objects.
213     if (k == ANNOTATION)
214     {
215         unlinkVector(initial, PARENT_DIAGRAM, CHILDREN);
216         unlinkVector(initial, PARENT_BLOCK, CHILDREN);
217         // RELATED_TO is not referenced back
218     }
219     else if (k == BLOCK)
220     {
221         unlinkVector(initial, PARENT_DIAGRAM, CHILDREN);
222         unlinkVector(initial, PARENT_BLOCK, CHILDREN);
223
224         deleteVector(initial, INPUTS);
225         deleteVector(initial, OUTPUTS);
226         deleteVector(initial, EVENT_INPUTS);
227         deleteVector(initial, EVENT_OUTPUTS);
228
229         unlink(initial, CHILDREN, PARENT_BLOCK);
230         deleteVector(initial, CHILDREN);
231         // FIXME what about REFERENCED_PORT ?
232     }
233     else if (k == DIAGRAM)
234     {
235         unlink(initial, CHILDREN, PARENT_DIAGRAM);
236         deleteVector(initial, CHILDREN);
237     }
238     else if (k == LINK)
239     {
240         unlinkVector(initial, PARENT_DIAGRAM, CHILDREN);
241         unlinkVector(initial, PARENT_BLOCK, CHILDREN);
242
243         unlinkVector(initial, SOURCE_PORT, CONNECTED_SIGNALS);
244         unlinkVector(initial, DESTINATION_PORT, CONNECTED_SIGNALS);
245     }
246     else if (k == PORT)
247     {
248         unlinkVector(initial, SOURCE_BLOCK, INPUTS);
249         unlinkVector(initial, SOURCE_BLOCK, OUTPUTS);
250         unlinkVector(initial, SOURCE_BLOCK, EVENT_INPUTS);
251         unlinkVector(initial, SOURCE_BLOCK, EVENT_OUTPUTS);
252
253         unlink(initial, CONNECTED_SIGNALS, SOURCE_PORT);
254         unlink(initial, CONNECTED_SIGNALS, DESTINATION_PORT);
255     }
256
257     // delete the object
258     lock(&m_instance.onModelStructuralModification);
259     m_instance.model.deleteObject(uid);
260     unlock(&m_instance.onModelStructuralModification);
261
262     lock(&m_instance.onViewsStructuralModification);
263     for (view_set_t::iterator iter = m_instance.allViews.begin(); iter != m_instance.allViews.end(); ++iter)
264     {
265         (*iter)->objectDeleted(uid, k);
266     }
267     unlock(&m_instance.onViewsStructuralModification);
268 }
269
270 void Controller::unlinkVector(model::BaseObject* initial, object_properties_t uid_prop, object_properties_t ref_prop)
271 {
272     ScicosID v;
273     getObjectProperty(initial->id(), initial->kind(), uid_prop, v);
274     if (v != ScicosID())
275     {
276         auto o = getObject(v);
277         if (o == nullptr)
278         {
279             return;
280         }
281
282         std::vector<ScicosID> children;
283         getObjectProperty(o->id(), o->kind(), ref_prop, children);
284
285         std::vector<ScicosID>::iterator it = std::find(children.begin(), children.end(), initial->id());
286         if (it != children.end())
287         {
288             children.erase(it);
289         }
290
291         setObjectProperty(o->id(), o->kind(), ref_prop, children);
292     }
293 }
294
295 void Controller::unlink(model::BaseObject* initial, object_properties_t uid_prop, object_properties_t ref_prop)
296 {
297     std::vector<ScicosID> v;
298     getObjectProperty(initial->id(), initial->kind(), uid_prop, v);
299     for (const ScicosID id : v)
300     {
301         if (id != ScicosID())
302         {
303             auto o = getObject(id);
304             if (o == nullptr)
305             {
306                 continue;
307             }
308
309             // Find which end of the link is connected to the port
310             ScicosID oppositeRef;
311             getObjectProperty(o->id(), o->kind(), ref_prop, oppositeRef);
312             if (oppositeRef == initial->id())
313             {
314                 setObjectProperty(o->id(), o->kind(), ref_prop, ScicosID());
315             }
316         }
317     }
318 }
319
320 void Controller::deleteVector(model::BaseObject* initial, object_properties_t uid_prop)
321 {
322     std::vector<ScicosID> children;
323     getObjectProperty(initial->id(), initial->kind(), uid_prop, children);
324
325     for (ScicosID id : children)
326     {
327         deleteObject(id);
328     }
329 }
330
331 template<typename T>
332 void Controller::cloneProperties(model::BaseObject* initial, model::BaseObject* clone)
333 {
334     for (int i = 0; i < MAX_OBJECT_PROPERTIES; ++i)
335     {
336         enum object_properties_t p = static_cast<enum object_properties_t>(i);
337
338         T value;
339         bool status = getObjectProperty(initial, p, value);
340         if (status)
341         {
342             setObjectProperty(clone, p, value);
343         }
344     }
345 }
346
347 model::BaseObject* Controller::cloneObject(std::map<model::BaseObject*, model::BaseObject*>& mapped, model::BaseObject* initial, bool cloneChildren, bool clonePorts)
348 {
349     const kind_t k = initial->kind();
350     ScicosID o = createObject(k);
351     model::BaseObject* cloned = getObject(o);
352     mapped.insert(std::make_pair(initial, cloned));
353
354     lock(&m_instance.onViewsStructuralModification);
355     for (view_set_t::iterator iter = m_instance.allViews.begin(); iter != m_instance.allViews.end(); ++iter)
356     {
357         (*iter)->objectCloned(initial->id(), o, k);
358     }
359     unlock(&m_instance.onViewsStructuralModification);
360
361     // Get then set all properties per type that do not manage ScicosID
362     cloneProperties<double>(initial, cloned);
363     cloneProperties<int>(initial, cloned);
364     cloneProperties<bool>(initial, cloned);
365     cloneProperties<std::string>(initial, cloned);
366     cloneProperties<std::vector<double> >(initial, cloned);
367     cloneProperties<std::vector<int> >(initial, cloned);
368     cloneProperties<std::vector<std::string> >(initial, cloned);
369
370     // deep copy children, manage ScicosID and std::vector<ScicosID>
371     if (k == ANNOTATION)
372     {
373         deepClone(mapped, initial, cloned, PARENT_DIAGRAM, false);
374         deepClone(mapped, initial, cloned, PARENT_BLOCK, false);
375         deepClone(mapped, initial, cloned, RELATED_TO, true);
376     }
377     else if (k == BLOCK)
378     {
379         deepClone(mapped, initial, cloned, PARENT_DIAGRAM, false);
380         if (clonePorts)
381         {
382             // copy the block and all its ports
383             deepCloneVector(mapped, initial, cloned, INPUTS, true);
384             deepCloneVector(mapped, initial, cloned, OUTPUTS, true);
385             deepCloneVector(mapped, initial, cloned, EVENT_INPUTS, true);
386             deepCloneVector(mapped, initial, cloned, EVENT_OUTPUTS, true);
387         }
388
389         deepClone(mapped, initial, cloned, PARENT_BLOCK, false);
390         if (cloneChildren)
391         {
392             deepCloneVector(mapped, initial, cloned, CHILDREN, true);
393         }
394         // FIXME what about REFERENCED_PORT ?
395     }
396     else if (k == DIAGRAM)
397     {
398         if (cloneChildren)
399         {
400             deepCloneVector(mapped, initial, cloned, CHILDREN, true);
401         }
402     }
403     else if (k == LINK)
404     {
405         deepClone(mapped, initial, cloned, PARENT_DIAGRAM, false);
406         deepClone(mapped, initial, cloned, PARENT_BLOCK, false);
407         deepClone(mapped, initial, cloned, SOURCE_PORT, false);
408         deepClone(mapped, initial, cloned, DESTINATION_PORT, false);
409     }
410     else if (k == PORT)
411     {
412         deepClone(mapped, initial, cloned, SOURCE_BLOCK, false);
413         deepCloneVector(mapped, initial, cloned, CONNECTED_SIGNALS, false);
414     }
415     return cloned;
416 }
417
418 void Controller::deepClone(std::map<model::BaseObject*, model::BaseObject*>& mapped, model::BaseObject* initial, model::BaseObject* clone, object_properties_t p, bool cloneIfNotFound)
419 {
420     ScicosID v;
421     getObjectProperty(initial, p, v);
422
423     model::BaseObject* opposite = getObject(v);
424     model::BaseObject* cloned;
425
426     std::map<model::BaseObject*, model::BaseObject*>::iterator it = mapped.find(opposite);
427     if (it != mapped.end())
428     {
429         cloned = it->second;
430     }
431     else
432     {
433         if (cloneIfNotFound)
434         {
435             if (v != ScicosID())
436             {
437                 cloned = cloneObject(mapped, opposite, true, true);
438             }
439             else
440             {
441                 cloned = nullptr;
442             }
443         }
444         else
445         {
446             cloned = nullptr;
447         }
448     }
449
450     if (cloned == nullptr)
451     {
452         setObjectProperty(clone, p, ScicosID());
453     }
454     else
455     {
456         setObjectProperty(clone, p, cloned->id());
457     }
458 }
459
460 void Controller::deepCloneVector(std::map<model::BaseObject*, model::BaseObject*>& mapped, model::BaseObject* initial, model::BaseObject* clone, object_properties_t p, bool cloneIfNotFound)
461 {
462     std::vector<ScicosID> v;
463     getObjectProperty(initial, p, v);
464
465     std::vector<model::BaseObject*> cloned;
466     cloned.reserve(v.size());
467
468     for (const ScicosID & id : v)
469     {
470         if (id == ScicosID())
471         {
472             // Deleted Block, the cloning is done at Adapter level
473             cloned.push_back(nullptr);
474             continue;
475         }
476
477         model::BaseObject* opposite = getObject(id);
478         std::map<model::BaseObject*, model::BaseObject*>::iterator it = mapped.find(opposite);
479         if (it != mapped.end())
480         {
481             cloned.push_back(it->second);
482         }
483         else
484         {
485             if (cloneIfNotFound)
486             {
487                 if (id != ScicosID())
488                 {
489                     model::BaseObject* clone = cloneObject(mapped, opposite, true, true);
490                     cloned.push_back(clone);
491                 }
492                 else
493                 {
494                     cloned.push_back(nullptr);
495                 }
496             }
497             else
498             {
499                 cloned.push_back(nullptr);
500             }
501         }
502     }
503
504     // update the ScicosID related properties after cloning all the objects
505     if (p == CHILDREN)
506     {
507         for (auto const & it : mapped)
508         {
509             model::BaseObject* initial = it.first;
510             model::BaseObject* cloned = it.second;
511
512             switch (initial->kind())
513             {
514                 case PORT:
515                     deepCloneVector(mapped, initial, cloned, CONNECTED_SIGNALS, false);
516                     break;
517                 case LINK:
518                     deepClone(mapped, initial, cloned, SOURCE_PORT, false);
519                     deepClone(mapped, initial, cloned, DESTINATION_PORT, false);
520                     break;
521                 default:
522                     break;
523             }
524         }
525     }
526
527     // set the main object vector property
528     std::vector<ScicosID> clonedUIDs(cloned.size());
529     std::transform(cloned.begin(), cloned.end(), clonedUIDs.begin(), [](model::BaseObject * o)
530     {
531         if (o == nullptr)
532         {
533             return ScicosID();
534         }
535         else
536         {
537             return o->id();
538         }
539     });
540
541     setObjectProperty(clone, p, clonedUIDs);
542 }
543
544 ScicosID Controller::cloneObject(ScicosID uid, bool cloneChildren, bool clonePorts)
545 {
546     std::map<model::BaseObject*, model::BaseObject*> mapped;
547     model::BaseObject* clone = cloneObject(mapped, getObject(uid), cloneChildren, clonePorts);
548
549     return clone->id();
550 }
551
552 kind_t Controller::getKind(ScicosID uid) const
553 {
554     lock(&m_instance.onModelStructuralModification);
555
556     kind_t kind = m_instance.model.getKind(uid);
557
558     unlock(&m_instance.onModelStructuralModification);
559     return kind;
560 }
561
562 std::vector<ScicosID> Controller::getAll(kind_t k) const
563 {
564     lock(&m_instance.onModelStructuralModification);
565
566     auto vec = m_instance.model.getAll(k);
567
568     unlock(&m_instance.onModelStructuralModification);
569     return vec;
570 }
571
572 void Controller::sortAndFillKind(std::vector<ScicosID>& uids, std::vector<int>& kinds)
573 {
574     lock(&m_instance.onModelStructuralModification);
575
576     // create a container of pair
577     struct local_pair
578     {
579         ScicosID first;
580         int second;
581     };
582     std::vector<local_pair> container(uids.size());
583
584     // fill it
585     for (size_t i = 0; i < uids.size(); ++i)
586     {
587         container[i] = { uids[i], m_instance.model.getKind(uids[i]) };
588     }
589
590     // sort according to the kinds
591     std::stable_sort(container.begin(), container.end(), [] (const local_pair & a, const local_pair & b)
592     {
593         return a.second < b.second;
594     });
595
596     // move things back
597     uids.clear();
598     kinds.reserve(uids.capacity());
599     for (const auto & v : container)
600     {
601         uids.push_back(v.first);
602         kinds.push_back(v.second);
603     }
604
605     unlock(&m_instance.onModelStructuralModification);
606 }
607
608 template<typename T>
609 bool Controller::getObjectProperty(ScicosID uid, kind_t k, object_properties_t p, T& v) const
610 {
611     lock(&m_instance.onModelStructuralModification);
612     bool ret = m_instance.model.getObjectProperty(uid, k, p, v);
613     unlock(&m_instance.onModelStructuralModification);
614     return ret;
615 }
616
617 bool Controller::getObjectProperty(ScicosID uid, kind_t k, object_properties_t p, double& v) const
618 {
619     return getObjectProperty<>(uid, k, p, v);
620 }
621 bool Controller::getObjectProperty(ScicosID uid, kind_t k, object_properties_t p, int& v) const
622 {
623     return getObjectProperty<>(uid, k, p, v);
624 }
625 bool Controller::getObjectProperty(ScicosID uid, kind_t k, object_properties_t p, bool& v) const
626 {
627     return getObjectProperty<>(uid, k, p, v);
628 }
629 bool Controller::getObjectProperty(ScicosID uid, kind_t k, object_properties_t p, std::string& v) const
630 {
631     return getObjectProperty<>(uid, k, p, v);
632 }
633 bool Controller::getObjectProperty(ScicosID uid, kind_t k, object_properties_t p, ScicosID& v) const
634 {
635     return getObjectProperty<>(uid, k, p, v);
636 }
637 bool Controller::getObjectProperty(ScicosID uid, kind_t k, object_properties_t p, std::vector<double>& v) const
638 {
639     return getObjectProperty<>(uid, k, p, v);
640 }
641 bool Controller::getObjectProperty(ScicosID uid, kind_t k, object_properties_t p, std::vector<int>& v) const
642 {
643     return getObjectProperty<>(uid, k, p, v);
644 }
645 bool Controller::getObjectProperty(ScicosID uid, kind_t k, object_properties_t p, std::vector<bool>& v) const
646 {
647     return getObjectProperty<>(uid, k, p, v);
648 }
649 bool Controller::getObjectProperty(ScicosID uid, kind_t k, object_properties_t p, std::vector< std::string >& v) const
650 {
651     return getObjectProperty<>(uid, k, p, v);
652 }
653 bool Controller::getObjectProperty(ScicosID uid, kind_t k, object_properties_t p, std::vector<ScicosID>& v) const
654 {
655     return getObjectProperty<>(uid, k, p, v);
656 }
657
658 template<typename T>
659 update_status_t Controller::setObjectProperty(ScicosID uid, kind_t k, object_properties_t p, T v)
660 {
661     lock(&m_instance.onModelStructuralModification);
662     update_status_t status = m_instance.model.setObjectProperty(uid, k, p, v);
663     unlock(&m_instance.onModelStructuralModification);
664
665     lock(&m_instance.onViewsStructuralModification);
666     for (view_set_t::iterator iter = m_instance.allViews.begin(); iter != m_instance.allViews.end(); ++iter)
667     {
668         (*iter)->propertyUpdated(uid, k, p, status);
669     }
670     unlock(&m_instance.onViewsStructuralModification);
671     return status;
672 }
673
674 update_status_t Controller::setObjectProperty(ScicosID uid, kind_t k, object_properties_t p, double v)
675 {
676     return setObjectProperty<>(uid, k, p, v);
677 }
678 update_status_t Controller::setObjectProperty(ScicosID uid, kind_t k, object_properties_t p, int v)
679 {
680     return setObjectProperty<>(uid, k, p, v);
681 }
682 update_status_t Controller::setObjectProperty(ScicosID uid, kind_t k, object_properties_t p, bool v)
683 {
684     return setObjectProperty<>(uid, k, p, v);
685 }
686 update_status_t Controller::setObjectProperty(ScicosID uid, kind_t k, object_properties_t p, ScicosID v)
687 {
688     return setObjectProperty<>(uid, k, p, v);
689 }
690 update_status_t Controller::setObjectProperty(ScicosID uid, kind_t k, object_properties_t p, const std::string& v)
691 {
692     return setObjectProperty<>(uid, k, p, v);
693 }
694 update_status_t Controller::setObjectProperty(ScicosID uid, kind_t k, object_properties_t p, const std::vector<double>& v)
695 {
696     return setObjectProperty<>(uid, k, p, v);
697 }
698 update_status_t Controller::setObjectProperty(ScicosID uid, kind_t k, object_properties_t p, const std::vector<int>& v)
699 {
700     return setObjectProperty<>(uid, k, p, v);
701 }
702 update_status_t Controller::setObjectProperty(ScicosID uid, kind_t k, object_properties_t p, const std::vector<bool>& v)
703 {
704     return setObjectProperty<>(uid, k, p, v);
705 }
706 update_status_t Controller::setObjectProperty(ScicosID uid, kind_t k, object_properties_t p, const std::vector< std::string >& v)
707 {
708     return setObjectProperty<>(uid, k, p, v);
709 }
710 update_status_t Controller::setObjectProperty(ScicosID uid, kind_t k, object_properties_t p, const std::vector<ScicosID>& v)
711 {
712     return setObjectProperty<>(uid, k, p, v);
713 }
714
715 model::BaseObject* Controller::getObject(ScicosID uid) const
716 {
717     lock(&m_instance.onModelStructuralModification);
718     model::BaseObject* o = m_instance.model.getObject(uid);
719     unlock(&m_instance.onModelStructuralModification);
720     return o;
721 }
722
723 } /* namespace org_scilab_modules_scicos */
724