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