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