Xcos MVC: slight improvements in Adapters
[scilab.git] / scilab / modules / scicos / src / cpp / view_scilab / DiagramAdapter.cpp
1 /*
2  *  Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3  *  Copyright (C) 2014-2014 - Scilab Enterprises - Clement DAVID
4  *
5  *  This file must be used under the terms of the CeCILL.
6  *  This source file is licensed as described in the file COPYING, which
7  *  you should have received as part of this distribution.  The terms
8  *  are also available at
9  *  http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt
10  *
11  */
12
13 #include <string>
14 #include <vector>
15 #include <sstream>
16 #include <memory>
17
18 #include "internal.hxx"
19 #include "double.hxx"
20 #include "list.hxx"
21 #include "mlist.hxx"
22 #include "tlist.hxx"
23 #include "string.hxx"
24 #include "types.hxx"
25 #include "user.hxx"
26
27 #include "utilities.hxx"
28 #include "Controller.hxx"
29 #include "DiagramAdapter.hxx"
30 #include "Adapters.hxx"
31 #include "ParamsAdapter.hxx"
32 #include "BlockAdapter.hxx"
33 #include "LinkAdapter.hxx"
34 #include "TextAdapter.hxx"
35 #include "model/BaseObject.hxx"
36
37 extern "C" {
38 #include "sci_malloc.h"
39 #include "charEncoding.h"
40 }
41
42 namespace org_scilab_modules_scicos
43 {
44 namespace view_scilab
45 {
46 namespace
47 {
48
49 const std::wstring TextSharedTypeStr (L"Text");
50
51 struct props
52 {
53
54     static types::InternalType* get(const DiagramAdapter& adaptor, const Controller& controller)
55     {
56         ParamsAdapter localAdaptor = ParamsAdapter(adaptor.getAdaptee());
57         return localAdaptor.getAsTList(new types::TList(), controller);
58     }
59
60     static bool set(DiagramAdapter& adaptor, types::InternalType* v, Controller& controller)
61     {
62         ParamsAdapter localAdaptor = ParamsAdapter(adaptor.getAdaptee());
63         return localAdaptor.setAsTList(v, controller);
64     }
65 };
66
67 struct objs
68 {
69
70     static types::InternalType* get(const DiagramAdapter& adaptor, const Controller& controller)
71     {
72         model::Diagram* adaptee = adaptor.getAdaptee().get();
73
74         std::vector<ScicosID> children;
75         controller.getObjectProperty(adaptee->id(), DIAGRAM, CHILDREN, children);
76
77         types::List* o = new types::List();
78
79         std::vector<link_t> from = adaptor.getFrom();
80         std::vector<link_t> to = adaptor.getTo();
81
82         int link_number = 0;
83         Controller newController;
84         for (int i = 0; i < static_cast<int>(children.size()); ++i)
85         {
86             std::shared_ptr<model::BaseObject> item = newController.getObject(children[i]);
87             switch (item->kind())
88             {
89                 case ANNOTATION:
90                 {
91                     std::shared_ptr<model::Annotation> annotation = std::static_pointer_cast<model::Annotation>(item);
92                     TextAdapter* localAdaptor = new TextAdapter(annotation);
93                     o->set(i, localAdaptor);
94                     continue;
95                 }
96                 case BLOCK:
97                 {
98                     std::shared_ptr<model::Block> block = std::static_pointer_cast<model::Block>(item);
99                     BlockAdapter* localAdaptor = new BlockAdapter(block);
100
101                     types::List* list_objects = adaptor.getListObjects()->getAs<types::List>();
102                     if (i < list_objects->getSize())
103                     {
104                         BlockAdapter* oldBlock = list_objects->get(i)->getAs<BlockAdapter>();
105                         DiagramAdapter* oldBlockDiagram = oldBlock->getDiagram();
106                         if (oldBlockDiagram != nullptr)
107                         {
108                             oldBlockDiagram->IncreaseRef();
109                         }
110                         localAdaptor->setDiagram(oldBlockDiagram);
111                         types::InternalType* oldDoc = oldBlock->getDocContent();
112                         localAdaptor->setDocContent(oldDoc);
113                     }
114
115                     o->set(i, localAdaptor);
116                     continue;
117                 }
118                 case LINK:
119                 {
120                     std::shared_ptr<model::Link> link = std::static_pointer_cast<model::Link>(item);
121                     LinkAdapter* localAdaptor = new LinkAdapter(link);
122
123                     // In case a Link points to a Block that has not been added yet,
124                     // retrieve the 'from' and 'to' values from the Diagram Adapter if they have been saved
125                     if (link_number < static_cast<int>(from.size()))
126                     {
127                         localAdaptor->setFrom(from[link_number]);
128                         localAdaptor->setTo(to[link_number]);
129                         link_number++;
130                     }
131                     o->set(i, localAdaptor);
132                     continue;
133                 }
134                 default:
135                     return 0;
136             }
137         }
138         return o;
139     }
140
141     static bool set(DiagramAdapter& adaptor, types::InternalType* v, Controller& controller)
142     {
143         // Decode the list and set all children of the Diagram
144         if (v->getType() != types::InternalType::ScilabList)
145         {
146             return false;
147         }
148
149         model::Diagram* adaptee = adaptor.getAdaptee().get();
150
151         types::List* list = v->getAs<types::List>();
152
153         // Clear the children list before the loop to reset the diagram children
154         // and clear the old Links information
155         std::vector<ScicosID> diagramChildren;
156         controller.getObjectProperty(adaptee->id(), DIAGRAM, CHILDREN, diagramChildren);
157         for (ScicosID id : diagramChildren)
158         {
159             auto o = controller.getObject(id);
160             controller.setObjectProperty(id, o->kind(), PARENT_DIAGRAM, 0ll);
161         }
162         diagramChildren.clear();
163
164         // Set the children to the right IDs
165         std::vector<LinkAdapter*> linkListView;
166         for (int i = 0; i < list->getSize(); ++i)
167         {
168             if (list->get(i)->getType() == types::InternalType::ScilabUserType)
169             {
170
171                 const Adapters::adapters_index_t adapter_index = Adapters::instance().lookup_by_typename(list->get(i)->getShortTypeStr());
172
173                 // Then, each adapter gets linked to the diagram through its adaptee (PARENT_DIAGRAM)
174                 // and the diagram's adaptee lists its adaptees (CHILDREN).
175                 ScicosID id;
176                 switch (adapter_index)
177                 {
178                     case Adapters::BLOCK_ADAPTER:
179                     {
180                         BlockAdapter* modelElement = list->get(i)->getAs<BlockAdapter>();
181
182                         id = modelElement->getAdaptee()->id();
183
184                         controller.setObjectProperty(id, BLOCK, PARENT_DIAGRAM, adaptee->id());
185                         diagramChildren.push_back(id);
186                         break;
187                     }
188                     case Adapters::LINK_ADAPTER:
189                     {
190                         LinkAdapter* modelElement = list->get(i)->getAs<LinkAdapter>();
191
192                         id = modelElement->getAdaptee()->id();
193
194                         controller.setObjectProperty(id, LINK, PARENT_DIAGRAM, adaptee->id());
195
196                         // Do the linking in the next loop, in case the Link points to a Block that has not been added yet
197                         linkListView.push_back(modelElement);
198
199                         diagramChildren.push_back(id);
200                         break;
201                     }
202                     case Adapters::TEXT_ADAPTER:
203                     {
204                         TextAdapter* modelElement = list->get(i)->getAs<TextAdapter>();
205
206                         id = modelElement->getAdaptee()->id();
207
208                         controller.setObjectProperty(id, ANNOTATION, PARENT_DIAGRAM, adaptee->id());
209                         diagramChildren.push_back(id);
210                         break;
211                     }
212                     default:
213                         return false;
214                 }
215             }
216             else if (list->get(i)->getType() == types::InternalType::ScilabMList)
217             {
218                 // Allow to pass mlists to 'objs', representing Text blocks
219                 types::MList* modelElement = list->get(i)->getAs<types::MList>();
220                 types::String* header = modelElement->getFieldNames();
221                 if (header->get(0) != TextSharedTypeStr)
222                 {
223                     return false;
224                 }
225
226                 // Create a Text block based on the input MList and add it to the diagram
227                 ScicosID newID = controller.createObject(ANNOTATION);
228                 TextAdapter* newAdaptor = new TextAdapter(std::static_pointer_cast<model::Annotation>(controller.getObject(newID)));
229                 // Fill the block with the input mlist
230                 if (!newAdaptor->setAsTList(modelElement, controller))
231                 {
232                     return false;
233                 }
234
235                 // Modify the input list to save the new block
236                 list->set(i, newAdaptor);
237
238                 controller.setObjectProperty(newID, ANNOTATION, PARENT_DIAGRAM, adaptee->id());
239                 diagramChildren.push_back(newID);
240             }
241             else
242             {
243                 return false;
244             }
245         }
246         controller.setObjectProperty(adaptee->id(), DIAGRAM, CHILDREN, diagramChildren);
247
248         adaptor.setListObjects(v);
249
250         std::vector<link_t> from_content (linkListView.size());
251         std::vector<link_t> to_content (linkListView.size());
252         // Do the linking at model-level
253         for (int i = 0; i < static_cast<int>(linkListView.size()); ++i)
254         {
255             // Trigger 'from' and 'to' properties
256             from_content[i] = linkListView[i]->getFrom();
257             linkListView[i]->setFromInModel(from_content[i], controller);
258
259             to_content[i] = linkListView[i]->getTo();
260             linkListView[i]->setToInModel(to_content[i], controller);
261         }
262         adaptor.setFrom(from_content);
263         adaptor.setTo(to_content);
264
265         return true;
266     }
267 };
268
269 struct version
270 {
271
272     static types::InternalType* get(const DiagramAdapter& adaptor, const Controller& controller)
273     {
274         model::Diagram* adaptee = adaptor.getAdaptee().get();
275
276         std::string version;
277         controller.getObjectProperty(adaptee->id(), DIAGRAM, VERSION_NUMBER, version);
278
279         return new types::String(version.data());
280     }
281
282     static bool set(DiagramAdapter& adaptor, types::InternalType* v, Controller& controller)
283     {
284         if (v->getType() == types::InternalType::ScilabString)
285         {
286             types::String* current = v->getAs<types::String>();
287             if (current->getSize() != 1)
288             {
289                 return false;
290             }
291
292             model::Diagram* adaptee = adaptor.getAdaptee().get();
293
294             char* c_str = wide_string_to_UTF8(current->get(0));
295             std::string version (c_str);
296             FREE(c_str);
297
298             controller.setObjectProperty(adaptee->id(), DIAGRAM, VERSION_NUMBER, version);
299             return true;
300         }
301         else if (v->getType() == types::InternalType::ScilabDouble)
302         {
303             types::Double* current = v->getAs<types::Double>();
304             if (current->getSize() != 0)
305             {
306                 return false;
307             }
308
309             model::Diagram* adaptee = adaptor.getAdaptee().get();
310
311             std::string version;
312             controller.setObjectProperty(adaptee->id(), DIAGRAM, VERSION_NUMBER, version);
313             return true;
314         }
315
316         return false;
317     }
318 };
319
320 struct contrib
321 {
322
323     static types::InternalType* get(const DiagramAdapter& adaptor, const Controller& /*controller*/)
324     {
325         return adaptor.getContribContent();
326     }
327
328     static bool set(DiagramAdapter& adaptor, types::InternalType* v, Controller& /*controller*/)
329     {
330         adaptor.setContribContent(v->clone());
331         return true;
332     }
333 };
334
335 } /* namespace */
336
337 template<> property<DiagramAdapter>::props_t property<DiagramAdapter>::fields = property<DiagramAdapter>::props_t();
338
339 DiagramAdapter::DiagramAdapter(std::shared_ptr<org_scilab_modules_scicos::model::Diagram> adaptee) :
340     BaseAdapter<DiagramAdapter, org_scilab_modules_scicos::model::Diagram>(adaptee),
341     list_objects(new types::List()),
342     from_vec(),
343     to_vec(),
344     contrib_content(new types::List())
345 {
346     if (property<DiagramAdapter>::properties_have_not_been_set())
347     {
348         property<DiagramAdapter>::fields.reserve(4);
349         property<DiagramAdapter>::add_property(L"props", &props::get, &props::set);
350         property<DiagramAdapter>::add_property(L"objs", &objs::get, &objs::set);
351         property<DiagramAdapter>::add_property(L"version", &version::get, &version::set);
352         property<DiagramAdapter>::add_property(L"contrib", &contrib::get, &contrib::set);
353     }
354 }
355
356 DiagramAdapter::DiagramAdapter(const DiagramAdapter& adapter) :
357     BaseAdapter<DiagramAdapter, org_scilab_modules_scicos::model::Diagram>(adapter),
358     list_objects(),
359     from_vec(adapter.from_vec),
360     to_vec(adapter.to_vec),
361     contrib_content(adapter.getContribContent())
362 {
363     // Generate an Adapter for each child of the cloned Diagram and store them all in 'list_objects'
364     Controller controller;
365     std::vector<ScicosID> children;
366     controller.getObjectProperty(getAdaptee()->id(), DIAGRAM, CHILDREN, children);
367
368     std::vector<LinkAdapter*> linkListView; // Store the new LinkAdapters to make the linking at model-level after the loop
369     types::List* List_objects = new types::List();
370     for (int i = 0; i < static_cast<int>(children.size()); ++i)
371     {
372         std::shared_ptr<model::BaseObject> item = controller.getObject(children[i]);
373         switch (item->kind())
374         {
375             case ANNOTATION:
376             {
377                 std::shared_ptr<model::Annotation> annotation = std::static_pointer_cast<model::Annotation>(item);
378                 TextAdapter* localAdaptor = new TextAdapter(annotation);
379
380                 List_objects->set(i, localAdaptor);
381                 continue;
382             }
383             case BLOCK:
384             {
385                 std::shared_ptr<model::Block> block = std::static_pointer_cast<model::Block>(item);
386                 BlockAdapter* localAdaptor = new BlockAdapter(block);
387
388                 // If the diagram's block was a SuperBlock, make its new adapter point to its old diagram
389                 types::List* oldList_objects = adapter.getListObjects()->getAs<types::List>();
390                 if (i < oldList_objects->getSize())
391                 {
392                     BlockAdapter* oldBlock = oldList_objects->get(i)->getAs<BlockAdapter>();
393                     DiagramAdapter* oldBlockDiagram = oldBlock->getDiagram();
394                     if (oldBlockDiagram != nullptr)
395                     {
396                         oldBlockDiagram->IncreaseRef();
397                     }
398                     localAdaptor->setDiagram(oldBlockDiagram);
399                 }
400
401                 List_objects->set(i, localAdaptor);
402                 continue;
403             }
404             case LINK:
405             {
406                 std::shared_ptr<model::Link> link = std::static_pointer_cast<model::Link>(item);
407                 LinkAdapter* localAdaptor = new LinkAdapter(link);
408
409                 // Do the model linking in the next loop, in case the Link points to a Block that has not been added yet
410                 linkListView.push_back(localAdaptor);
411
412                 List_objects->set(i, localAdaptor);
413                 continue;
414             }
415             default:
416             {
417             }
418         }
419     }
420
421     // Do the linking at model-level, from the old 'from_vec' and 'to_vec'
422     for (int i = 0; i < static_cast<int>(linkListView.size()); ++i)
423     {
424         // Trigger 'from' and 'to' properties
425         linkListView[i]->setFromInModel(from_vec[i], controller);
426         linkListView[i]->setToInModel(to_vec[i], controller);
427     }
428
429     list_objects = List_objects;
430 }
431
432 DiagramAdapter::~DiagramAdapter()
433 {
434     // Unlink the diagram's children if the adaptee is being deleted
435     if (getAdaptee().use_count() == 3)
436     {
437         Controller controller;
438         std::vector<ScicosID> diagramChildren;
439         controller.getObjectProperty(getAdaptee()->id(), DIAGRAM, CHILDREN, diagramChildren);
440         for (ScicosID id : diagramChildren)
441         {
442             auto o = controller.getObject(id);
443             controller.setObjectProperty(id, o->kind(), PARENT_DIAGRAM, 0ll);
444         }
445         diagramChildren.clear();
446         controller.setObjectProperty(getAdaptee()->id(), DIAGRAM, CHILDREN, diagramChildren);
447     }
448
449     list_objects->DecreaseRef();
450     list_objects->killMe();
451
452     contrib_content->DecreaseRef();
453     contrib_content->killMe();
454 }
455
456 std::wstring DiagramAdapter::getTypeStr()
457 {
458     return getSharedTypeStr();
459 }
460 std::wstring DiagramAdapter::getShortTypeStr()
461 {
462     return getSharedTypeStr();
463 }
464
465 types::InternalType* DiagramAdapter::getContribContent() const
466 {
467     contrib_content->IncreaseRef();
468     return contrib_content;
469 }
470
471 void DiagramAdapter::setContribContent(types::InternalType* v)
472 {
473     contrib_content->DecreaseRef();
474     contrib_content->killMe();
475
476     v->IncreaseRef();
477     contrib_content = v;
478 }
479
480 types::InternalType* DiagramAdapter::getListObjects() const
481 {
482     return list_objects;
483 }
484
485 void DiagramAdapter::setListObjects(types::InternalType* v)
486 {
487     // The old 'list_objects' needs to be freed after setting it to 'v'
488     types::InternalType* temp = list_objects;
489
490     v->IncreaseRef();
491     list_objects = v;
492
493     temp->DecreaseRef();
494     temp->killMe();
495 }
496
497 std::vector<link_t> DiagramAdapter::getFrom() const
498 {
499     return from_vec;
500 }
501
502 void DiagramAdapter::setFrom(const std::vector<link_t>& from)
503 {
504     from_vec = from;
505 }
506
507 std::vector<link_t> DiagramAdapter::getTo() const
508 {
509     return to_vec;
510 }
511
512 void DiagramAdapter::setTo(const std::vector<link_t>& to)
513 {
514     to_vec = to;
515 }
516
517 } /* namespace view_scilab */
518 } /* namespace org_scilab_modules_scicos */