fc650f8ad62a9ddf596aaa0045d647cb169d1b60
[scilab.git] / scilab / modules / xcos / src / java / org / scilab / modules / xcos / graph / XcosDiagram.java
1 /*
2  * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3  * Copyright (C) 2009-2009 - DIGITEO - Bruno JOFRET
4  * Copyright (C) 2009-2010 - DIGITEO - Clement DAVID
5  *
6  * This file must be used under the terms of the CeCILL.
7  * This source file is licensed as described in the file COPYING, which
8  * you should have received as part of this distribution.  The terms
9  * are also available at
10  * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt
11  *
12  */
13
14 package org.scilab.modules.xcos.graph;
15
16 import java.awt.GraphicsEnvironment;
17 import java.awt.event.ActionEvent;
18 import java.awt.event.ActionListener;
19 import java.io.File;
20 import java.io.IOException;
21 import java.util.ArrayList;
22 import java.util.Arrays;
23 import java.util.Collection;
24 import java.util.Collections;
25 import java.util.Comparator;
26 import java.util.HashSet;
27 import java.util.Hashtable;
28 import java.util.IllegalFormatException;
29 import java.util.LinkedList;
30 import java.util.List;
31 import java.util.Map;
32 import java.util.Queue;
33 import java.util.Set;
34 import java.util.logging.Logger;
35
36 import javax.swing.JFileChooser;
37 import javax.swing.JOptionPane;
38 import javax.swing.SwingWorker;
39 import javax.swing.Timer;
40
41 import org.scilab.modules.action_binding.highlevel.ScilabInterpreterManagement;
42 import org.scilab.modules.action_binding.highlevel.ScilabInterpreterManagement.InterpreterException;
43 import org.scilab.modules.graph.ScilabGraph;
44 import org.scilab.modules.graph.utils.ScilabGraphConstants;
45 import org.scilab.modules.gui.bridge.filechooser.SwingScilabFileChooser;
46 import org.scilab.modules.gui.bridge.tab.SwingScilabDockablePanel;
47 import org.scilab.modules.gui.messagebox.ScilabModalDialog;
48 import org.scilab.modules.gui.messagebox.ScilabModalDialog.AnswerOption;
49 import org.scilab.modules.gui.messagebox.ScilabModalDialog.ButtonType;
50 import org.scilab.modules.gui.messagebox.ScilabModalDialog.IconType;
51 import org.scilab.modules.gui.tabfactory.ScilabTabFactory;
52 import org.scilab.modules.xcos.JavaController;
53 import org.scilab.modules.xcos.Kind;
54 import org.scilab.modules.xcos.ObjectProperties;
55 import org.scilab.modules.xcos.VectorOfDouble;
56 import org.scilab.modules.xcos.VectorOfInt;
57 import org.scilab.modules.xcos.VectorOfString;
58 import org.scilab.modules.xcos.Xcos;
59 import org.scilab.modules.xcos.XcosTab;
60 import org.scilab.modules.xcos.actions.SaveAsAction;
61 import org.scilab.modules.xcos.block.AfficheBlock;
62 import org.scilab.modules.xcos.block.BasicBlock;
63 import org.scilab.modules.xcos.block.SplitBlock;
64 import org.scilab.modules.xcos.block.TextBlock;
65 import org.scilab.modules.xcos.block.io.EventInBlock;
66 import org.scilab.modules.xcos.block.io.EventOutBlock;
67 import org.scilab.modules.xcos.block.io.ExplicitInBlock;
68 import org.scilab.modules.xcos.block.io.ExplicitOutBlock;
69 import org.scilab.modules.xcos.block.io.ImplicitInBlock;
70 import org.scilab.modules.xcos.block.io.ImplicitOutBlock;
71 import org.scilab.modules.xcos.configuration.ConfigurationManager;
72 import org.scilab.modules.xcos.graph.model.BlockInterFunction;
73 import org.scilab.modules.xcos.graph.model.ScicosObjectOwner;
74 import org.scilab.modules.xcos.graph.model.XcosCell;
75 import org.scilab.modules.xcos.graph.model.XcosCellFactory;
76 import org.scilab.modules.xcos.graph.swing.GraphComponent;
77 import org.scilab.modules.xcos.io.XcosFileType;
78 import org.scilab.modules.xcos.io.scicos.ScilabDirectHandler;
79 import org.scilab.modules.xcos.link.BasicLink;
80 import org.scilab.modules.xcos.link.CommandControlLink;
81 import org.scilab.modules.xcos.link.ExplicitLink;
82 import org.scilab.modules.xcos.link.ImplicitLink;
83 import org.scilab.modules.xcos.port.BasicPort;
84 import org.scilab.modules.xcos.port.BasicPort.Type;
85 import org.scilab.modules.xcos.port.Orientation;
86 import org.scilab.modules.xcos.port.PortCheck;
87 import org.scilab.modules.xcos.port.command.CommandPort;
88 import org.scilab.modules.xcos.port.control.ControlPort;
89 import org.scilab.modules.xcos.port.input.ExplicitInputPort;
90 import org.scilab.modules.xcos.port.input.ImplicitInputPort;
91 import org.scilab.modules.xcos.port.output.ExplicitOutputPort;
92 import org.scilab.modules.xcos.port.output.ImplicitOutputPort;
93 import org.scilab.modules.xcos.preferences.XcosOptions;
94 import org.scilab.modules.xcos.utils.BlockPositioning;
95 import org.scilab.modules.xcos.utils.Stack;
96 import org.scilab.modules.xcos.utils.XcosConstants;
97 import org.scilab.modules.xcos.utils.XcosDialogs;
98 import org.scilab.modules.xcos.utils.XcosMessages;
99
100 import com.mxgraph.model.mxCell;
101 import com.mxgraph.model.mxGeometry;
102 import com.mxgraph.model.mxGraphModel;
103 import com.mxgraph.model.mxICell;
104 import com.mxgraph.util.mxEvent;
105 import com.mxgraph.util.mxEventObject;
106 import com.mxgraph.util.mxPoint;
107 import com.mxgraph.util.mxRectangle;
108 import com.mxgraph.util.mxUndoableEdit;
109 import com.mxgraph.util.mxUndoableEdit.mxUndoableChange;
110 import com.mxgraph.view.mxGraphSelectionModel;
111 import com.mxgraph.view.mxMultiplicity;
112 import java.lang.reflect.Constructor;
113 import org.scilab.modules.types.ScilabString;
114 import org.scilab.modules.types.ScilabType;
115 import org.scilab.modules.xcos.io.ScilabTypeCoder;
116
117 /**
118  * The base class for a diagram. This class contains jgraphx + Scicos data.
119  */
120 public class XcosDiagram extends ScilabGraph {
121     private static final Logger LOG = Logger.getLogger(XcosDiagram.class.getName());
122
123     private static final String MODIFIED = "modified";
124     private static final String CELLS = "cells";
125     public static final String IN = "in";
126     public static final String OUT = "out";
127     public static final String EIN = "ein";
128     public static final String EOUT = "eout";
129
130     /**
131      * Prefix used to tag text node.
132      */
133     public static final String HASH_IDENTIFIER = "#identifier";
134
135     /**
136      * Default geometry used while adding a label to a block (on the middle and below the bottom of the parent block)
137      */
138     private static final mxGeometry DEFAULT_LABEL_GEOMETRY = new mxGeometry(0.5, 1.1, 0.0, 0.0);
139
140     /**
141      * Constructor
142      *
143      * @param withVisibleFeatures
144      *            true if the visible features should be activated, false otherwise. Disable it on encode/decode leads to a huge performance gain.
145      */
146     public XcosDiagram(final long diagramId, final Kind kind) {
147         super(new mxGraphModel(), Xcos.getInstance().getStyleSheet());
148
149         // add the default parent (the JGraphX layer)
150         XcosCell parent = new XcosCell(diagramId, kind);
151         new JavaController().referenceObject(diagramId);
152         ((mxICell) getModel().getRoot()).insert(parent);
153         setDefaultParent(parent);
154
155         setComponent(new GraphComponent(this));
156         initComponent();
157
158         // Forbid disconnecting cells once it is connected.
159         setCellsDisconnectable(false);
160
161         // Forbid pending edges.
162         setAllowDanglingEdges(false);
163
164         // Cannot connect port to itself.
165         setAllowLoops(false);
166
167         // Override isCellResizable to filter what the user can resize
168         setCellsResizable(true);
169
170         /* Labels use HTML if not equal to interface function name */
171         setHtmlLabels(true);
172         /*
173          * by default every label is movable, see XcosDiagram##isLabelMovable(java.lang.Object) for restrictions
174          */
175         setVertexLabelsMovable(true);
176         setEdgeLabelsMovable(true);
177
178         //
179         setCloneInvalidEdges(true);
180
181         // Override isCellEditable to filter what the user can edit
182         setCellsEditable(true);
183
184         setConnectableEdges(true);
185
186         // Do not clear edge points on connect
187         setResetEdgesOnConnect(false);
188
189         setMultiplicities();
190
191         setAutoOrigin(true);
192     }
193
194     /*
195      * Static helpers
196      */
197
198     /**
199      * Only return the instanceof klass
200      *
201      * @param selection
202      *            the selection to filter out
203      * @param klass
204      *            the class selector
205      * @return the selection with only klass instance.
206      */
207     public static Object[] filterByClass(final Object[] selection, final Class<BasicBlock> klass) {
208         return mxGraphModel.filterCells(selection, new mxGraphModel.Filter() {
209             @Override
210             public boolean filter(Object cell) {
211                 return klass.isInstance(cell);
212             }
213         });
214     }
215
216     /**
217      * Fill the hierarchy from the first element up to the root diagram (included)
218      * <p>
219      * Should be used as :
220      * <pre>
221      *  hierarchy = fillHierarchy(new ScicosObjectOwner(getUID(), getKind()))
222      * </pre>
223      * @param hierarchy the collection to fill
224      * @return the filled collection (the root at the end)
225      */
226     public static Stack<ScicosObjectOwner> lookForHierarchy(ScicosObjectOwner current) {
227         ScicosObjectOwner local = current;
228         Stack<ScicosObjectOwner> hierarchy = new Stack<>();
229         JavaController controller = new JavaController();
230
231         long[] parent = new long[] {local.getUID()};
232         if (local.getKind() == Kind.DIAGRAM) {
233             hierarchy.push(local);
234             return hierarchy;
235         }
236
237         while (parent[0] != 0l) {
238             hierarchy.push(new ScicosObjectOwner(parent[0], Kind.BLOCK));
239             controller.getObjectProperty(local.getUID(), local.getKind(), ObjectProperties.PARENT_BLOCK, parent);
240         }
241
242         controller.getObjectProperty(local.getUID(), local.getKind(), ObjectProperties.PARENT_DIAGRAM, parent);
243         hierarchy.push(new ScicosObjectOwner(parent[0], Kind.DIAGRAM));
244
245         return hierarchy;
246     }
247
248     /**
249      * Sort the blocks per first integer parameter value
250      *
251      * @param blocks
252      *            the block list
253      * @return the sorted block list (same instance)
254      */
255     public List<? extends BasicBlock> iparSort(final List<? extends BasicBlock> blocks) {
256         Collections.sort(blocks, new Comparator<BasicBlock>() {
257             @Override
258             public int compare(BasicBlock o1, BasicBlock o2) {
259
260                 JavaController controller = new JavaController();
261
262                 final VectorOfInt data1 = new VectorOfInt();
263                 final VectorOfInt data2 = new VectorOfInt();
264
265                 controller.getObjectProperty(o1.getUID(), Kind.BLOCK, ObjectProperties.IPAR, data1);
266                 controller.getObjectProperty(o2.getUID(), Kind.BLOCK, ObjectProperties.IPAR, data2);
267
268                 final int value1;
269                 if (data1.size() >= 1) {
270                     value1 = data1.get(0);
271                 } else {
272                     value1 = 0;
273                 }
274
275                 final int value2;
276                 if (data2.size() >= 1) {
277                     value2 = data2.get(0);
278                 } else {
279                     value2 = 0;
280                 }
281
282                 return value1 - value2;
283             }
284         });
285         return blocks;
286     }
287
288     /**
289      * @param <T>
290      *            The type to work on
291      * @param klass
292      *            the class instance to work on
293      * @return list of typed block
294      */
295     @SuppressWarnings("unchecked")
296     private <T extends BasicBlock> List<T> getAllTypedBlock(Class<T> klass) {
297         final List<T> list = new ArrayList<T>();
298
299         int blockCount = getModel().getChildCount(getDefaultParent());
300
301         for (int i = 0; i < blockCount; i++) {
302             Object cell = getModel().getChildAt(getDefaultParent(), i);
303             if (klass.isInstance(cell)) {
304                 // According to the test we are sure that the cell is an
305                 // instance of T. Thus we can safely cast it.
306                 list.add((T) cell);
307             }
308         }
309         return list;
310     }
311
312     /**
313      * @param <T>
314      * @param <T>
315      *            The type to work on
316      * @param klasses
317      *            the class instance list to work on
318      * @return list of typed block
319      */
320     private <T extends BasicBlock> List<T> getAllTypedBlock(Class<T>[] klasses) {
321         final List<T> list = new ArrayList<T>();
322         for (Class<T> klass : klasses) {
323             list.addAll(getAllTypedBlock(klass));
324         }
325         return list;
326     }
327
328     /**
329      * Fill the context with I/O port
330      *
331      * @param context
332      *            the context to fill
333      */
334     @SuppressWarnings("unchecked")
335     protected void fillContext(final Hashtable<Object, Object> context) {
336         if (!context.containsKey(IN)) {
337             context.put(IN, iparSort(getAllTypedBlock(new Class[] { ExplicitInBlock.class, ImplicitInBlock.class })));
338         }
339         if (!context.containsKey(OUT)) {
340             context.put(OUT, iparSort(getAllTypedBlock(new Class[] { ExplicitOutBlock.class, ImplicitOutBlock.class })));
341         }
342         if (!context.containsKey(EIN)) {
343             context.put(EIN, iparSort(getAllTypedBlock(new Class[] { EventInBlock.class })));
344         }
345         if (!context.containsKey(EOUT)) {
346             context.put(EOUT, iparSort(getAllTypedBlock(new Class[] { EventOutBlock.class })));
347         }
348     }
349
350     @Override
351     public String validateCell(final Object cell, final Hashtable<Object, Object> context) {
352         if (getKind() == Kind.BLOCK) {
353             return validateChildDiagram(cell, context);
354         } else {
355             // does not perform any validation on a root diagram
356             return null;
357         }
358     }
359
360     /**
361      * Validate I/O ports.
362      *
363      * /!\ No model modification should be made in this method, this is only a validation method.
364      *
365      * @param cell
366      *            Cell that represents the cell to validate.
367      * @param context
368      *            Hashtable that represents the global validation state.
369      */
370     public String validateChildDiagram(final Object cell, final Hashtable<Object, Object> context) {
371         String err = null;
372
373         /*
374          * Only validate I/O blocks
375          */
376
377         // get the key
378         final String key;
379         if (cell instanceof ExplicitInBlock || cell instanceof ImplicitInBlock) {
380             key = IN;
381         } else if (cell instanceof ExplicitOutBlock || cell instanceof ImplicitOutBlock) {
382             key = OUT;
383         } else if (cell instanceof EventInBlock) {
384             key = EIN;
385         } else if (cell instanceof EventOutBlock) {
386             key = EOUT;
387         } else {
388             return null;
389         }
390         final BasicBlock block = (BasicBlock) cell;
391
392         /*
393          * Prepare validation
394          */
395
396         // fill the context once
397         fillContext(context);
398
399         /*
400          * Validate with ipar
401          */
402
403         // get the real index
404         final List<? extends BasicBlock> blocks = (List<? extends BasicBlock>) context.get(key);
405         final int realIndex = blocks.indexOf(block) + 1;
406
407         // get the user index
408         JavaController controller = new JavaController();
409         VectorOfInt ipar = new VectorOfInt();
410         controller.getObjectProperty(realIndex, Kind.BLOCK, ObjectProperties.IPAR, ipar);
411         if (ipar.size() > 1) {
412             return err;
413         }
414         final int userIndex = ipar.get(0);
415
416         // if the indexes are not equals, alert the user.
417         if (realIndex != userIndex) {
418             final StringBuilder str = new StringBuilder();
419             str.append("<html><body><em>");
420             str.append(XcosMessages.WRONG_PORT_NUMBER);
421             str.append("</em><br/>");
422             str.append(String.format(XcosMessages.EXPECTING_NUMBER, realIndex));
423             str.append("</body></html>    ");
424
425             err = str.toString();
426         }
427
428         return err;
429     }
430
431     /*
432      * Static diagram listeners
433      */
434
435     /**
436      * CellResizedTracker Called when mxEvents.CELLS_RESIZED is fired.
437      */
438     private static final class CellResizedTracker implements mxIEventListener {
439
440         private static CellResizedTracker instance;
441
442         /**
443          * Constructor
444          */
445         private CellResizedTracker() {
446         }
447
448         /**
449          * @return the instance
450          */
451         public static CellResizedTracker getInstance() {
452             if (instance == null) {
453                 instance = new CellResizedTracker();
454             }
455             return instance;
456         }
457
458         /**
459          * Update the cell view
460          *
461          * @param source
462          *            the source instance
463          * @param evt
464          *            the event data
465          * @see com.mxgraph.util.mxEventSource.mxIEventListener#invoke(java.lang.Object, com.mxgraph.util.mxEventObject)
466          */
467         @Override
468         public void invoke(final Object source, final mxEventObject evt) {
469             final XcosDiagram diagram = (XcosDiagram) source;
470             final Object[] cells = (Object[]) evt.getProperty(CELLS);
471
472             diagram.getModel().beginUpdate();
473             try {
474                 for (int i = 0; i < cells.length; ++i) {
475                     if (cells[i] instanceof BasicBlock) {
476                         BlockPositioning.updateBlockView(diagram, (BasicBlock) cells[i]);
477                     }
478                 }
479             } finally {
480                 diagram.getModel().endUpdate();
481             }
482         }
483     }
484
485     /**
486      * Update the modified block on undo/redo
487      */
488     private static final class UndoUpdateTracker implements mxIEventListener {
489         private static UndoUpdateTracker instance;
490
491         /**
492          * Constructor
493          */
494         public UndoUpdateTracker() {
495         }
496
497         /**
498          * @return the instance
499          */
500         public static UndoUpdateTracker getInstance() {
501             if (instance == null) {
502                 instance = new UndoUpdateTracker();
503             }
504             return instance;
505         }
506
507         /**
508          * Update the block and style on undo
509          *
510          * @param source
511          *            the source instance
512          * @param evt
513          *            the event data
514          * @see com.mxgraph.util.mxEventSource.mxIEventListener#invoke(java.lang.Object, com.mxgraph.util.mxEventObject)
515          */
516         @Override
517         public void invoke(final Object source, final mxEventObject evt) {
518             final mxUndoableEdit edit = (mxUndoableEdit) evt.getProperty(ScilabGraphConstants.EVENT_CHANGE_EDIT);
519
520             final mxGraphModel model = (mxGraphModel) edit.getSource();
521             final List<mxUndoableChange> changes = edit.getChanges();
522
523             final Object[] changedCells = getSelectionCellsForChanges(changes, model);
524             model.beginUpdate();
525             try {
526                 // FIXME manage that for the MVC
527                 // for (final Object object : changedCells) {
528                 // if (object instanceof BasicBlock) {
529                 // final BasicBlock current = (BasicBlock) object;
530                 // final XcosDiagram graph = current.getParentDiagram();
531                 //
532                 // // When we change the style property we have to update
533                 // // some BasiBlock fields
534                 // if (changes.get(0) instanceof mxStyleChange) {
535                 // current.updateFieldsFromStyle();
536                 // }
537                 //
538                 // // update the superblock container ports if the block is
539                 // // inside a superblock diagram
540                 // if (graph instanceof SuperBlockDiagram) {
541                 // SuperBlockDiagram superdiagram = (SuperBlockDiagram) current.getParentDiagram();
542                 // SuperBlock superblock = superdiagram.getContainer();
543                 // superblock.updateExportedPort();
544                 // }
545                 //
546                 // // Update the block position
547                 // BlockPositioning.updateBlockView(current);
548                 //
549                 // // force a refresh of the block ports and links
550                 // // connected to these ports
551                 // final int childCount = model.getChildCount(current);
552                 // for (int i = 0; i < childCount; i++) {
553                 // final Object port = model.getChildAt(current, i);
554                 // graph.getView().clear(port, true, true);
555                 // final int edgeCount = model.getEdgeCount(port);
556                 // for (int j = 0; j < edgeCount; j++) {
557                 // final Object edge = model.getEdgeAt(port, j);
558                 // graph.getView().clear(edge, true, true);
559                 // }
560                 // }
561                 // // force a refresh of the block
562                 // graph.getView().clear(current, true, true);
563                 //
564                 // graph.getView().validate();
565                 // graph.repaint();
566                 // }
567                 // }
568             } finally {
569                 model.endUpdate();
570             }
571         }
572     }
573
574     /**
575      * Refresh each block on modification (update port position, etc...)
576      */
577     private static final class RefreshBlockTracker implements mxIEventListener {
578         private static RefreshBlockTracker instance;
579
580         /**
581          * Default constructor
582          */
583         private RefreshBlockTracker() {
584         }
585
586         /**
587          * @return the instance
588          */
589         public static RefreshBlockTracker getInstance() {
590             if (instance == null) {
591                 instance = new RefreshBlockTracker();
592             }
593             return instance;
594         }
595
596         /**
597          * Refresh the block on port added
598          *
599          * @param sender
600          *            the diagram
601          * @param evt
602          *            the event
603          * @see com.mxgraph.util.mxEventSource.mxIEventListener#invoke(java.lang.Object, com.mxgraph.util.mxEventObject)
604          */
605         @Override
606         public void invoke(Object sender, mxEventObject evt) {
607             final XcosDiagram diagram = (XcosDiagram) sender;
608
609             diagram.getModel().beginUpdate();
610             try {
611                 final BasicBlock updatedBlock = (BasicBlock) evt.getProperty(XcosConstants.EVENT_BLOCK_UPDATED);
612                 BlockPositioning.updateBlockView(diagram, updatedBlock);
613
614                 diagram.getView().clear(updatedBlock, true, true);
615
616                 // validate display errors
617                 diagram.getAsComponent().clearCellOverlays();
618                 diagram.getAsComponent().validateGraph();
619
620                 diagram.getView().validate();
621             } finally {
622                 diagram.getModel().endUpdate();
623             }
624         }
625     }
626
627     /**
628      * Hook method that creates the new edge for insertEdge. This implementation does not set the source and target of the edge, these are set when the edge is added to the model.
629      *
630      * @param parent
631      *            Cell that specifies the parent of the new edge.
632      * @param id
633      *            Optional string that defines the Id of the new edge.
634      * @param value
635      *            Object to be used as the user object.
636      * @param source
637      *            Cell that defines the source of the edge.
638      * @param target
639      *            Cell that defines the target of the edge.
640      * @param style
641      *            Optional string that defines the cell style.
642      * @return Returns the new edge to be inserted.
643      * @see com.mxgraph.view.mxGraph#createEdge(java.lang.Object, java.lang.String, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.String)
644      */
645     @Override
646     public Object createEdge(Object parent, String id, Object value, Object source, Object target, String style) {
647         Object ret = null;
648         JavaController controller = new JavaController();
649
650         if (source instanceof BasicPort) {
651             BasicPort src = (BasicPort) source;
652             BasicLink link = null;
653
654             long uid = controller.createObject(Kind.LINK);
655             if (src.getType() == Type.EXPLICIT) {
656                 link = new ExplicitLink(uid);
657             } else if (src.getType() == Type.IMPLICIT) {
658                 link = new ImplicitLink(uid);
659             } else {
660                 link = new CommandControlLink(uid);
661             }
662
663             // allocate the associated geometry
664             link.setGeometry(new mxGeometry());
665             ret = link;
666         } else if (source instanceof SplitBlock) {
667             SplitBlock src = (SplitBlock) source;
668             return createEdge(parent, id, value, src.getIn(), target, style);
669         } else if (source instanceof BasicLink) {
670             BasicLink src = (BasicLink) source;
671             BasicLink link = null;
672
673             try {
674                 Class<? extends BasicLink> klass = src.getClass();
675                 Constructor<? extends BasicLink> cstr = klass.getConstructor(Long.TYPE);
676                 link = cstr.newInstance(controller.createObject(Kind.LINK));
677
678                 // allocate the associated geometry
679                 link.setGeometry(new mxGeometry());
680             } catch (ReflectiveOperationException e) {
681                 LOG.severe(e.toString());
682             }
683
684             ret = link;
685         }
686
687         if (ret == null) {
688             ret = super.createEdge(parent, id, value, source, target, style);
689             LOG.warning("Creating a non typed edge");
690         }
691
692         return ret;
693     }
694
695     /**
696      * Add an edge from a source to the target.
697      *
698      * @param cell
699      *            the edge to add (may be null)
700      * @param parent
701      *            the parent of the source and the target
702      * @param source
703      *            the source cell
704      * @param target
705      *            the target cell
706      * @param index
707      *            the index of the edge
708      * @return the added edge or null.
709      * @see com.mxgraph.view.mxGraph#addEdge(java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Integer)
710      */
711     @Override
712     public Object addCell(Object cell, Object parent, Integer index, Object source, Object target) {
713
714         // already connected edge or normal block
715         if (source == null && target == null) {
716             return super.addCell(cell, parent, index, source, target);
717         }
718
719         // Command -> Control
720         if (source instanceof CommandPort && target instanceof ControlPort && cell instanceof CommandControlLink) {
721             return super.addCell(cell, parent, index, source, target);
722         }
723
724         // Control -> Command
725         // Switch source and target !
726         if (target instanceof CommandPort && source instanceof ControlPort && cell instanceof CommandControlLink) {
727             BasicLink current = (BasicLink) cell;
728             current.invertDirection();
729
730             return super.addCell(cell, parent, index, target, source);
731         }
732
733         // ExplicitOutput -> ExplicitInput
734         if (source instanceof ExplicitOutputPort && target instanceof ExplicitInputPort && cell instanceof ExplicitLink) {
735             return super.addCell(cell, parent, index, source, target);
736         }
737         // ExplicitInput -> ExplicitOutput
738         // Switch source and target !
739         if (target instanceof ExplicitOutputPort && source instanceof ExplicitInputPort && cell instanceof ExplicitLink) {
740             BasicLink current = (BasicLink) cell;
741             current.invertDirection();
742
743             return super.addCell(cell, parent, index, target, source);
744         }
745
746         // ImplicitOutput -> ImplicitInput
747         if (source instanceof ImplicitOutputPort && target instanceof ImplicitInputPort && cell instanceof ImplicitLink) {
748             return super.addCell(cell, parent, index, source, target);
749         }
750         // ImplicitInput -> ImplicitOutput
751         // Switch source and target !
752         if (target instanceof ImplicitOutputPort && source instanceof ImplicitInputPort && cell instanceof ImplicitLink) {
753             BasicLink current = (BasicLink) cell;
754             current.invertDirection();
755
756             return super.addCell(cell, parent, index, target, source);
757         }
758
759         // ImplicitInput -> ImplicitInput
760         if (source instanceof ImplicitInputPort && target instanceof ImplicitInputPort && cell instanceof ImplicitLink) {
761             return super.addCell(cell, parent, index, source, target);
762         }
763         // ImplicitOutputPort -> ImplicitOutput
764         // Switch source and target !
765         if (target instanceof ImplicitOutputPort && source instanceof ImplicitOutputPort && cell instanceof ImplicitLink) {
766             BasicLink current = (BasicLink) cell;
767             current.invertDirection();
768
769             return super.addCell(cell, parent, index, target, source);
770         }
771
772         /*
773          * Split management
774          */
775
776         // ExplicitLink -> ExplicitInputPort
777         if (source instanceof ExplicitLink && target instanceof ExplicitInputPort && cell instanceof ExplicitLink) {
778             SplitBlock split = addSplitEdge(((BasicLink) cell).getGeometry().getSourcePoint(), (BasicLink) source);
779             return addCell(cell, parent, index, split.getOut2(), target);
780         }
781         // ExplicitOutput -> ExpliciLink
782         // Switch source and target !
783         if (target instanceof ExplicitLink && source instanceof ExplicitInputPort && cell instanceof ExplicitLink) {
784             final BasicLink current = (BasicLink) cell;
785             final SplitBlock split = addSplitEdge(current.getGeometry().getTargetPoint(), (BasicLink) target);
786
787             current.invertDirection();
788
789             return addCell(cell, parent, index, split.getOut2(), source);
790         }
791
792         // ImplicitLink -> ImplicitInputPort
793         if (source instanceof ImplicitLink && target instanceof ImplicitInputPort && cell instanceof ImplicitLink) {
794             SplitBlock split = addSplitEdge(((BasicLink) cell).getGeometry().getSourcePoint(), (BasicLink) source);
795             return addCell(cell, parent, index, split.getOut2(), target);
796         }
797         // ImplicitInputPort -> ImplicitLink
798         // Switch source and target !
799         if (target instanceof ImplicitLink && source instanceof ImplicitInputPort && cell instanceof ImplicitLink) {
800             final BasicLink current = (BasicLink) cell;
801             final SplitBlock split = addSplitEdge(current.getGeometry().getTargetPoint(), (BasicLink) target);
802
803             current.invertDirection();
804
805             return addCell(cell, parent, index, split.getOut2(), source);
806         }
807
808         // ImplicitLink -> ImplicitOutputPort
809         if (source instanceof ImplicitLink && target instanceof ImplicitOutputPort && cell instanceof ImplicitLink) {
810             final BasicLink current = (BasicLink) cell;
811             final SplitBlock split = addSplitEdge(current.getGeometry().getTargetPoint(), (BasicLink) source);
812             return addCell(cell, parent, index, split.getOut2(), source);
813         }
814         // ImplicitOutputPort -> ImplicitLink
815         // Switch source and target !
816         if (target instanceof ImplicitLink && source instanceof ImplicitOutputPort && cell instanceof ImplicitLink) {
817             final BasicLink current = (BasicLink) cell;
818             final SplitBlock split = addSplitEdge(current.getGeometry().getTargetPoint(), (ImplicitLink) target);
819             return addCell(cell, parent, index, split.getOut2(), source);
820         }
821
822         // CommandControlLink -> ControlPort
823         if (source instanceof CommandControlLink && target instanceof ControlPort && cell instanceof CommandControlLink) {
824             SplitBlock split = addSplitEdge(((BasicLink) cell).getGeometry().getSourcePoint(), (BasicLink) source);
825             return addCell(cell, parent, index, split.getOut2(), target);
826         }
827         // ControlPort -> CommandControlLink
828         // Switch source and target !
829         if (target instanceof CommandControlLink && source instanceof ControlPort && cell instanceof CommandControlLink) {
830             final BasicLink current = (BasicLink) cell;
831             final SplitBlock split = addSplitEdge(current.getGeometry().getTargetPoint(), (BasicLink) target);
832
833             current.invertDirection();
834
835             return addCell(cell, parent, index, split.getOut2(), source);
836         }
837
838         if (cell instanceof BasicLink && source != null && target != null) {
839             LOG.severe("Unable to add a typed link");
840             return null;
841         } else {
842             LOG.severe("Adding an untyped edge");
843             return super.addCell(cell, parent, index, source, target);
844         }
845     }
846
847     /**
848      * Add a split on a edge.
849      *
850      * @param splitPoint
851      *            the split point (center of the split block)
852      * @param link
853      *            source link
854      * @return split block
855      */
856     public SplitBlock addSplitEdge(final mxPoint splitPoint, final BasicLink link) {
857         final BasicPort linkSource = (BasicPort) link.getSource();
858         final BasicPort linkTarget = (BasicPort) link.getTarget();
859
860         /*
861          * Select the right split accordingly to the link klass
862          */
863         BlockInterFunction f;
864         if (link instanceof CommandControlLink) {
865             f = BlockInterFunction.CLKSPLIT_f;
866         } else if (link instanceof ImplicitLink) {
867             f = BlockInterFunction.IMPSPLIT_f;
868         } else {
869             f = BlockInterFunction.SPLIT_f;
870         }
871
872         final SplitBlock splitBlock = (SplitBlock) XcosCellFactory.createBlock(f);
873
874         // snap the center of the split block on the grid
875         mxGeometry geom = splitBlock.getGeometry();
876         double x = snap(splitPoint.getX());
877         double y = snap(splitPoint.getY());
878         geom.setX(x - (geom.getWidth() / 2.));
879         geom.setY(y - (geom.getHeight() / 2.));
880         splitBlock.setGeometry(geom);
881
882         getModel().beginUpdate();
883         try {
884             // Origin of the parent, (0,0) as default may be different in case
885             mxPoint orig = link.getParent().getGeometry();
886             if (orig == null) {
887                 orig = new mxPoint();
888             }
889
890             addCell(splitBlock);
891
892             // Update old link
893
894             // get breaking segment and related point
895             mxPoint splitTr = new mxPoint(splitPoint.getX() - orig.getX(), splitPoint.getY() - orig.getY());
896             final int pos = link.findNearestSegment(splitTr);
897
898             // save points after breaking point
899             final List<mxPoint> saveStartPoints = link.getPoints(pos, true);
900             final List<mxPoint> saveEndPoints = link.getPoints(pos, false);
901
902             // remove the first end point if the position is near the split
903             // position
904             if (saveEndPoints.size() > 0) {
905                 final mxPoint p = saveEndPoints.get(0);
906                 final double dx = p.getX() - splitTr.getX();
907                 final double dy = p.getY() - splitTr.getY();
908
909                 if (!getAsComponent().isSignificant(dx, dy)) {
910                     saveEndPoints.remove(0);
911                 }
912             }
913
914             getModel().remove(link);
915             connect(linkSource, splitBlock.getIn(), saveStartPoints, orig);
916             connect(splitBlock.getOut1(), linkTarget, saveEndPoints, orig);
917
918             refresh();
919         } finally {
920             getModel().endUpdate();
921         }
922
923         return splitBlock;
924     }
925
926     /**
927      * Connect two port together with the associated points.
928      *
929      * This method perform the connection in two step in order to generate the right UndoableChangeEdits.
930      *
931      * @param src
932      *            the source port
933      * @param trg
934      *            the target port
935      * @param points
936      *            the points
937      * @param orig
938      *            the origin point (may be (0,0))
939      */
940     public void connect(BasicPort src, BasicPort trg, List<mxPoint> points, mxPoint orig) {
941         mxGeometry geometry;
942
943         /*
944          * Add the link with a default geometry
945          */
946         final Object newLink1 = createEdge(null, null, null, src, trg, null);
947         addCell(newLink1, null, null, src, trg);
948         geometry = getModel().getGeometry(newLink1);
949         if (getModel().getParent(newLink1) instanceof BasicBlock) {
950             // on a loop link, translate the points as the cell has been moved to the parent
951             orig.setX(orig.getX() + geometry.getX());
952             orig.setY(orig.getY() + geometry.getY());
953         }
954         geometry.setPoints(points);
955         getModel().setGeometry(newLink1, geometry);
956
957         /*
958          * Update the geometry
959          */
960         // should be cloned to generate an event
961         geometry = (mxGeometry) getModel().getGeometry(newLink1).clone();
962         final double dx = orig.getX();
963         final double dy = orig.getY();
964
965         geometry.translate(dx, dy);
966         getModel().setGeometry(newLink1, geometry);
967     }
968
969     /**
970      * Initialize component settings for a graph.
971      *
972      * This method *must* be used to setup the component after any reassociation.
973      */
974     public void initComponent() {
975         getAsComponent().setToolTips(true);
976
977         // This enable stop editing cells when pressing Enter.
978         getAsComponent().setEnterStopsCellEditing(false);
979
980         getAsComponent().setTolerance(1);
981
982         getAsComponent().getViewport().setOpaque(false);
983
984         getAsComponent().setBackground(XcosOptions.getEdition().getGraphBackground());
985
986         final boolean gridEnable = XcosOptions.getEdition().isGraphGridEnable();
987         setGridVisible(gridEnable);
988         if (gridEnable) {
989             setGridSize(XcosOptions.getEdition().getGraphGrid());
990         }
991     }
992
993     /**
994      * Install the multiplicities (use for link checking)
995      */
996     private void setMultiplicities() {
997         final List<mxMultiplicity> multiplicities = new ArrayList<mxMultiplicity>();
998
999         // Input data port
1000         multiplicities.add(new PortCheck(ExplicitInputPort.class, Collections.unmodifiableList(new ArrayList<Class<? extends mxCell>>() {
1001             private static final long serialVersionUID = -4987163442006736665L;
1002
1003             {
1004                 add(ExplicitOutputPort.class);
1005                 add(ExplicitLink.class);
1006             }
1007         }), XcosMessages.LINK_ERROR_EXPLICIT_IN));
1008         multiplicities.add(new PortCheck(ImplicitInputPort.class, Collections.unmodifiableList(new ArrayList<Class<? extends mxCell>>() {
1009             private static final long serialVersionUID = 886376532181210926L;
1010
1011             {
1012                 add(ImplicitOutputPort.class);
1013                 add(ImplicitInputPort.class);
1014                 add(ImplicitLink.class);
1015             }
1016         }), XcosMessages.LINK_ERROR_IMPLICIT_IN));
1017
1018         // Output data port
1019         multiplicities.add(new PortCheck(ExplicitOutputPort.class, Collections.unmodifiableList(new ArrayList<Class<? extends mxCell>>() {
1020             private static final long serialVersionUID = 4594127972486054821L;
1021
1022             {
1023                 add(ExplicitInputPort.class);
1024             }
1025         }), XcosMessages.LINK_ERROR_EXPLICIT_OUT));
1026         multiplicities.add(new PortCheck(ImplicitOutputPort.class, Collections.unmodifiableList(new ArrayList<Class<? extends mxCell>>() {
1027             private static final long serialVersionUID = -3719677806532507973L;
1028
1029             {
1030                 add(ImplicitInputPort.class);
1031                 add(ImplicitOutputPort.class);
1032                 add(ImplicitLink.class);
1033             }
1034         }), XcosMessages.LINK_ERROR_IMPLICIT_OUT));
1035
1036         // Control port
1037         multiplicities.add(new PortCheck(ControlPort.class, Collections.unmodifiableList(new ArrayList<Class<? extends mxCell>>() {
1038             private static final long serialVersionUID = 2941077191386058497L;
1039
1040             {
1041                 add(CommandPort.class);
1042                 add(CommandControlLink.class);
1043             }
1044         }), XcosMessages.LINK_ERROR_EVENT_IN));
1045
1046         // Command port
1047         multiplicities.add(new PortCheck(CommandPort.class, Collections.unmodifiableList(new ArrayList<Class<? extends mxCell>>() {
1048             private static final long serialVersionUID = -3470370027962480362L;
1049
1050             {
1051                 add(ControlPort.class);
1052             }
1053         }), XcosMessages.LINK_ERROR_EVENT_OUT));
1054
1055         // ExplicitLink connections
1056         multiplicities.add(new PortCheck(ExplicitLink.class, Collections.unmodifiableList(new ArrayList<Class<? extends mxCell>>() {
1057             private static final long serialVersionUID = 7423543162930147373L;
1058
1059             {
1060                 add(ExplicitInputPort.class);
1061             }
1062         }), XcosMessages.LINK_ERROR_EVENT_OUT));
1063
1064         // ImplicitLink connections
1065         multiplicities.add(new PortCheck(ImplicitLink.class, Collections.unmodifiableList(new ArrayList<Class<? extends mxCell>>() {
1066             private static final long serialVersionUID = 7775100011122283282L;
1067
1068             {
1069                 add(ImplicitInputPort.class);
1070                 add(ImplicitOutputPort.class);
1071             }
1072         }), XcosMessages.LINK_ERROR_EVENT_OUT));
1073
1074         // CommandControlLink connections
1075         multiplicities.add(new PortCheck(CommandControlLink.class, Collections.unmodifiableList(new ArrayList<Class<? extends mxCell>>() {
1076             private static final long serialVersionUID = 3260421433507192386L;
1077
1078             {
1079                 add(ControlPort.class);
1080             }
1081         }), XcosMessages.LINK_ERROR_EVENT_OUT));
1082
1083         // Already connected port
1084         multiplicities.add(new PortCheck(BasicPort.class, Collections.unmodifiableList(new ArrayList<Class<? extends mxCell>>() {
1085             private static final long serialVersionUID = 6376349598052836660L;
1086
1087             {
1088                 add(BasicPort.class);
1089             }
1090         }), XcosMessages.LINK_ERROR_ALREADY_CONNECTED));
1091
1092         setMultiplicities(multiplicities.toArray(new mxMultiplicity[multiplicities.size()]));
1093     }
1094
1095     /**
1096      * Install all needed Listeners.
1097      */
1098     public void installListeners() {
1099         /*
1100          * First remove all listeners if present
1101          */
1102         removeListener(CellResizedTracker.getInstance());
1103         getUndoManager().removeListener(UndoUpdateTracker.getInstance());
1104         removeListener(RefreshBlockTracker.getInstance());
1105
1106         // Track when resizing a cell.
1107         addListener(mxEvent.CELLS_RESIZED, CellResizedTracker.getInstance());
1108
1109         // Update the blocks view on undo/redo
1110         getUndoManager().addListener(mxEvent.UNDO, UndoUpdateTracker.getInstance());
1111         getUndoManager().addListener(mxEvent.REDO, UndoUpdateTracker.getInstance());
1112
1113     }
1114
1115     /**
1116      * Removes the given cells from the graph including all connected edges if includeEdges is true. The change is carried out using cellsRemoved.
1117      *
1118      * @param cells
1119      *            the cells to be removed
1120      * @param includeEdges
1121      *            true if the edges must be removed, false otherwise.
1122      * @return the deleted cells
1123      * @see com.mxgraph.view.mxGraph#removeCells(java.lang.Object[], boolean)
1124      */
1125     @Override
1126     public Object[] removeCells(final Object[] cells, final boolean includeEdges) {
1127         if (cells == null || cells.length == 0) {
1128             return super.removeCells(cells, includeEdges);
1129         }
1130
1131         /*
1132          * First remove all links connected to a removed Split if applicable
1133          */
1134         final Object[] initialCells;
1135         if (includeEdges) {
1136             initialCells = addAllEdges(cells);
1137         } else {
1138             initialCells = cells;
1139         }
1140
1141         // stash used on the loop
1142         final Queue<Object> loopCells = new LinkedList<Object>(Arrays.asList(initialCells));
1143         // the cells that need to be really
1144         final Set<Object> removedCells = new HashSet<Object>(loopCells);
1145         // couple of cells to reconnect
1146         final List<BasicPort[]> connectedCells = new ArrayList<BasicPort[]>();
1147         final List<List<mxPoint>> connectedPoints = new ArrayList<List<mxPoint>>();
1148
1149         /*
1150          * Then loop on the algorithm to select the right edges
1151          */
1152         // /!\ not bounded algorithm
1153         while (loopCells.size() > 0) {
1154             Object cell = loopCells.remove();
1155
1156             if (cell instanceof BasicLink) {
1157                 /*
1158                  * Continue on non fully connected links
1159                  */
1160                 if (((BasicLink) cell).getSource() == null) {
1161                     continue;
1162                 }
1163                 if (((BasicLink) cell).getTarget() == null) {
1164                     continue;
1165                 }
1166
1167                 /*
1168                  * Add any split to a link
1169                  */
1170                 addTerminalParent(((BasicLink) cell).getSource(), removedCells, loopCells);
1171                 addTerminalParent(((BasicLink) cell).getTarget(), removedCells, loopCells);
1172
1173             } else if (cell instanceof SplitBlock) {
1174                 final SplitBlock splitBlock = (SplitBlock) cell;
1175
1176                 /*
1177                  * Remove related connection or not and reconnect.
1178                  */
1179
1180                 if (splitBlock.getIn().getEdgeCount() == 0 || splitBlock.getOut1().getEdgeCount() == 0 || splitBlock.getOut2().getEdgeCount() == 0) {
1181                     // corner case, all links will be removed
1182                     continue;
1183                 }
1184
1185                 final mxICell inLink = splitBlock.getIn().getEdgeAt(0);
1186                 final mxICell out1Link = splitBlock.getOut1().getEdgeAt(0);
1187                 final mxICell out2Link = splitBlock.getOut2().getEdgeAt(0);
1188
1189                 final boolean inRemoved = removedCells.contains(inLink);
1190                 final boolean out1Removed = removedCells.contains(out1Link);
1191                 final boolean out2Removed = removedCells.contains(out2Link);
1192
1193                 /*
1194                  * Explicit case, if the in link is deleted; all the out links also should.
1195                  */
1196                 if (inLink instanceof ExplicitLink && inRemoved) {
1197                     if (removedCells.add(out1Link)) {
1198                         loopCells.add(out1Link);
1199                     }
1200
1201                     if (removedCells.add(out2Link)) {
1202                         loopCells.add(out2Link);
1203                     }
1204                 }
1205
1206                 /*
1207                  * Global case reconnect if not removed
1208                  */
1209                 final BasicPort[] connection;
1210                 List<mxPoint> points = null;
1211                 if (!inRemoved && !out1Removed && out2Removed) {
1212                     connection = findTerminals(inLink, out1Link, removedCells);
1213                     points = getDirectPoints(splitBlock, inLink, out1Link);
1214                 } else if (!inRemoved && out1Removed && !out2Removed) {
1215                     connection = findTerminals(inLink, out2Link, removedCells);
1216                     points = getDirectPoints(splitBlock, inLink, out2Link);
1217                 } else if (inRemoved && !out1Removed && !out2Removed) {
1218                     // only implicit or event case, log otherwise
1219                     if (out1Link instanceof ExplicitLink || out2Link instanceof ExplicitLink) {
1220                         LOG.severe("Reconnection failed for explicit links");
1221                         connection = null;
1222                     } else {
1223                         connection = findTerminals(out1Link, out2Link, removedCells);
1224                         points = getDirectPoints(splitBlock, out1Link, out2Link);
1225                     }
1226                 } else {
1227                     connection = null;
1228                 }
1229
1230                 if (connection != null) {
1231                     connectedCells.add(connection);
1232                     connectedPoints.add(points);
1233                 }
1234             }
1235         }
1236
1237         final Object[] ret;
1238         getModel().beginUpdate();
1239         try {
1240             ret = super.removeCells(removedCells.toArray(), includeEdges);
1241             for (int i = 0; i < connectedCells.size(); i++) {
1242                 final BasicPort[] connection = connectedCells.get(i);
1243                 final List<mxPoint> points = connectedPoints.get(i);
1244                 if (!removedCells.contains(connection[0].getParent()) && !removedCells.contains(connection[1].getParent())) {
1245                     connect(connection[0], connection[1], points, new mxPoint());
1246                 }
1247             }
1248         } finally {
1249             getModel().endUpdate();
1250         }
1251         return ret;
1252     }
1253
1254     /**
1255      * Add any terminal parent to the removed cells
1256      *
1257      * @param terminal
1258      *            the current terminal (instance of BasicPort)
1259      * @param removedCells
1260      *            the "to be removed" set
1261      * @param loopCells
1262      *            the "while loop" set
1263      */
1264     private void addTerminalParent(mxICell terminal, Collection<Object> removedCells, Collection<Object> loopCells) {
1265         assert(terminal == null || terminal instanceof BasicPort);
1266         assert(removedCells != null);
1267         assert(loopCells != null);
1268
1269         // getting terminal parent
1270         mxICell target = null;
1271         if (terminal != null) {
1272             target = terminal.getParent();
1273         } else {
1274             target = null;
1275         }
1276
1277         // add target if applicable
1278         if (target instanceof SplitBlock) {
1279             if (removedCells.add(target)) {
1280                 loopCells.add(target);
1281             }
1282         }
1283     }
1284
1285     /**
1286      * Find the terminals when relinking the 2 links
1287      *
1288      * This method ensure that {source, target} are not child of removed blocks.
1289      *
1290      * @param linkSource
1291      *            the normal source link
1292      * @param linkTerminal
1293      *            the normal target link
1294      * @param removedCells
1295      *            the set of removed objects
1296      * @return the {source, target} connection
1297      */
1298     private BasicPort[] findTerminals(final mxICell linkSource, final mxICell linkTerminal, final Set<Object> removedCells) {
1299         BasicPort src = (BasicPort) linkSource.getTerminal(true);
1300         BasicPort tgt = (BasicPort) linkTerminal.getTerminal(false);
1301         if (linkSource instanceof ImplicitLink) {
1302             if (removedCells.contains(src.getParent())) {
1303                 src = (BasicPort) linkSource.getTerminal(false);
1304             }
1305             if (removedCells.contains(tgt.getParent())) {
1306                 tgt = (BasicPort) linkTerminal.getTerminal(true);
1307             }
1308         }
1309
1310         return new BasicPort[] { src, tgt };
1311     }
1312
1313     /**
1314      * Get the direct points from inLink.getSource() to outLink.getTarget().
1315      *
1316      * @param splitBlock
1317      *            the current splitblock (added as a mid-point)
1318      * @param inLink
1319      *            the link before the split
1320      * @param outLink
1321      *            the link after the split
1322      * @return the points
1323      */
1324     private List<mxPoint> getDirectPoints(final SplitBlock splitBlock, final mxICell inLink, final mxICell outLink) {
1325         List<mxPoint> points;
1326         // add the points before the split
1327         points = new ArrayList<mxPoint>();
1328         if (inLink.getGeometry().getPoints() != null) {
1329             points.addAll(inLink.getGeometry().getPoints());
1330         }
1331
1332         // add a new point at the split location
1333         points.add(new mxPoint(snap(splitBlock.getGeometry().getCenterX()), snap(splitBlock.getGeometry().getCenterY())));
1334
1335         // add the points after the split
1336         if (outLink.getGeometry().getPoints() != null) {
1337             points.addAll(outLink.getGeometry().getPoints());
1338         }
1339
1340         return points;
1341     }
1342
1343     /**
1344      * Manage Group to be CellFoldable i.e with a (-) to reduce and a (+) to expand them. Labels (mxCell instance with value) should not have a visible foldable sign.
1345      *
1346      * @param cell
1347      *            the selected cell
1348      * @param collapse
1349      *            the collapse settings
1350      * @return always <code>false</code>
1351      * @see com.mxgraph.view.mxGraph#isCellFoldable(java.lang.Object, boolean)
1352      */
1353     @Override
1354     public boolean isCellFoldable(final Object cell, final boolean collapse) {
1355         return false;
1356     }
1357
1358     /**
1359      * Not BasicBLock cell have a moveable label.
1360      *
1361      * @param cell
1362      *            the cell
1363      * @return true if the corresponding label is moveable
1364      * @see com.mxgraph.view.mxGraph#isLabelMovable(java.lang.Object)
1365      */
1366     @Override
1367     public boolean isLabelMovable(Object cell) {
1368         return !(cell instanceof BasicBlock);
1369     }
1370
1371     /**
1372      * Return true if selectable
1373      *
1374      * @param cell
1375      *            the cell
1376      * @return status
1377      * @see com.mxgraph.view.mxGraph#isCellSelectable(java.lang.Object)
1378      */
1379     @Override
1380     public boolean isCellSelectable(final Object cell) {
1381         if (cell instanceof BasicPort) {
1382             return false;
1383         }
1384         return super.isCellSelectable(cell);
1385     }
1386
1387     /**
1388      * Return true if movable
1389      *
1390      * @param cell
1391      *            the cell
1392      * @return status
1393      * @see com.mxgraph.view.mxGraph#isCellMovable(java.lang.Object)
1394      */
1395     @Override
1396     public boolean isCellMovable(final Object cell) {
1397         if (cell instanceof BasicPort) {
1398             return false;
1399         }
1400
1401         boolean movable = false;
1402         final Object[] cells = getSelectionCells();
1403
1404         // don't move if selection is only links
1405         for (Object c : cells) {
1406             if (!(c instanceof BasicLink)) {
1407                 movable = true;
1408                 break;
1409             }
1410         }
1411
1412         return movable && super.isCellMovable(cell);
1413     }
1414
1415     /**
1416      * Return true if resizable
1417      *
1418      * @param cell
1419      *            the cell
1420      * @return status
1421      * @see com.mxgraph.view.mxGraph#isCellResizable(java.lang.Object)
1422      */
1423     @Override
1424     public boolean isCellResizable(final Object cell) {
1425         if (cell instanceof SplitBlock) {
1426             return false;
1427         }
1428         return (cell instanceof BasicBlock) && super.isCellResizable(cell);
1429     }
1430
1431     /**
1432      * A cell is deletable if it is not a locked block or an identifier cell
1433      *
1434      * @param cell
1435      *            the cell
1436      * @return status
1437      * @see com.mxgraph.view.mxGraph#isCellDeletable(java.lang.Object)
1438      */
1439     @Override
1440     public boolean isCellDeletable(final Object cell) {
1441         final boolean isALockedBLock = cell instanceof BasicBlock && ((BasicBlock) cell).isLocked();
1442         final boolean isAnIdentifier = cell.getClass().equals(mxCell.class);
1443
1444         if (isALockedBLock) {
1445             return false;
1446         }
1447         if (isAnIdentifier) {
1448             return true;
1449         }
1450
1451         return super.isCellDeletable(cell);
1452     }
1453
1454     /**
1455      * Return true if editable
1456      *
1457      * @param cell
1458      *            the cell
1459      * @return status
1460      * @see com.mxgraph.view.mxGraph#isCellEditable(java.lang.Object)
1461      */
1462     @Override
1463     public boolean isCellEditable(final Object cell) {
1464         return (cell instanceof TextBlock) && super.isCellDeletable(cell);
1465     }
1466
1467     /**
1468      * Return or create the identifier for the cell
1469      *
1470      * @param cell
1471      *            the cell to check
1472      * @return the identifier cell
1473      */
1474     public mxCell getOrCreateCellIdentifier(final mxCell cell) {
1475         if (cell.getId().endsWith(HASH_IDENTIFIER)) {
1476             return cell;
1477         }
1478
1479         final mxGraphModel graphModel = (mxGraphModel) getModel();
1480
1481         final mxCell identifier;
1482         final String cellId = cell.getId() + HASH_IDENTIFIER;
1483         if (graphModel.getCell(cellId) == null) {
1484             // create the identifier
1485             identifier = createCellIdentifier(cell);
1486
1487             // add the identifier
1488             graphModel.add(cell, identifier, cell.getChildCount());
1489         } else {
1490             identifier = (mxCell) graphModel.getCell(cellId);
1491         }
1492         return identifier;
1493     }
1494
1495     /**
1496      * Return the identifier for the cell
1497      *
1498      * @param cell
1499      *            the cell to check
1500      * @return the identifier cell
1501      */
1502     public mxCell getCellIdentifier(final mxCell cell) {
1503         final mxGraphModel graphModel = (mxGraphModel) getModel();
1504         final String cellId = cell.getId() + HASH_IDENTIFIER;
1505
1506         return (mxCell) graphModel.getCell(cellId);
1507     }
1508
1509     /**
1510      * Create a cell identifier for a specific cell
1511      *
1512      * @param cell
1513      *            the cell
1514      * @return the cell identifier.
1515      */
1516     public mxCell createCellIdentifier(final mxCell cell) {
1517         final mxCell identifier;
1518         final String cellId = cell.getId() + HASH_IDENTIFIER;
1519
1520         identifier = new mxCell(null, (mxGeometry) DEFAULT_LABEL_GEOMETRY.clone(), "noLabel=0;opacity=0;");
1521         identifier.getGeometry().setRelative(true);
1522         identifier.setVertex(true);
1523         identifier.setConnectable(false);
1524         identifier.setId(cellId);
1525
1526         return identifier;
1527     }
1528
1529     /**
1530      * Get the label for the cell according to its style.
1531      *
1532      * @param cell
1533      *            the cell object
1534      * @return a representative the string (block name) or a style specific style.
1535      * @see com.mxgraph.view.mxGraph#convertValueToString(java.lang.Object)
1536      */
1537     @Override
1538     public String convertValueToString(Object cell) {
1539         String ret = null;
1540
1541         if (cell != null) {
1542             JavaController controller = new JavaController();
1543             final Map<String, Object> style = getCellStyle(cell);
1544
1545             final String displayedLabel = (String) style.get("displayedLabel");
1546             if (displayedLabel != null) {
1547                 if (cell instanceof BasicBlock) {
1548                     BasicBlock block = (BasicBlock) cell;
1549                     VectorOfDouble v = new VectorOfDouble();
1550                     controller.getObjectProperty(block.getUID(), block.getKind(), ObjectProperties.EXPRS, v);
1551
1552                     ScilabType var = new ScilabTypeCoder().vec2var(v);
1553                     if (var instanceof ScilabString) {
1554                         ScilabString str = (ScilabString) var;
1555                         Object[] exprs = new String[str.getHeight() * str.getWidth()];
1556                         for (int i = 0; i < str.getHeight() ; i++)
1557                             for (int j = 0; j < str.getWidth() ; j++) {
1558                                 exprs[i + j * str.getHeight()] = str.getData()[i][j];
1559                             }
1560                         try {
1561                             ret = String.format(displayedLabel, exprs);
1562                         } catch (IllegalFormatException e) {
1563                             LOG.severe(e.toString());
1564                             ret = displayedLabel;
1565                         }
1566                     } else {
1567                         ret = displayedLabel;
1568                     }
1569                 } else {
1570                     ret = displayedLabel;
1571                 }
1572             } else {
1573                 final String label = super.convertValueToString(cell);
1574                 if (label.isEmpty() && cell instanceof BasicBlock) {
1575                     String[] interfaceFunction = new String[1];
1576
1577                     controller.getObjectProperty(((BasicBlock) cell).getUID(), Kind.BLOCK, ObjectProperties.INTERFACE_FUNCTION, interfaceFunction);
1578                     ret = interfaceFunction[0];
1579                 } else {
1580                     ret = label;
1581                 }
1582             }
1583         }
1584
1585         return ret;
1586     }
1587
1588     /**
1589      * Return true if auto sized
1590      *
1591      * @param cell
1592      *            the cell
1593      * @return status
1594      * @see com.mxgraph.view.mxGraph#isAutoSizeCell(java.lang.Object)
1595      */
1596     @Override
1597     public boolean isAutoSizeCell(final Object cell) {
1598         boolean status = super.isAutoSizeCell(cell);
1599
1600         if (cell instanceof AfficheBlock) {
1601             status |= true;
1602         }
1603
1604         if (cell instanceof TextBlock) {
1605             status &= false;
1606         }
1607
1608         return status;
1609     }
1610
1611     /**
1612      * {@inheritDoc} Do not extends if the port position is north or south.
1613      */
1614     @Override
1615     public boolean isExtendParent(Object cell) {
1616         final boolean extendsParents;
1617
1618         if (cell instanceof BasicPort) {
1619             final BasicPort p = (BasicPort) cell;
1620             extendsParents = !(p.getOrientation() == Orientation.NORTH || p.getOrientation() == Orientation.SOUTH) && super.isExtendParent(p);
1621         } else {
1622             extendsParents = super.isExtendParent(cell);
1623         }
1624         return extendsParents;
1625     }
1626
1627     /**
1628      * @return the model ID
1629      */
1630     public long getUID() {
1631         return ((XcosCell) getDefaultParent()).getUID();
1632     }
1633
1634     /**
1635      * @return Kind.DIAGRAM or Kind.BLOCK
1636      */
1637     public Kind getKind() {
1638         return ((XcosCell) getDefaultParent()).getKind();
1639     }
1640
1641     /**
1642      * Manage the visibility of the grid and the associated menu
1643      *
1644      * @param status
1645      *            new status
1646      */
1647     public void setGridVisible(final boolean status) {
1648         setGridEnabled(status);
1649         getAsComponent().setGridVisible(status);
1650         getAsComponent().repaint();
1651     }
1652
1653     /**
1654      * @return save status
1655      */
1656     public boolean saveDiagram() {
1657         final boolean isSuccess = saveDiagramAs(getSavedFile());
1658
1659         if (isSuccess) {
1660             setModified(false);
1661         }
1662
1663         return isSuccess;
1664     }
1665
1666     /**
1667      * @param fileName
1668      *            diagram filename
1669      * @return save status
1670      */
1671     public boolean saveDiagramAs(final File fileName) {
1672         boolean isSuccess = false;
1673         File writeFile = fileName;
1674         XcosFileType format = XcosOptions.getPreferences().getFileFormat();
1675
1676         info(XcosMessages.SAVING_DIAGRAM);
1677         if (fileName == null) {
1678             final SwingScilabFileChooser fc = SaveAsAction.createFileChooser();
1679             SaveAsAction.configureFileFilters(fc);
1680             ConfigurationManager.configureCurrentDirectory(fc);
1681
1682             if (getSavedFile() != null) {
1683                 // using save-as, the file chooser should have a filename
1684                 // without extension as default
1685                 String filename = getSavedFile().getName();
1686                 filename = filename.substring(0, filename.lastIndexOf('.'));
1687                 fc.setSelectedFile(new File(filename));
1688             } else {
1689                 final String title = getTitle();
1690                 if (title != null) {
1691                     /*
1692                      * Escape file to handle not supported character in file name (may be Windows only) see http://msdn.microsoft.com/en -us/library/windows/desktop/aa365247%28v=vs.85%29.aspx
1693                      */
1694                     final char[] regex = "<>:\"/\\|?*".toCharArray();
1695                     String escaped = title;
1696                     for (char c : regex) {
1697                         escaped = escaped.replace(c, '-');
1698                     }
1699
1700                     fc.setSelectedFile(new File(escaped));
1701                 }
1702             }
1703
1704             int status = fc.showSaveDialog(this.getAsComponent());
1705             if (status != JFileChooser.APPROVE_OPTION) {
1706                 info(XcosMessages.EMPTY_INFO);
1707                 return isSuccess;
1708             }
1709
1710             // change the format if the user choose a save-able file format
1711             final XcosFileType selectedFilter = XcosFileType.findFileType(fc.getFileFilter());
1712             if (XcosFileType.getAvailableSaveFormats().contains(selectedFilter)) {
1713                 format = selectedFilter;
1714             }
1715             writeFile = fc.getSelectedFile();
1716         }
1717
1718         /* Extension/format update */
1719
1720         // using a String filename also works on a non-existing file
1721         final String filename = writeFile.getName();
1722
1723         /*
1724          * Look for the user extension if it does not exists, append a default one
1725          *
1726          * if the specified extension is handled, update the save format ; else append a default extension and use the default format
1727          */
1728         XcosFileType userExtension = XcosFileType.findFileType(filename);
1729         if (userExtension == null) {
1730             writeFile = new File(writeFile.getParent(), filename + format.getDottedExtension());
1731             userExtension = format;
1732         }
1733         if (XcosFileType.getAvailableSaveFormats().contains(userExtension)) {
1734             format = userExtension;
1735         } else {
1736             writeFile = new File(writeFile.getParent(), filename + format.getDottedExtension());
1737         }
1738
1739         /*
1740          * If the file exists, ask for confirmation if this is not the previously saved file
1741          */
1742         if (writeFile.exists() && !writeFile.equals(getSavedFile())) {
1743             final boolean overwrite = ScilabModalDialog.show(XcosTab.get(this), XcosMessages.OVERWRITE_EXISTING_FILE, XcosMessages.XCOS, IconType.QUESTION_ICON,
1744                                       ButtonType.YES_NO) == AnswerOption.YES_OPTION;
1745
1746             if (!overwrite) {
1747                 info(XcosMessages.EMPTY_INFO);
1748                 return false;
1749             }
1750         }
1751
1752         /*
1753          * Really save the data
1754          */
1755         try {
1756             format.save(writeFile.getCanonicalPath(), getRootDiagram());
1757             setSavedFile(writeFile);
1758
1759             setTitle(writeFile.getName().substring(0, writeFile.getName().lastIndexOf('.')));
1760             ConfigurationManager.getInstance().addToRecentFiles(writeFile);
1761             setModified(false);
1762             isSuccess = true;
1763         } catch (final Exception e) {
1764             LOG.severe(e.toString());
1765
1766             XcosDialogs.couldNotSaveFile(this);
1767         }
1768
1769         info(XcosMessages.EMPTY_INFO);
1770         return isSuccess;
1771     }
1772
1773     /**
1774      * Perform post loading initialization.
1775      *
1776      * @param file
1777      *            the loaded file
1778      */
1779     public void postLoad(final File file) {
1780         final String name = file.getName();
1781
1782         if (XcosFileType.getAvailableSaveFormats().contains(XcosFileType.findFileType(file))) {
1783             setSavedFile(file);
1784         }
1785         setTitle(name.substring(0, name.lastIndexOf('.')));
1786         setModified(false);
1787         updateTabTitle();
1788
1789         fireEvent(new mxEventObject(mxEvent.ROOT));
1790
1791         info(XcosMessages.EMPTY_INFO);
1792     }
1793
1794     @Override
1795     public void setSavedFile(File savedFile) {
1796         super.setSavedFile(savedFile);
1797
1798         if (savedFile != null) {
1799             JavaController controller = new JavaController();
1800             controller.setObjectProperty(getUID(), getKind(), ObjectProperties.PATH, savedFile.getAbsolutePath());
1801         }
1802     }
1803
1804
1805     /**
1806      * Set the title of the diagram
1807      *
1808      * @param title
1809      *            the title
1810      * @see org.scilab.modules.graph.ScilabGraph#setTitle(java.lang.String)
1811      */
1812     @Override
1813     public void setTitle(final String title) {
1814         super.setTitle(title);
1815
1816         JavaController controller = new JavaController();
1817         controller.setObjectProperty(getUID(), getKind(), ObjectProperties.TITLE, title);
1818     }
1819
1820     /**
1821      * Update the title
1822      */
1823     public void updateTabTitle() {
1824         // get the modifier string
1825         final String modified;
1826         if (isModified()) {
1827             modified = "*";
1828         } else {
1829             modified = "";
1830         }
1831
1832         // get the title string
1833         final String title = getTitle();
1834
1835         // get the path
1836         CharSequence formattedPath = "";
1837         if (getKind() == Kind.DIAGRAM) {
1838             final File savedFile = getSavedFile();
1839             if (savedFile != null) {
1840                 try {
1841                     final String path = savedFile.getCanonicalPath();
1842                     formattedPath = new StringBuilder().append(" (").append(path).append(')');
1843                 } catch (final IOException e) {
1844                     LOG.warning(e.toString());
1845                 }
1846             }
1847         }
1848
1849         // Product name
1850         final String product = Xcos.TRADENAME;
1851
1852         final String tabTitle = new StringBuilder().append(modified).append(title).append(formattedPath).append(" - ").append(product).toString();
1853
1854         final SwingScilabDockablePanel tab = ScilabTabFactory.getInstance().getFromCache(getGraphTab());
1855         if (tab != null) {
1856             tab.setName(tabTitle);
1857         }
1858     }
1859
1860     /**
1861      * Load a file with different method depending on it extension
1862      *
1863      * @param controller
1864      *            the used controller
1865      * @param file
1866      *            File to load (can be null)
1867      */
1868     public void transformAndLoadFile(final JavaController controller, final String file) {
1869         final File f;
1870         final XcosFileType filetype;
1871         if (file != null) {
1872             f = new File(file);
1873             filetype = XcosFileType.findFileType(f);
1874         } else {
1875             f = null;
1876             filetype = null;
1877         }
1878         new SwingWorker<XcosDiagram, ActionEvent>() {
1879             int counter = 0;
1880             final Timer t = new Timer(1000, new ActionListener() {
1881                 @Override
1882                 public void actionPerformed(ActionEvent e) {
1883                     counter = (counter + 1) % (XcosMessages.DOTS.length() + 1);
1884                     String str = XcosMessages.LOADING_DIAGRAM + XcosMessages.DOTS.substring(0, counter);
1885
1886                     XcosDiagram.this.info(str);
1887                 }
1888             });
1889
1890             @Override
1891             protected XcosDiagram doInBackground() {
1892                 t.start();
1893
1894                 final Xcos instance = Xcos.getInstance();
1895                 XcosDiagram diag = XcosDiagram.this;
1896
1897                 diag.setReadOnly(true);
1898
1899                 /*
1900                  * Load, log errors and notify
1901                  */
1902                 synchronized (instance) {
1903                     try {
1904
1905                         if (f != null && filetype != null) {
1906                             filetype.load(file, XcosDiagram.this);
1907                         } else {
1908                             XcosCellFactory.insertChildren(controller, XcosDiagram.this);
1909                         }
1910
1911                         instance.setLastError("");
1912                     } catch (Exception e) {
1913                         e.printStackTrace();
1914
1915                         Throwable ex = e;
1916                         while (ex instanceof RuntimeException) {
1917                             ex = ex.getCause();
1918                         }
1919                         instance.setLastError(ex.getMessage());
1920                     }
1921                     instance.notify();
1922                 }
1923
1924                 return diag;
1925             }
1926
1927             @Override
1928             protected void done() {
1929                 t.stop();
1930                 XcosDiagram.this.setReadOnly(false);
1931                 XcosDiagram.this.getUndoManager().clear();
1932                 XcosDiagram.this.refresh();
1933
1934                 /*
1935                  * Load has finished
1936                  */
1937                 if (f != null && filetype != null) {
1938                     postLoad(f);
1939                 }
1940                 XcosDiagram.this.info(XcosMessages.EMPTY_INFO);
1941             }
1942
1943         } .execute();
1944     }
1945
1946     /**
1947      * Getting the root diagram of a decomposed diagram
1948      *
1949      * @return Root parent of the whole parent
1950      */
1951     public XcosDiagram getRootDiagram() {
1952         if (getKind() == Kind.DIAGRAM) {
1953             return this;
1954         }
1955
1956         JavaController controller = new JavaController();
1957         long[] parent = new long[1];
1958         controller.getObjectProperty(getUID(), getKind(), ObjectProperties.PARENT_DIAGRAM, parent);
1959
1960         Collection<XcosDiagram> diagrams = Xcos.getInstance().getDiagrams(parent[0]);
1961         return diagrams.stream().filter(d -> d.getUID() == parent[0]).findFirst().get();
1962     }
1963
1964     /**
1965      * Returns the tooltip to be used for the given cell.
1966      *
1967      * @param cell
1968      *            block
1969      * @return cell tooltip
1970      */
1971     @Override
1972     public String getToolTipForCell(final Object cell) {
1973         if (cell instanceof XcosCell) {
1974             return String.valueOf(((XcosCell) cell).getUID());
1975         }
1976         return "";
1977     }
1978
1979     /**
1980      * Display the message in info bar.
1981      *
1982      * @param message
1983      *            Information
1984      */
1985     public void info(final String message) {
1986         final XcosTab tab = XcosTab.get(this);
1987
1988         if (tab != null && tab.getInfoBar() != null) {
1989             tab.getInfoBar().setText(message);
1990         }
1991     }
1992
1993     /**
1994      * Display the message into an error popup
1995      *
1996      * @param message
1997      *            Error of the message
1998      */
1999     public void error(final String message) {
2000         JOptionPane.showMessageDialog(getAsComponent(), message, XcosMessages.XCOS, JOptionPane.ERROR_MESSAGE);
2001     }
2002
2003     /**
2004      * Find the block corresponding to the given uid and display a warning message.
2005      *
2006      * @param uid
2007      *            - A String as UID.
2008      * @param message
2009      *            - The message to display.
2010      */
2011     public void warnCellByUID(final String uid, final String message) {
2012         final Object cell = ((mxGraphModel) getModel()).getCell(uid);
2013         if (cell == null) {
2014             return;
2015         }
2016
2017         if (GraphicsEnvironment.isHeadless()) {
2018             System.err.printf("%s at %s\n    %s: %s\n", "warnCell", getRootDiagram().getTitle(), uid, message);
2019             return;
2020         }
2021
2022         // open the tab
2023         if (XcosTab.get(this) == null) {
2024             XcosTab.restore(this);
2025         }
2026
2027         if (message.isEmpty()) {
2028             getAsComponent().clearCellOverlays(cell);
2029         } else {
2030             getAsComponent().setCellWarning(cell, message, null, true);
2031         }
2032     }
2033
2034     @Override
2035     public File getSavedFile() {
2036         if (getKind() == Kind.DIAGRAM) {
2037             return super.getSavedFile();
2038         } else {
2039             return getRootDiagram().getSavedFile();
2040         }
2041     }
2042
2043     /**
2044      * Read the applicable context on this diagram.
2045      * <p>
2046      * This function retrieve the current diagram's context and all its parent
2047      * @return the full context
2048      */
2049     public String[] getContext() {
2050         final ArrayList<String> allContext = new ArrayList<>();
2051         final Stack<ScicosObjectOwner> hierarchy = lookForHierarchy(new ScicosObjectOwner(getUID(), getKind()));
2052
2053         final JavaController controller = new JavaController();
2054         final VectorOfString context = new VectorOfString();
2055
2056         hierarchy.stream().forEach(o -> {
2057             controller.getObjectProperty(o.getUID(), o.getKind(), ObjectProperties.DIAGRAM_CONTEXT, context);
2058
2059             final int length = context.size();
2060             for (int i = 0; i < length; i++) {
2061                 allContext.add(context.get(i));
2062             }
2063             allContext.add("");
2064         });
2065
2066         return allContext.toArray(new String[allContext.size()]);
2067     }
2068
2069
2070     /**
2071      * Evaluate the current context
2072      *
2073      * @return The resulting data. Keys are variable names and Values are evaluated values.
2074      */
2075     public Map<String, String> evaluateContext() {
2076         Map<String, String> result = Collections.emptyMap();
2077         final ScilabDirectHandler handler = ScilabDirectHandler.acquire();
2078         if (handler == null) {
2079             return result;
2080         }
2081
2082         try {
2083             // first write the context strings
2084             handler.writeContext(getContext());
2085
2086             // evaluate using script2var
2087             ScilabInterpreterManagement.synchronousScilabExec(ScilabDirectHandler.CONTEXT + " = script2var(" + ScilabDirectHandler.CONTEXT + ", struct());");
2088
2089             // read the structure
2090             result = handler.readContext();
2091         } catch (final InterpreterException e) {
2092             info("Unable to evaluate the contexte");
2093             e.printStackTrace();
2094         } finally {
2095             handler.release();
2096         }
2097
2098         return result;
2099     }
2100
2101     /**
2102      * Returns true if the given cell is a not a block nor a port.
2103      *
2104      * @param cell
2105      *            the drop target
2106      * @param cells
2107      *            the cells to be dropped
2108      * @return the drop status
2109      * @see com.mxgraph.view.mxGraph#isValidDropTarget(java.lang.Object, java.lang.Object[])
2110      */
2111     @Override
2112     public boolean isValidDropTarget(final Object cell, final Object[] cells) {
2113         return !(cell instanceof BasicBlock) && !(cell instanceof BasicBlock) && !(cell instanceof BasicPort) && super.isValidDropTarget(cell, cells);
2114     }
2115
2116     /**
2117      * Construct a new selection model used on this graph.
2118      *
2119      * @return a new selection model instance.
2120      * @see com.mxgraph.view.mxGraph#createSelectionModel()
2121      */
2122     @Override
2123     protected mxGraphSelectionModel createSelectionModel() {
2124         return new mxGraphSelectionModel(this) {
2125             /**
2126              * When we only want to select a cell which is a port, select the parent block.
2127              *
2128              * @param cell
2129              *            the cell
2130              * @see com.mxgraph.view.mxGraphSelectionModel#setCell(java.lang.Object)
2131              */
2132             @Override
2133             public void setCell(final Object cell) {
2134                 final Object current;
2135                 if (cell instanceof BasicPort) {
2136                     current = getModel().getParent(cell);
2137                 } else {
2138                     current = cell;
2139                 }
2140                 super.setCell(current);
2141             }
2142         };
2143     }
2144 }