a841c70e2dd8d8f58cd71cb05f5609f36f936918
[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         return saveDiagramAs(getSavedFile());
1658     }
1659
1660     /**
1661      * @param fileName
1662      *            diagram filename
1663      * @return save status
1664      */
1665     public boolean saveDiagramAs(final File fileName) {
1666         boolean isSuccess = false;
1667         File writeFile = fileName;
1668         XcosFileType format = XcosOptions.getPreferences().getFileFormat();
1669
1670         info(XcosMessages.SAVING_DIAGRAM);
1671         if (fileName == null) {
1672             final SwingScilabFileChooser fc = SaveAsAction.createFileChooser();
1673             SaveAsAction.configureFileFilters(fc);
1674             ConfigurationManager.configureCurrentDirectory(fc);
1675
1676             if (getSavedFile() != null) {
1677                 // using save-as, the file chooser should have a filename
1678                 // without extension as default
1679                 String filename = getSavedFile().getName();
1680                 filename = filename.substring(0, filename.lastIndexOf('.'));
1681                 fc.setSelectedFile(new File(filename));
1682             } else {
1683                 final String title = getTitle();
1684                 if (title != null) {
1685                     /*
1686                      * 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
1687                      */
1688                     final char[] regex = "<>:\"/\\|?*".toCharArray();
1689                     String escaped = title;
1690                     for (char c : regex) {
1691                         escaped = escaped.replace(c, '-');
1692                     }
1693
1694                     fc.setSelectedFile(new File(escaped));
1695                 }
1696             }
1697
1698             int status = fc.showSaveDialog(this.getAsComponent());
1699             if (status != JFileChooser.APPROVE_OPTION) {
1700                 info(XcosMessages.EMPTY_INFO);
1701                 return isSuccess;
1702             }
1703
1704             // change the format if the user choose a save-able file format
1705             final XcosFileType selectedFilter = XcosFileType.findFileType(fc.getFileFilter());
1706             if (XcosFileType.getAvailableSaveFormats().contains(selectedFilter)) {
1707                 format = selectedFilter;
1708             }
1709             writeFile = fc.getSelectedFile();
1710         }
1711
1712         /* Extension/format update */
1713
1714         // using a String filename also works on a non-existing file
1715         final String filename = writeFile.getName();
1716
1717         /*
1718          * Look for the user extension if it does not exists, append a default one
1719          *
1720          * if the specified extension is handled, update the save format ; else append a default extension and use the default format
1721          */
1722         XcosFileType userExtension = XcosFileType.findFileType(filename);
1723         if (userExtension == null) {
1724             writeFile = new File(writeFile.getParent(), filename + format.getDottedExtension());
1725             userExtension = format;
1726         }
1727         if (XcosFileType.getAvailableSaveFormats().contains(userExtension)) {
1728             format = userExtension;
1729         } else {
1730             writeFile = new File(writeFile.getParent(), filename + format.getDottedExtension());
1731         }
1732
1733         /*
1734          * If the file exists, ask for confirmation if this is not the previously saved file
1735          */
1736         if (writeFile.exists() && !writeFile.equals(getSavedFile())) {
1737             final boolean overwrite = ScilabModalDialog.show(XcosTab.get(this), XcosMessages.OVERWRITE_EXISTING_FILE, XcosMessages.XCOS, IconType.QUESTION_ICON,
1738                                       ButtonType.YES_NO) == AnswerOption.YES_OPTION;
1739
1740             if (!overwrite) {
1741                 info(XcosMessages.EMPTY_INFO);
1742                 return false;
1743             }
1744         }
1745
1746         /*
1747          * Really save the data
1748          */
1749         try {
1750             format.save(writeFile.getCanonicalPath(), getRootDiagram());
1751             setSavedFile(writeFile);
1752
1753             setTitle(writeFile.getName().substring(0, writeFile.getName().lastIndexOf('.')));
1754             ConfigurationManager.getInstance().addToRecentFiles(writeFile);
1755             setModified(false);
1756             isSuccess = true;
1757         } catch (final Exception e) {
1758             LOG.severe(e.toString());
1759
1760             XcosDialogs.couldNotSaveFile(this);
1761         }
1762
1763         updateTabTitle();
1764         info(XcosMessages.EMPTY_INFO);
1765         return isSuccess;
1766     }
1767
1768     /**
1769      * Perform post loading initialization.
1770      *
1771      * @param file
1772      *            the loaded file
1773      */
1774     public void postLoad(final File file) {
1775         final String name = file.getName();
1776
1777         if (XcosFileType.getAvailableSaveFormats().contains(XcosFileType.findFileType(file))) {
1778             setSavedFile(file);
1779         }
1780         setTitle(name.substring(0, name.lastIndexOf('.')));
1781         setModified(false);
1782         updateTabTitle();
1783
1784         fireEvent(new mxEventObject(mxEvent.ROOT));
1785
1786         info(XcosMessages.EMPTY_INFO);
1787     }
1788
1789     @Override
1790     public void setSavedFile(File savedFile) {
1791         super.setSavedFile(savedFile);
1792
1793         if (savedFile != null) {
1794             JavaController controller = new JavaController();
1795             controller.setObjectProperty(getUID(), getKind(), ObjectProperties.PATH, savedFile.getAbsolutePath());
1796         }
1797     }
1798
1799
1800     /**
1801      * Set the title of the diagram
1802      *
1803      * @param title
1804      *            the title
1805      * @see org.scilab.modules.graph.ScilabGraph#setTitle(java.lang.String)
1806      */
1807     @Override
1808     public void setTitle(final String title) {
1809         super.setTitle(title);
1810
1811         JavaController controller = new JavaController();
1812         controller.setObjectProperty(getUID(), getKind(), ObjectProperties.TITLE, title);
1813     }
1814
1815     /**
1816      * Update the title
1817      */
1818     public void updateTabTitle() {
1819         // get the modifier string
1820         final String modified;
1821         if (isModified()) {
1822             modified = "*";
1823         } else {
1824             modified = "";
1825         }
1826
1827         // get the title string
1828         final String title = getTitle();
1829
1830         // get the path
1831         CharSequence formattedPath = "";
1832         if (getKind() == Kind.DIAGRAM) {
1833             final File savedFile = getSavedFile();
1834             if (savedFile != null) {
1835                 try {
1836                     final String path = savedFile.getCanonicalPath();
1837                     formattedPath = new StringBuilder().append(" (").append(path).append(')');
1838                 } catch (final IOException e) {
1839                     LOG.warning(e.toString());
1840                 }
1841             }
1842         }
1843
1844         // Product name
1845         final String product = Xcos.TRADENAME;
1846
1847         final String tabTitle = new StringBuilder().append(modified).append(title).append(formattedPath).append(" - ").append(product).toString();
1848
1849         final SwingScilabDockablePanel tab = ScilabTabFactory.getInstance().getFromCache(getGraphTab());
1850         if (tab != null) {
1851             tab.setName(tabTitle);
1852         }
1853     }
1854
1855     /**
1856      * Load a file with different method depending on it extension
1857      *
1858      * @param controller
1859      *            the used controller
1860      * @param file
1861      *            File to load (can be null)
1862      */
1863     public void transformAndLoadFile(final JavaController controller, final String file) {
1864         final File f;
1865         final XcosFileType filetype;
1866         if (file != null) {
1867             f = new File(file);
1868             filetype = XcosFileType.findFileType(f);
1869         } else {
1870             f = null;
1871             filetype = null;
1872         }
1873         new SwingWorker<XcosDiagram, ActionEvent>() {
1874             int counter = 0;
1875             final Timer t = new Timer(1000, new ActionListener() {
1876                 @Override
1877                 public void actionPerformed(ActionEvent e) {
1878                     counter = (counter + 1) % (XcosMessages.DOTS.length() + 1);
1879                     String str = XcosMessages.LOADING_DIAGRAM + XcosMessages.DOTS.substring(0, counter);
1880
1881                     XcosDiagram.this.info(str);
1882                 }
1883             });
1884
1885             @Override
1886             protected XcosDiagram doInBackground() {
1887                 t.start();
1888
1889                 final Xcos instance = Xcos.getInstance();
1890                 XcosDiagram diag = XcosDiagram.this;
1891
1892                 diag.setReadOnly(true);
1893
1894                 /*
1895                  * Load, log errors and notify
1896                  */
1897                 synchronized (instance) {
1898                     try {
1899
1900                         if (f != null && filetype != null) {
1901                             filetype.load(file, XcosDiagram.this);
1902                         } else {
1903                             XcosCellFactory.insertChildren(controller, XcosDiagram.this);
1904                         }
1905
1906                         instance.setLastError("");
1907                     } catch (Exception e) {
1908                         e.printStackTrace();
1909
1910                         Throwable ex = e;
1911                         while (ex instanceof RuntimeException) {
1912                             ex = ex.getCause();
1913                         }
1914                         instance.setLastError(ex.getMessage());
1915                     }
1916                     instance.notify();
1917                 }
1918
1919                 return diag;
1920             }
1921
1922             @Override
1923             protected void done() {
1924                 t.stop();
1925                 XcosDiagram.this.setReadOnly(false);
1926                 XcosDiagram.this.getUndoManager().clear();
1927                 XcosDiagram.this.refresh();
1928
1929                 /*
1930                  * Load has finished
1931                  */
1932                 if (f != null && filetype != null) {
1933                     postLoad(f);
1934                 }
1935                 XcosDiagram.this.info(XcosMessages.EMPTY_INFO);
1936             }
1937
1938         } .execute();
1939     }
1940
1941     /**
1942      * Getting the root diagram of a decomposed diagram
1943      *
1944      * @return Root parent of the whole parent
1945      */
1946     public XcosDiagram getRootDiagram() {
1947         if (getKind() == Kind.DIAGRAM) {
1948             return this;
1949         }
1950
1951         JavaController controller = new JavaController();
1952         long[] parent = new long[1];
1953         controller.getObjectProperty(getUID(), getKind(), ObjectProperties.PARENT_DIAGRAM, parent);
1954
1955         Collection<XcosDiagram> diagrams = Xcos.getInstance().getDiagrams(parent[0]);
1956         return diagrams.stream().filter(d -> d.getUID() == parent[0]).findFirst().get();
1957     }
1958
1959     /**
1960      * Returns the tooltip to be used for the given cell.
1961      *
1962      * @param cell
1963      *            block
1964      * @return cell tooltip
1965      */
1966     @Override
1967     public String getToolTipForCell(final Object cell) {
1968         if (cell instanceof XcosCell) {
1969             return String.valueOf(((XcosCell) cell).getUID());
1970         }
1971         return "";
1972     }
1973
1974     /**
1975      * Display the message in info bar.
1976      *
1977      * @param message
1978      *            Information
1979      */
1980     public void info(final String message) {
1981         final XcosTab tab = XcosTab.get(this);
1982
1983         if (tab != null && tab.getInfoBar() != null) {
1984             tab.getInfoBar().setText(message);
1985         }
1986     }
1987
1988     /**
1989      * Display the message into an error popup
1990      *
1991      * @param message
1992      *            Error of the message
1993      */
1994     public void error(final String message) {
1995         JOptionPane.showMessageDialog(getAsComponent(), message, XcosMessages.XCOS, JOptionPane.ERROR_MESSAGE);
1996     }
1997
1998     /**
1999      * Find the block corresponding to the given uid and display a warning message.
2000      *
2001      * @param uid
2002      *            - A String as UID.
2003      * @param message
2004      *            - The message to display.
2005      */
2006     public void warnCellByUID(final String uid, final String message) {
2007         final Object cell = ((mxGraphModel) getModel()).getCell(uid);
2008         if (cell == null) {
2009             return;
2010         }
2011
2012         if (GraphicsEnvironment.isHeadless()) {
2013             System.err.printf("%s at %s\n    %s: %s\n", "warnCell", getRootDiagram().getTitle(), uid, message);
2014             return;
2015         }
2016
2017         // open the tab
2018         if (XcosTab.get(this) == null) {
2019             XcosTab.restore(this);
2020         }
2021
2022         if (message.isEmpty()) {
2023             getAsComponent().clearCellOverlays(cell);
2024         } else {
2025             getAsComponent().setCellWarning(cell, message, null, true);
2026         }
2027     }
2028
2029     @Override
2030     public File getSavedFile() {
2031         if (getKind() == Kind.DIAGRAM) {
2032             return super.getSavedFile();
2033         } else {
2034             return getRootDiagram().getSavedFile();
2035         }
2036     }
2037
2038     /**
2039      * Read the applicable context on this diagram.
2040      * <p>
2041      * This function retrieve the current diagram's context and all its parent
2042      * @return the full context
2043      */
2044     public String[] getContext() {
2045         final ArrayList<String> allContext = new ArrayList<>();
2046         final Stack<ScicosObjectOwner> hierarchy = lookForHierarchy(new ScicosObjectOwner(getUID(), getKind()));
2047
2048         final JavaController controller = new JavaController();
2049         final VectorOfString context = new VectorOfString();
2050
2051         hierarchy.stream().forEach(o -> {
2052             controller.getObjectProperty(o.getUID(), o.getKind(), ObjectProperties.DIAGRAM_CONTEXT, context);
2053
2054             final int length = context.size();
2055             for (int i = 0; i < length; i++) {
2056                 allContext.add(context.get(i));
2057             }
2058             allContext.add("");
2059         });
2060
2061         return allContext.toArray(new String[allContext.size()]);
2062     }
2063
2064
2065     /**
2066      * Evaluate the current context
2067      *
2068      * @return The resulting data. Keys are variable names and Values are evaluated values.
2069      */
2070     public Map<String, String> evaluateContext() {
2071         Map<String, String> result = Collections.emptyMap();
2072         final ScilabDirectHandler handler = ScilabDirectHandler.acquire();
2073         if (handler == null) {
2074             return result;
2075         }
2076
2077         try {
2078             // first write the context strings
2079             handler.writeContext(getContext());
2080
2081             // evaluate using script2var
2082             ScilabInterpreterManagement.synchronousScilabExec(ScilabDirectHandler.CONTEXT + " = script2var(" + ScilabDirectHandler.CONTEXT + ", struct());");
2083
2084             // read the structure
2085             result = handler.readContext();
2086         } catch (final InterpreterException e) {
2087             info("Unable to evaluate the contexte");
2088             e.printStackTrace();
2089         } finally {
2090             handler.release();
2091         }
2092
2093         return result;
2094     }
2095
2096     /**
2097      * Returns true if the given cell is a not a block nor a port.
2098      *
2099      * @param cell
2100      *            the drop target
2101      * @param cells
2102      *            the cells to be dropped
2103      * @return the drop status
2104      * @see com.mxgraph.view.mxGraph#isValidDropTarget(java.lang.Object, java.lang.Object[])
2105      */
2106     @Override
2107     public boolean isValidDropTarget(final Object cell, final Object[] cells) {
2108         return !(cell instanceof BasicBlock) && !(cell instanceof BasicBlock) && !(cell instanceof BasicPort) && super.isValidDropTarget(cell, cells);
2109     }
2110
2111     /**
2112      * Construct a new selection model used on this graph.
2113      *
2114      * @return a new selection model instance.
2115      * @see com.mxgraph.view.mxGraph#createSelectionModel()
2116      */
2117     @Override
2118     protected mxGraphSelectionModel createSelectionModel() {
2119         return new mxGraphSelectionModel(this) {
2120             /**
2121              * When we only want to select a cell which is a port, select the parent block.
2122              *
2123              * @param cell
2124              *            the cell
2125              * @see com.mxgraph.view.mxGraphSelectionModel#setCell(java.lang.Object)
2126              */
2127             @Override
2128             public void setCell(final Object cell) {
2129                 final Object current;
2130                 if (cell instanceof BasicPort) {
2131                     current = getModel().getParent(cell);
2132                 } else {
2133                     current = cell;
2134                 }
2135                 super.setCell(current);
2136             }
2137         };
2138     }
2139 }