Xcos: fix SuperBlock children ownership
[scilab.git] / scilab / modules / xcos / src / java / org / scilab / modules / xcos / block / BasicBlock.java
1 /*
2  * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3  * Copyright (C) 2009 - DIGITEO - Bruno JOFRET
4  * Copyright (C) 2011-2017 - Scilab Enterprises - Clement DAVID
5  *
6  * Copyright (C) 2012 - 2016 - Scilab Enterprises
7  *
8  * This file is hereby licensed under the terms of the GNU GPL v2.0,
9  * pursuant to article 5.3.4 of the CeCILL v.2.1.
10  * This file was originally licensed under the terms of the CeCILL v2.1,
11  * and continues to be available under such terms.
12  * For more information, see the COPYING file which you should have received
13  * along with this program.
14  *
15  */
16
17 package org.scilab.modules.xcos.block;
18
19 import java.awt.MouseInfo;
20 import java.io.Serializable;
21 import java.util.ArrayList;
22 import java.util.Arrays;
23 import java.util.Collections;
24 import java.util.Deque;
25 import java.util.HashMap;
26 import java.util.HashSet;
27 import java.util.LinkedList;
28 import java.util.List;
29 import java.util.Map;
30 import java.util.Set;
31
32 import org.scilab.modules.action_binding.InterpreterManagement;
33 import org.scilab.modules.graph.ScilabGraph;
34 import org.scilab.modules.graph.actions.CopyAction;
35 import org.scilab.modules.graph.actions.CutAction;
36 import org.scilab.modules.graph.actions.DeleteAction;
37 import org.scilab.modules.graph.actions.base.DefaultAction;
38 import org.scilab.modules.gui.bridge.contextmenu.SwingScilabContextMenu;
39 import org.scilab.modules.gui.contextmenu.ContextMenu;
40 import org.scilab.modules.gui.contextmenu.ScilabContextMenu;
41 import org.scilab.modules.gui.events.callback.CommonCallBack;
42 import org.scilab.modules.gui.menu.Menu;
43 import org.scilab.modules.gui.menu.ScilabMenu;
44 import org.scilab.modules.gui.menuitem.MenuItem;
45 import org.scilab.modules.gui.menuitem.ScilabMenuItem;
46 import org.scilab.modules.xcos.JavaController;
47 import org.scilab.modules.xcos.Kind;
48 import org.scilab.modules.xcos.ObjectProperties;
49 import org.scilab.modules.xcos.VectorOfDouble;
50 import org.scilab.modules.xcos.VectorOfInt;
51 import org.scilab.modules.xcos.VectorOfScicosID;
52 import org.scilab.modules.xcos.VectorOfString;
53 import org.scilab.modules.xcos.Xcos;
54 import org.scilab.modules.xcos.XcosTab;
55 import org.scilab.modules.xcos.actions.EditFormatAction;
56 import org.scilab.modules.xcos.actions.ShowHideShadowAction;
57 import org.scilab.modules.xcos.block.actions.AutoPositionSplitBlockAction;
58 import org.scilab.modules.xcos.block.actions.BlockDocumentationAction;
59 import org.scilab.modules.xcos.block.actions.BlockParametersAction;
60 import org.scilab.modules.xcos.block.actions.BorderColorAction;
61 import org.scilab.modules.xcos.block.actions.FilledColorAction;
62 import org.scilab.modules.xcos.block.actions.FlipAction;
63 import org.scilab.modules.xcos.block.actions.MirrorAction;
64 import org.scilab.modules.xcos.block.actions.RegionToSuperblockAction;
65 import org.scilab.modules.xcos.block.actions.RotateAction;
66 import org.scilab.modules.xcos.block.actions.ViewDetailsAction;
67 import org.scilab.modules.xcos.block.actions.alignement.AlignBlockAction;
68 import org.scilab.modules.xcos.block.actions.alignement.AlignBlockActionBottom;
69 import org.scilab.modules.xcos.block.actions.alignement.AlignBlockActionCenter;
70 import org.scilab.modules.xcos.block.actions.alignement.AlignBlockActionLeft;
71 import org.scilab.modules.xcos.block.actions.alignement.AlignBlockActionMiddle;
72 import org.scilab.modules.xcos.block.actions.alignement.AlignBlockActionRight;
73 import org.scilab.modules.xcos.block.actions.alignement.AlignBlockActionTop;
74 import org.scilab.modules.xcos.graph.XcosDiagram;
75 import org.scilab.modules.xcos.graph.model.XcosCell;
76 import org.scilab.modules.xcos.port.command.CommandPort;
77 import org.scilab.modules.xcos.port.control.ControlPort;
78 import org.scilab.modules.xcos.port.input.InputPort;
79 import org.scilab.modules.xcos.port.output.OutputPort;
80 import org.scilab.modules.xcos.utils.BlockPositioning;
81 import org.scilab.modules.xcos.utils.XcosMessages;
82
83 import com.mxgraph.model.mxGeometry;
84 import com.mxgraph.model.mxICell;
85
86 /**
87  * A block on the diagram
88  */
89 @SuppressWarnings(value = { "serial" })
90 public class BasicBlock extends XcosCell implements Serializable {
91     /**
92      * Sorted kind of input, useful to sort them by kind
93      */
94     private static final Class<?>[] sortedChildrenClass = { InputPort.class, OutputPort.class, ControlPort.class, CommandPort.class, Object.class };
95
96     /*
97      * Default values
98      */
99
100     /**
101      * Default interface function name
102      */
103     public static final String DEFAULT_INTERFACE_FUNCTION = "xcos_block";
104     /**
105      * Default simulation function name
106      */
107     public static final String DEFAULT_SIMULATION_FUNCTION = "xcos_simulate";
108
109     /*
110      * Local constants
111      */
112
113     protected static final double DEFAULT_POSITION_X = 10.0;
114     protected static final double DEFAULT_POSITION_Y = 10.0;
115     private static final double DEFAULT_WIDTH = 40.0;
116     private static final double DEFAULT_HEIGHT = 40.0;
117
118     /**
119      * Internal method to get a base index to compare with depending on the cell type.
120      *
121      * @param cell
122      *            the cell
123      * @return the base index
124      */
125     private static int compareByChildClass(final Object o1, final Object o2) {
126         int o1Index = 0;
127         int o2Index = 0;
128
129         for (int i = 0; i < sortedChildrenClass.length; i++) {
130             final Class<?> klass = sortedChildrenClass[i];
131
132             if (klass.isInstance(o1)) {
133                 o1Index = i;
134                 break;
135             }
136         }
137         for (int i = 0; i < sortedChildrenClass.length; i++) {
138             final Class<?> klass = sortedChildrenClass[i];
139
140             if (klass.isInstance(o2)) {
141                 o2Index = i;
142                 break;
143             }
144         }
145
146         final int base = o1Index - o2Index;
147         return base * (Integer.MAX_VALUE / sortedChildrenClass.length);
148     }
149
150     private boolean locked;
151
152     /**
153      * Hook used to implement specifix behavior after any parameter update and before re-layouting the block
154      */
155     public void updateBlockView() {
156     }
157
158     /**
159      * Represent a simulation function type compatible with Scilab/Scicos function type descriptors.
160      */
161     public static enum SimulationFunctionType {
162         /** event select; reduced at compilation */
163         ESELECT(-2), /** if then else; reduced at compilation */
164         IFTHENELSE(-1), /** first common block */
165         DEFAULT(0), /** first native block */
166         TYPE_1(1), /** second native block */
167         TYPE_2(2), /** third native block */
168         TYPE_3(3), /** forth native block */
169         C_OR_FORTRAN(4), /** Scilab blocks */
170         SCILAB(5), /** Debug blocks */
171         DEBUG(99), /** dynamic {@link #TYPE_1} Fortran blocks (fortran_block.sci) */
172         DYNAMIC_FORTRAN_1(1001), /** dynamic {@link #TYPE_1} C blocks (c_block.sci) */
173         DYNAMIC_C_1(2001), /** Explicit dynamic {@link #TYPE_4} blocks (CBLOCK.sci) */
174         DYNAMIC_EXPLICIT_4(2004), /** Implicit {@link #TYPE_1} Fortran blocks (DIFF_f.sci) */
175         OLDBLOCKS(10001), /** Implicit {@link #C_OR_FORTRAN} blocks */
176         IMPLICIT_C_OR_FORTRAN(10004), /** Implicit dynamic {@link #TYPE_4} blocks (CBLOCK.sci) */
177         DYNAMIC_IMPLICIT_4(12004), /** Modelica {@link #C_OR_FORTRAN} blocks */
178         MODELICA(30004), /** Magic types */
179         UNKNOWN(5);
180
181         private final int value;
182
183         /**
184          * Default constructor
185          *
186          * @param scilabValue
187          *            Scilab/Scicos function type descriptor
188          */
189         private SimulationFunctionType(int scilabValue) {
190             value = scilabValue;
191         }
192
193         /**
194          * Get the Java descriptor from the Scilab descriptor.
195          *
196          * @param scilabValue
197          *            Scilab/Scicos function type descriptor
198          * @return The corresponding java descriptor
199          */
200         public static SimulationFunctionType convertScilabValue(int scilabValue) {
201             for (SimulationFunctionType iter : SimulationFunctionType.values()) {
202                 if (iter.getValue() == scilabValue) {
203                     return iter;
204                 }
205             }
206             return UNKNOWN;
207         }
208
209         /**
210          * Get the Scilab Descriptor from the Java Descriptor
211          *
212          * @return The corresponding Scilab/Scicos descriptor
213          */
214         public int getValue() {
215             return value;
216         }
217     };
218
219     /**
220      * Constructor that setup some properties and pass all its arguments this its {@link XcosCell} parent.
221      *
222      *
223      * @param controller The shared controller instance
224      * @param uid the uid of the MVC object
225      * @param kind {@link Kind#BLOCK} or {@link Kind#ANNOTATION}
226      * @param value the value of the block
227      * @param geometry the geometry to apply to this JGraphX object
228      * @param style the style to apply to this JGraphX object
229      * @param id the id to apply to this JGraphX object
230      */
231     public BasicBlock(final JavaController controller, long uid, Kind kind, Object value, mxGeometry geometry, String style, String id) {
232         super(controller, uid, kind, value, geometry, style, id);
233
234         /*
235          * Default JGraphX properties for blocks
236          */
237         this.visible = true;
238         this.vertex = true;
239         this.connectable = false;
240     }
241
242     /**
243      * @return locked status
244      */
245     public synchronized boolean isLocked() {
246         return locked;
247     }
248
249     /**
250      * @param locked
251      *            change locked status
252      */
253     public synchronized void setLocked(boolean locked) {
254         this.locked = locked;
255     }
256
257     /**
258      * Does the block update and register on the undo manager
259      * @param controller the shared controller
260      * @param parent the parent diagram or superblock diagram
261      * @param modifiedBlock the new settings
262      */
263     public void updateBlockSettings(JavaController controller, XcosDiagram parent, BasicBlock modifiedBlock) {
264         if (modifiedBlock == null) {
265             return;
266         }
267
268         /*
269          * Update the block settings
270          */
271         updateFields(controller, parent, modifiedBlock);
272
273         /*
274          * Update the children ports
275          */
276         updateChildren(controller, parent, modifiedBlock);
277     }
278
279     /**
280      * Update the instance field and the model values
281      *
282      * @param modifiedBlock
283      *            the modified instance
284      */
285     protected void updateFields(JavaController controller, XcosDiagram parent, BasicBlock modifiedBlock) {
286         if (modifiedBlock == null) {
287             return;
288         }
289
290         // TODO these copies are not managed by undo/redo ; fix that
291
292         int[] integer = new int[1];
293
294         controller.getObjectProperty(modifiedBlock.getUID(), modifiedBlock.getKind(), ObjectProperties.SIM_FUNCTION_API, integer);
295         controller.setObjectProperty(getUID(), getKind(), ObjectProperties.SIM_FUNCTION_API, integer[0]);
296
297         String[] str = new String[1];
298
299         controller.getObjectProperty(modifiedBlock.getUID(), modifiedBlock.getKind(), ObjectProperties.INTERFACE_FUNCTION, str);
300         controller.setObjectProperty(getUID(), getKind(), ObjectProperties.INTERFACE_FUNCTION, str[0]);
301
302         controller.getObjectProperty(modifiedBlock.getUID(), modifiedBlock.getKind(), ObjectProperties.SIM_FUNCTION_NAME, str);
303         controller.setObjectProperty(getUID(), getKind(), ObjectProperties.SIM_FUNCTION_NAME, str[0]);
304
305         controller.getObjectProperty(modifiedBlock.getUID(), modifiedBlock.getKind(), ObjectProperties.SIM_BLOCKTYPE, str);
306         controller.setObjectProperty(getUID(), getKind(), ObjectProperties.SIM_BLOCKTYPE, str[0]);
307
308         VectorOfDouble vDouble = new VectorOfDouble();
309
310         controller.getObjectProperty(modifiedBlock.getUID(), modifiedBlock.getKind(), ObjectProperties.EXPRS, vDouble);
311         controller.setObjectProperty(getUID(), getKind(), ObjectProperties.EXPRS, vDouble);
312
313         controller.getObjectProperty(modifiedBlock.getUID(), modifiedBlock.getKind(), ObjectProperties.STATE, vDouble);
314         controller.setObjectProperty(getUID(), getKind(), ObjectProperties.STATE, vDouble);
315
316         controller.getObjectProperty(modifiedBlock.getUID(), modifiedBlock.getKind(), ObjectProperties.DSTATE, vDouble);
317         controller.setObjectProperty(getUID(), getKind(), ObjectProperties.DSTATE, vDouble);
318
319         controller.getObjectProperty(modifiedBlock.getUID(), modifiedBlock.getKind(), ObjectProperties.ODSTATE, vDouble);
320         controller.setObjectProperty(getUID(), getKind(), ObjectProperties.ODSTATE, vDouble);
321
322         controller.getObjectProperty(modifiedBlock.getUID(), modifiedBlock.getKind(), ObjectProperties.RPAR, vDouble);
323         controller.setObjectProperty(getUID(), getKind(), ObjectProperties.RPAR, vDouble);
324
325         controller.getObjectProperty(modifiedBlock.getUID(), modifiedBlock.getKind(), ObjectProperties.OPAR, vDouble);
326         controller.setObjectProperty(getUID(), getKind(), ObjectProperties.OPAR, vDouble);
327
328         controller.getObjectProperty(modifiedBlock.getUID(), modifiedBlock.getKind(), ObjectProperties.EQUATIONS, vDouble);
329         controller.setObjectProperty(getUID(), getKind(), ObjectProperties.EQUATIONS, vDouble);
330
331         VectorOfInt vInt = new VectorOfInt();
332
333         controller.getObjectProperty(modifiedBlock.getUID(), modifiedBlock.getKind(), ObjectProperties.SIM_DEP_UT, vInt);
334         controller.setObjectProperty(getUID(), getKind(), ObjectProperties.SIM_DEP_UT, vInt);
335
336         controller.getObjectProperty(modifiedBlock.getUID(), modifiedBlock.getKind(), ObjectProperties.NZCROSS, vInt);
337         controller.setObjectProperty(getUID(), getKind(), ObjectProperties.NZCROSS, vInt);
338
339         controller.getObjectProperty(modifiedBlock.getUID(), modifiedBlock.getKind(), ObjectProperties.NMODE, vInt);
340         controller.setObjectProperty(getUID(), getKind(), ObjectProperties.NMODE, vInt);
341
342         controller.getObjectProperty(modifiedBlock.getUID(), modifiedBlock.getKind(), ObjectProperties.IPAR, vInt);
343         controller.setObjectProperty(getUID(), getKind(), ObjectProperties.IPAR, vInt);
344
345         controller.getObjectProperty(modifiedBlock.getUID(), modifiedBlock.getKind(), ObjectProperties.COLOR, vInt);
346         controller.setObjectProperty(getUID(), getKind(), ObjectProperties.COLOR, vInt);
347
348         VectorOfString vStr = new VectorOfString();
349
350         controller.getObjectProperty(modifiedBlock.getUID(), modifiedBlock.getKind(), ObjectProperties.DIAGRAM_CONTEXT, vStr);
351         controller.setObjectProperty(getUID(), getKind(), ObjectProperties.DIAGRAM_CONTEXT, vStr);
352
353         // move the children to the parent
354         VectorOfScicosID localChildren = new VectorOfScicosID();
355         controller.getObjectProperty(modifiedBlock.getUID(), modifiedBlock.getKind(), ObjectProperties.CHILDREN, localChildren);
356         final int size = localChildren.size();
357         for (int i = 0; i < size; i++) {
358             long child = localChildren.get(i);
359             controller.referenceObject(child);
360             controller.setObjectProperty(child, controller.getKind(child), ObjectProperties.PARENT_BLOCK, getUID());
361         }
362         controller.setObjectProperty(getUID(), getKind(), ObjectProperties.CHILDREN, localChildren);
363
364         /*
365          * JGraphX mapped properties
366          */
367         updateStyle(controller, parent, modifiedBlock);
368         updateValue(controller, parent, modifiedBlock);
369     }
370
371     /**
372      * Update the JGraphX style property accordingly to the block new style
373      *
374      * @param controller shared controller
375      * @param parent the diagram
376      * @param modifiedBlock a block copy containing the new style
377      */
378     protected void updateStyle(JavaController controller, XcosDiagram parent, BasicBlock modifiedBlock) {
379         parent.getModel().setStyle(this, modifiedBlock.getStyle());
380     }
381
382     /**
383      * Update the JGraphX style property accordingly to the block new value
384      *
385      * @param controller shared controller
386      * @param parent the diagram
387      * @param modifiedBlock a block copy containing the new value
388      */
389     protected void updateValue(JavaController controller, XcosDiagram parent, BasicBlock modifiedBlock) {
390         parent.getModel().setValue(this, modifiedBlock.getValue());
391     }
392
393     /**
394      * Update the children of the block.
395      *
396      * @param modifiedBlock
397      *            the new block instance
398      */
399     protected void updateChildren(JavaController controller, XcosDiagram parent, BasicBlock modifiedBlock) {
400         if (modifiedBlock == null) {
401             return;
402         }
403
404         /*
405          * Checked as port classes only
406          */
407         Set < Class <? extends mxICell >> types = new HashSet < > (Arrays.asList(InputPort.class, OutputPort.class, ControlPort.class, CommandPort.class));
408
409         Map < Class <? extends mxICell > , Deque<mxICell >> annotatedOlds = getTypedChildren(types);
410         Map < Class <? extends mxICell > , Deque<mxICell >> annotatedNews = modifiedBlock.getTypedChildren(types);
411
412         parent.getModel().beginUpdate();
413         try {
414             for (Class <? extends mxICell > klass : types) {
415                 final Deque<mxICell> olds = annotatedOlds.get(klass);
416                 final Deque<mxICell> news = annotatedNews.get(klass);
417
418                 // updated ports
419                 while (!olds.isEmpty() && !news.isEmpty()) {
420                     mxICell previous = olds.poll();
421                     mxICell modified = news.poll();
422
423                     final int previousIndex = children.indexOf(previous);
424
425                     // relink
426                     if (previous.getEdgeCount() != 0) {
427                         final mxICell relinked = previous.getEdgeAt(0);
428                         final boolean isOutgoing = previous == relinked.getTerminal(true);
429                         previous.removeEdge(relinked, isOutgoing);
430                         modified.insertEdge(relinked, isOutgoing);
431                     }
432
433                     parent.removeCells(new Object[] { previous }, false);
434                     parent.addCells(new Object[] { modified }, this, previousIndex);
435
436                     // Clone the geometry to avoid empty geometry on new cells.
437                     parent.getModel().setGeometry(modified, (mxGeometry) previous.getGeometry().clone());
438
439                 }
440
441                 // removed ports
442                 if (!olds.isEmpty()) {
443                     parent.removeCells(olds.toArray(), true);
444                 }
445
446                 // added ports
447                 if (!news.isEmpty()) {
448                     parent.addCells(news.toArray(), this);
449                 }
450             }
451         } finally {
452             parent.getModel().endUpdate();
453         }
454     }
455
456     /**
457      * Format the children as a typed map for the given class set.
458      *
459      * @param types
460      *            the classes to search for.
461      * @return a map which linked foreach type the corresponding cell list.
462      */
463     private Map < Class <? extends mxICell > , Deque<mxICell >> getTypedChildren(Set < Class <? extends mxICell >> types) {
464         Map < Class <? extends mxICell > , Deque<mxICell >> oldPorts = new HashMap < > ();
465
466         // Allocate all types set
467         for (Class <? extends mxICell > type : types) {
468             oldPorts.put(type, new LinkedList<>());
469         }
470
471         if (getChildCount() <= 0) {
472             return oldPorts;
473         }
474
475         // children lookup
476         for (Object cell : children) {
477
478             Class <? extends Object > klass = cell.getClass();
479             while (klass != null) {
480                 if (types.contains(klass)) {
481                     break;
482                 }
483                 klass = klass.getSuperclass();
484             }
485
486             final Deque<mxICell> current = oldPorts.get(klass);
487             if (current != null) {
488                 current.add((mxICell) cell);
489             }
490         }
491
492         return oldPorts;
493     }
494
495     /**
496      * Format the children as a typed map index for the given class set.
497      *
498      * @param types
499      *            the classes to search for.
500      * @return a map which linked foreach type the corresponding cell index in the children.
501      */
502     private Map<Class<? extends mxICell>, ArrayList<Integer>> getTypedChildrenIndexes(Set<Class<? extends mxICell>> types) {
503         Map<Class<? extends mxICell>, ArrayList<Integer>> oldPorts = new HashMap<Class<? extends mxICell>, ArrayList<Integer>>();
504
505         // Allocate all types set
506         for (Class<? extends mxICell> type : types) {
507             oldPorts.put(type, new ArrayList<>());
508         }
509
510         if (getChildCount() <= 0) {
511             return oldPorts;
512         }
513
514         // children lookup
515         for (int i = 0; i < children.size(); i++) {
516             final Object cell = children.get(i);
517
518             Class<? extends Object> klass = cell.getClass();
519             while (klass != null) {
520                 if (types.contains(klass)) {
521                     break;
522                 }
523                 klass = klass.getSuperclass();
524             }
525
526             final ArrayList<Integer> current = oldPorts.get(klass);
527             if (current != null) {
528                 current.add(i);
529             }
530         }
531
532         return oldPorts;
533     }
534
535     public ArrayList<Integer> getTypedChildrenIndexes(Class<? extends mxICell> type) {
536         return getTypedChildrenIndexes(Collections.singleton(type)).get(type);
537     }
538
539     public Map<Class<? extends mxICell>, ArrayList<Long>> getTypedChildrenUIDs(Set<Class<? extends mxICell>> types) {
540         Map<Class<? extends mxICell>, ArrayList<Long>> ports = new HashMap<>();
541
542         // Allocate all types set
543         types.stream().forEach(t -> ports.put(t, new ArrayList<>()));
544
545         if (getChildCount() <= 0) {
546             return ports;
547         }
548
549         // children lookup
550         for (int i = 0; i < children.size(); i++) {
551             final Object cell = children.get(i);
552
553             Class<? extends Object> klass = cell.getClass();
554             while (klass != null) {
555                 if (types.contains(klass)) {
556                     break;
557                 }
558                 klass = klass.getSuperclass();
559             }
560
561             final ArrayList<Long> current = ports.get(klass);
562             if (current != null) {
563                 if (cell instanceof XcosCell) {
564                     current.add(((XcosCell) cell).getUID());
565                 } else {
566                     current.add(0l);
567                 }
568             }
569         }
570
571         return ports;
572     }
573
574     public ArrayList<Long> getTypedChildrenUIDs(Class<? extends mxICell> type) {
575         return getTypedChildrenUIDs(Collections.singleton(type)).get(type);
576     }
577
578     /**
579      * @param graph
580      *            parent graph
581      */
582     public void openContextMenu(ScilabGraph graph) {
583         ContextMenu menu = null;
584         // if (getParentDiagram() instanceof PaletteDiagram) {
585         // menu = createPaletteContextMenu(graph);
586         // } else {
587         menu = createContextMenu(graph);
588         // }
589         menu.setVisible(true);
590     }
591
592     /**
593      * @param graph
594      *            parent graph
595      * @return context menu
596      */
597     // CSOFF: JavaNCSS
598     public ContextMenu createPaletteContextMenu(ScilabGraph graph) {
599         ContextMenu menu = ScilabContextMenu.createContextMenu();
600
601         final List<XcosDiagram> allDiagrams = Xcos.getInstance().openedDiagrams();
602
603         if (allDiagrams.size() == 0) {
604             // No diagram opened: should never happen if Xcos opens an empty
605             // diagram when it is launched
606             MenuItem addTo = ScilabMenuItem.createMenuItem();
607
608             addTo.setText(XcosMessages.ADDTO_NEW_DIAGRAM);
609             addTo.setCallback(new CommonCallBack(XcosMessages.ADDTO_NEW_DIAGRAM) {
610                 @Override
611                 public void callBack() {
612                     JavaController controller = new JavaController();
613                     BasicBlock block = null;
614                     try {
615                         block = (BasicBlock) BasicBlock.this.clone();
616                     } catch (CloneNotSupportedException e) {
617                         e.printStackTrace();
618                     }
619
620                     XcosDiagram theDiagram = new XcosDiagram(controller, controller.createObject(Kind.DIAGRAM), Kind.DIAGRAM, "");
621                     theDiagram.getModel().add(theDiagram.getDefaultParent(), block, 0);
622                     mxGeometry geom = BasicBlock.this.getGeometry();
623                     setDefaultPosition(geom);
624                     theDiagram.getModel().setGeometry(block, geom);
625
626                     XcosTab.get(theDiagram).setVisible(true);
627                     BlockPositioning.updateBlockView(theDiagram, block);
628                 }
629             });
630
631             menu.add(addTo);
632
633         } else if (allDiagrams.size() == 1) {
634             // A single diagram opened: add to this diagram
635             MenuItem addTo = ScilabMenuItem.createMenuItem();
636
637             addTo.setText(XcosMessages.ADDTO + " " + XcosTab.get(allDiagrams.get(0)).getName());
638             final XcosDiagram theDiagram = allDiagrams.get(0);
639             addTo.setCallback(new CommonCallBack(theDiagram.getTitle()) {
640                 private static final long serialVersionUID = -99601763227525686L;
641
642                 @Override
643                 public void callBack() {
644                     BasicBlock block = null;
645                     try {
646                         block = (BasicBlock) BasicBlock.this.clone();
647                     } catch (CloneNotSupportedException e) {
648                         e.printStackTrace();
649                     }
650                     theDiagram.getModel().add(theDiagram.getDefaultParent(), block, 0);
651                     mxGeometry geom = BasicBlock.this.getGeometry();
652                     setDefaultPosition(geom);
653                     theDiagram.getModel().setGeometry(block, geom);
654                     BlockPositioning.updateBlockView(theDiagram, block);
655                 }
656             });
657
658             menu.add(addTo);
659
660         } else {
661             // The user has to choose
662             Menu addTo = ScilabMenu.createMenu();
663
664             addTo.setText(XcosMessages.ADDTO);
665
666             for (int i = 0; i < allDiagrams.size(); i++) {
667                 MenuItem diagram = ScilabMenuItem.createMenuItem();
668                 final XcosDiagram theDiagram = allDiagrams.get(i);
669                 diagram.setText(XcosTab.get(allDiagrams.get(i)).getName());
670                 diagram.setCallback(new CommonCallBack(theDiagram.getTitle()) {
671                     private static final long serialVersionUID = 3345416658377835057L;
672
673                     @Override
674                     public void callBack() {
675                         BasicBlock block = null;
676                         try {
677                             block = (BasicBlock) BasicBlock.this.clone();
678                         } catch (CloneNotSupportedException e) {
679                             e.printStackTrace();
680                         }
681                         theDiagram.getModel().add(theDiagram.getDefaultParent(), block, 0);
682                         mxGeometry geom = BasicBlock.this.getGeometry();
683                         setDefaultPosition(geom);
684                         theDiagram.getModel().setGeometry(block, geom);
685                         BlockPositioning.updateBlockView(theDiagram, block);
686                     }
687                 });
688                 addTo.add(diagram);
689             }
690
691             menu.add(addTo);
692         }
693
694         menu.getAsSimpleContextMenu().addSeparator();
695
696         MenuItem help = ScilabMenuItem.createMenuItem();
697         help.setText(XcosMessages.BLOCK_DOCUMENTATION);
698         help.setCallback(new CommonCallBack(XcosMessages.BLOCK_DOCUMENTATION) {
699             private static final long serialVersionUID = -1480947262397441951L;
700
701             @Override
702             public void callBack() {
703                 JavaController controller = new JavaController();
704                 String[] interfaceFunctionName = new String[1];
705                 controller.getObjectProperty(getUID(), Kind.BLOCK, ObjectProperties.INTERFACE_FUNCTION, interfaceFunctionName);
706
707                 InterpreterManagement.requestScilabExec("help " + interfaceFunctionName[0]);
708             }
709         });
710         menu.add(help);
711
712         menu.setVisible(true);
713
714         ((SwingScilabContextMenu) menu.getAsSimpleContextMenu()).setLocation(MouseInfo.getPointerInfo().getLocation().x,
715                 MouseInfo.getPointerInfo().getLocation().y);
716
717         return menu;
718     }
719
720     // CSON: JavaNCSS
721
722     /**
723      * @param graph
724      *            parent graph
725      * @return context menu
726      */
727     // CSOFF: JavaNCSS
728     public ContextMenu createContextMenu(ScilabGraph graph) {
729         ContextMenu menu = ScilabContextMenu.createContextMenu();
730         Map<Class<? extends DefaultAction>, Menu> menuList = new HashMap<>();
731
732         MenuItem value = BlockParametersAction.createMenu(graph);
733         menuList.put(BlockParametersAction.class, value);
734         menu.add(value);
735         /*--- */
736         menu.getAsSimpleContextMenu().addSeparator();
737         /*--- */
738         value = CutAction.cutMenu(graph);
739         menuList.put(CutAction.class, value);
740         menu.add(value);
741         value = CopyAction.copyMenu(graph);
742         menuList.put(CopyAction.class, value);
743         menu.add(value);
744         value = DeleteAction.createMenu(graph);
745         menuList.put(DeleteAction.class, value);
746         menu.add(value);
747         /*--- */
748         menu.getAsSimpleContextMenu().addSeparator();
749         /*--- */
750         value = RegionToSuperblockAction.createMenu(graph);
751         menuList.put(RegionToSuperblockAction.class, value);
752         menu.add(value);
753         /*--- */
754         menu.getAsSimpleContextMenu().addSeparator();
755         /*--- */
756         Menu format = ScilabMenu.createMenu();
757         format.setText(XcosMessages.FORMAT);
758         menu.add(format);
759         value = RotateAction.createMenu(graph);
760         menuList.put(RotateAction.class, value);
761         format.add(value);
762         value = MirrorAction.createMenu(graph);
763         menuList.put(MirrorAction.class, value);
764         format.add(value);
765         value = FlipAction.createMenu(graph);
766         menuList.put(FlipAction.class, value);
767         format.add(value);
768         value = ShowHideShadowAction.createMenu(graph);
769         menuList.put(ShowHideShadowAction.class, value);
770         format.add(value);
771         /*--- */
772         format.addSeparator();
773         /*--- */
774         Menu alignMenu = ScilabMenu.createMenu();
775         alignMenu.setText(XcosMessages.ALIGN_BLOCKS);
776         alignMenu.add(AlignBlockActionLeft.createMenu(graph));
777         alignMenu.add(AlignBlockActionCenter.createMenu(graph));
778         alignMenu.add(AlignBlockActionRight.createMenu(graph));
779         alignMenu.addSeparator();
780         alignMenu.add(AlignBlockActionTop.createMenu(graph));
781         alignMenu.add(AlignBlockActionMiddle.createMenu(graph));
782         alignMenu.add(AlignBlockActionBottom.createMenu(graph));
783         menuList.put(AlignBlockAction.class, alignMenu);
784         format.add(alignMenu);
785         /*--- */
786         format.addSeparator();
787         /*--- */
788         MenuItem sbapMenuItem = AutoPositionSplitBlockAction.createMenu(graph);
789         sbapMenuItem.setText(XcosMessages.BLOCK_AUTO_POSITION_SPLIT_BLOCK_CONTEXTUAL);
790         sbapMenuItem.setEnabled(false);
791         menuList.put(AutoPositionSplitBlockAction.class, sbapMenuItem);
792         format.add(sbapMenuItem);
793         /*--- */
794         format.addSeparator();
795         /*--- */
796         if (graph.getSelectionCells().length > 1) {
797             format.add(BorderColorAction.createMenu(graph));
798             format.add(FilledColorAction.createMenu(graph));
799         } else {
800             format.add(EditFormatAction.createMenu(graph));
801         }
802         /*--- */
803         menu.getAsSimpleContextMenu().addSeparator();
804         /*--- */
805         menu.add(ViewDetailsAction.createMenu(graph));
806         /*--- */
807         menu.getAsSimpleContextMenu().addSeparator();
808         /*--- */
809         menu.add(BlockDocumentationAction.createMenu(graph));
810
811         ((SwingScilabContextMenu) menu.getAsSimpleContextMenu()).setLocation(MouseInfo.getPointerInfo().getLocation().x,
812                 MouseInfo.getPointerInfo().getLocation().y);
813
814         customizeMenu(menuList);
815
816         return menu;
817     }
818
819     /**
820      * Override this to customize contextual menu
821      *
822      * @param menuList
823      *            list of menu
824      */
825     protected void customizeMenu(Map<Class<? extends DefaultAction>, Menu> menuList) {
826         // To be overridden by sub-classes
827     }
828
829     /**
830      * Set the default block position on the geom
831      *
832      * @param geom
833      *            the current geom
834      */
835     private void setDefaultPosition(mxGeometry geom) {
836         geom.setX(DEFAULT_POSITION_X);
837         geom.setY(DEFAULT_POSITION_Y);
838     }
839
840     /*
841      * Overriden methods from jgraphx
842      */
843
844     /**
845      * @return always false
846      * @see com.mxgraph.model.mxCell#isConnectable()
847      */
848     @Override
849     public boolean isConnectable() {
850         return false;
851     }
852
853     /**
854      * {@inheritDoc}
855      *
856      * Sync the specific child {@link EditFormatAction#HASH_IDENTIFIER}
857      */
858     @Override
859     public mxICell insert(mxICell child, int index) {
860         /*
861          * Update the id if this is an identifier cell (herited identifier)
862          */
863         if (child.getId().endsWith(XcosDiagram.HASH_IDENTIFIER)) {
864             child.setId(getId() + XcosDiagram.HASH_IDENTIFIER);
865         }
866
867         return super.insert(child, index);
868     }
869 }