Xcos: reduce complexity to detect wrong input block numbers
[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-en.txt
11  *
12  */
13
14 package org.scilab.modules.xcos.graph;
15
16 import static org.scilab.modules.xcos.utils.FileUtils.exists;
17
18 import java.awt.Color;
19 import java.awt.event.ActionEvent;
20 import java.awt.event.ActionListener;
21 import java.beans.PropertyChangeEvent;
22 import java.beans.PropertyChangeListener;
23 import java.beans.PropertyVetoException;
24 import java.io.File;
25 import java.io.IOException;
26 import java.rmi.server.UID;
27 import java.util.ArrayList;
28 import java.util.Arrays;
29 import java.util.Collection;
30 import java.util.Collections;
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.regex.Pattern;
39
40 import javax.swing.JFileChooser;
41 import javax.swing.JOptionPane;
42 import javax.swing.SwingWorker;
43 import javax.swing.Timer;
44 import javax.swing.filechooser.FileNameExtensionFilter;
45 import javax.xml.transform.Transformer;
46 import javax.xml.transform.TransformerException;
47 import javax.xml.transform.TransformerFactory;
48 import javax.xml.transform.dom.DOMSource;
49 import javax.xml.transform.stream.StreamResult;
50
51 import org.apache.commons.logging.Log;
52 import org.apache.commons.logging.LogFactory;
53 import org.scilab.modules.action_binding.highlevel.ScilabInterpreterManagement;
54 import org.scilab.modules.action_binding.highlevel.ScilabInterpreterManagement.InterpreterException;
55 import org.scilab.modules.commons.xml.ScilabTransformerFactory;
56 import org.scilab.modules.graph.ScilabGraph;
57 import org.scilab.modules.graph.utils.ScilabGraphConstants;
58 import org.scilab.modules.gui.bridge.filechooser.SwingScilabFileChooser;
59 import org.scilab.modules.gui.bridge.tab.SwingScilabTab;
60 import org.scilab.modules.gui.filechooser.FileChooser;
61 import org.scilab.modules.gui.filechooser.ScilabFileChooser;
62 import org.scilab.modules.gui.messagebox.ScilabModalDialog;
63 import org.scilab.modules.gui.messagebox.ScilabModalDialog.AnswerOption;
64 import org.scilab.modules.gui.messagebox.ScilabModalDialog.ButtonType;
65 import org.scilab.modules.gui.messagebox.ScilabModalDialog.IconType;
66 import org.scilab.modules.gui.tabfactory.ScilabTabFactory;
67 import org.scilab.modules.gui.utils.BarUpdater;
68 import org.scilab.modules.types.ScilabMList;
69 import org.scilab.modules.xcos.Xcos;
70 import org.scilab.modules.xcos.XcosTab;
71 import org.scilab.modules.xcos.block.AfficheBlock;
72 import org.scilab.modules.xcos.block.BasicBlock;
73 import org.scilab.modules.xcos.block.BlockFactory;
74 import org.scilab.modules.xcos.block.BlockFactory.BlockInterFunction;
75 import org.scilab.modules.xcos.block.SplitBlock;
76 import org.scilab.modules.xcos.block.SuperBlock;
77 import org.scilab.modules.xcos.block.TextBlock;
78 import org.scilab.modules.xcos.block.io.ContextUpdate;
79 import org.scilab.modules.xcos.configuration.ConfigurationManager;
80 import org.scilab.modules.xcos.graph.swing.GraphComponent;
81 import org.scilab.modules.xcos.io.XcosCodec;
82 import org.scilab.modules.xcos.io.scicos.DiagramElement;
83 import org.scilab.modules.xcos.io.scicos.H5RWHandler;
84 import org.scilab.modules.xcos.link.BasicLink;
85 import org.scilab.modules.xcos.link.commandcontrol.CommandControlLink;
86 import org.scilab.modules.xcos.link.explicit.ExplicitLink;
87 import org.scilab.modules.xcos.link.implicit.ImplicitLink;
88 import org.scilab.modules.xcos.port.BasicPort;
89 import org.scilab.modules.xcos.port.BasicPort.Type;
90 import org.scilab.modules.xcos.port.Orientation;
91 import org.scilab.modules.xcos.port.PortCheck;
92 import org.scilab.modules.xcos.port.command.CommandPort;
93 import org.scilab.modules.xcos.port.control.ControlPort;
94 import org.scilab.modules.xcos.port.input.ExplicitInputPort;
95 import org.scilab.modules.xcos.port.input.ImplicitInputPort;
96 import org.scilab.modules.xcos.port.output.ExplicitOutputPort;
97 import org.scilab.modules.xcos.port.output.ImplicitOutputPort;
98 import org.scilab.modules.xcos.utils.BlockPositioning;
99 import org.scilab.modules.xcos.utils.FileUtils;
100 import org.scilab.modules.xcos.utils.XcosConstants;
101 import org.scilab.modules.xcos.utils.XcosDialogs;
102 import org.scilab.modules.xcos.utils.XcosEvent;
103 import org.scilab.modules.xcos.utils.XcosFileType;
104 import org.scilab.modules.xcos.utils.XcosMessages;
105
106 import com.mxgraph.model.mxCell;
107 import com.mxgraph.model.mxGeometry;
108 import com.mxgraph.model.mxGraphModel;
109 import com.mxgraph.model.mxGraphModel.Filter;
110 import com.mxgraph.model.mxGraphModel.mxChildChange;
111 import com.mxgraph.model.mxGraphModel.mxStyleChange;
112 import com.mxgraph.model.mxICell;
113 import com.mxgraph.model.mxIGraphModel.mxAtomicGraphModelChange;
114 import com.mxgraph.util.mxEvent;
115 import com.mxgraph.util.mxEventObject;
116 import com.mxgraph.util.mxPoint;
117 import com.mxgraph.util.mxRectangle;
118 import com.mxgraph.util.mxUndoableEdit;
119 import com.mxgraph.util.mxUndoableEdit.mxUndoableChange;
120 import com.mxgraph.view.mxGraphSelectionModel;
121 import com.mxgraph.view.mxMultiplicity;
122 import com.mxgraph.view.mxStylesheet;
123
124 /**
125  * The base class for a diagram. This class contains jgraphx + Scicos data.
126  */
127 public class XcosDiagram extends ScilabGraph {
128     private static final Log LOG = LogFactory.getLog(XcosDiagram.class);
129
130     private static final String MODIFIED = "modified";
131     private static final String CELLS = "cells";
132
133     /*
134      * diagram data
135      */
136
137     // the associated parameters
138     private ScicosParameters scicosParameters;
139
140     // the scicos engine current status
141     private final transient CompilationEngineStatus engine;
142
143     private transient String viewPortTab;
144     private transient String diagramTab;
145
146     /**
147      * Constructor
148      */
149     public XcosDiagram() {
150         super();
151
152         // Scicos related setup
153         engine = new CompilationEngineStatus();
154         setScicosParameters(new ScicosParameters());
155
156         // Add a default listener to update the modification status when
157         // something has changed on the ScicosParameters
158         scicosParameters
159                 .addPropertyChangeListener(new PropertyChangeListener() {
160                     @Override
161                     public void propertyChange(final PropertyChangeEvent evt) {
162                         setModified(true);
163                     }
164                 });
165
166         setComponent(new GraphComponent(this));
167         initComponent();
168         installStylesheet();
169
170         // Forbid disconnecting cells once it is connected.
171         setCellsDisconnectable(false);
172
173         // Forbid pending edges.
174         setAllowDanglingEdges(false);
175
176         // Cannot connect port to itself.
177         setAllowLoops(false);
178
179         // Override isCellResizable to filter what the user can resize
180         setCellsResizable(true);
181
182         /* Labels use HTML if not equal to interface function name */
183         setHtmlLabels(true);
184         /*
185          * by default every label is movable, see
186          * XcosDiagram##isLabelMovable(java.lang.Object) for restrictions
187          */
188         setVertexLabelsMovable(true);
189         setEdgeLabelsMovable(true);
190
191         //
192         setCloneInvalidEdges(true);
193
194         // Override isCellEditable to filter what the user can edit
195         setCellsEditable(true);
196
197         setConnectableEdges(true);
198
199         // Do not clear edge points on connect
200         setResetEdgesOnConnect(false);
201
202         setMultiplicities();
203
204         setAutoOrigin(true);
205
206         // Add a listener to track when model is changed
207         getModel().addListener(mxEvent.CHANGE, ModelTracker.getInstance());
208
209         ((mxCell) getDefaultParent()).setId((new UID()).toString());
210         ((mxCell) getModel().getRoot()).setId((new UID()).toString());
211     }
212
213     /*
214      * Static helpers
215      */
216
217     /**
218      * Only return the instanceof klass
219      * 
220      * @param selection
221      *            the selection to filter out
222      * @param klass
223      *            the class selector
224      * @return the selection with only klass instance.
225      */
226     public static Object[] filterByClass(final Object[] selection,
227             final Class<BasicBlock> klass) {
228         return mxGraphModel.filterCells(selection, new mxGraphModel.Filter() {
229             @Override
230             public boolean filter(Object cell) {
231                 return klass.isInstance(cell);
232             }
233         });
234     }
235
236     /*
237      * Static diagram listeners
238      */
239
240     /**
241      * CellAddedTracker Called when mxEvents.CELLS_ADDED is fired.
242      */
243     private static final class CellAddedTracker implements mxIEventListener {
244
245         private static CellAddedTracker instance;
246
247         /**
248          * Default constructor
249          */
250         private CellAddedTracker() {
251         }
252
253         /**
254          * @return the instance
255          */
256         public static synchronized CellAddedTracker getInstance() {
257             if (instance == null) {
258                 instance = new CellAddedTracker();
259             }
260             return instance;
261         }
262
263         /**
264          * Update block values on add
265          * 
266          * @param source
267          *            the source instance
268          * @param evt
269          *            the event data
270          * @see com.mxgraph.util.mxEventSource.mxIEventListener#invoke(java.lang.Object,
271          *      com.mxgraph.util.mxEventObject)
272          */
273         @Override
274         public void invoke(final Object source, final mxEventObject evt) {
275             final XcosDiagram diagram = (XcosDiagram) source;
276             final Object[] cells = (Object[]) evt.getProperty(CELLS);
277
278             diagram.getModel().beginUpdate();
279             try {
280                 final Filter filter = new Filter() {
281                     @Override
282                     public boolean filter(Object cell) {
283                         if (cell instanceof BasicBlock) {
284                             // Update parent on cell addition
285                             ((BasicBlock) cell).setParentDiagram(diagram);
286                         }
287                         return false;
288                     }
289                 };
290
291                 for (int i = 0; i < cells.length; ++i) {
292                     mxGraphModel.filterDescendants(diagram.getModel(), filter,
293                             cells[i]);
294                 }
295             } finally {
296                 diagram.getModel().endUpdate();
297             }
298         }
299     }
300
301     /**
302      * CellResizedTracker Called when mxEvents.CELLS_RESIZED is fired.
303      */
304     private static final class CellResizedTracker implements mxIEventListener {
305
306         private static CellResizedTracker instance;
307
308         /**
309          * Constructor
310          */
311         private CellResizedTracker() {
312         }
313
314         /**
315          * @return the instance
316          */
317         public static CellResizedTracker getInstance() {
318             if (instance == null) {
319                 instance = new CellResizedTracker();
320             }
321             return instance;
322         }
323
324         /**
325          * Update the cell view
326          * 
327          * @param source
328          *            the source instance
329          * @param evt
330          *            the event data
331          * @see com.mxgraph.util.mxEventSource.mxIEventListener#invoke(java.lang.Object,
332          *      com.mxgraph.util.mxEventObject)
333          */
334         @Override
335         public void invoke(final Object source, final mxEventObject evt) {
336             final XcosDiagram diagram = (XcosDiagram) source;
337             final Object[] cells = (Object[]) evt.getProperty(CELLS);
338
339             diagram.getModel().beginUpdate();
340             try {
341                 for (int i = 0; i < cells.length; ++i) {
342                     if (cells[i] instanceof BasicBlock) {
343                         BlockPositioning.updateBlockView((BasicBlock) cells[i]);
344                     }
345                 }
346             } finally {
347                 diagram.getModel().endUpdate();
348             }
349         }
350     }
351
352     /**
353      * ModelTracker called when mxEvents.CHANGE occurs on a model
354      */
355     private static final class ModelTracker implements mxIEventListener {
356         private static ModelTracker instance;
357
358         /**
359          * Constructor
360          */
361         private ModelTracker() {
362         }
363
364         /**
365          * @return the instance
366          */
367         public static ModelTracker getInstance() {
368             if (instance == null) {
369                 instance = new ModelTracker();
370             }
371             return instance;
372         }
373
374         /**
375          * Fire cell value update on any change
376          * 
377          * @param source
378          *            the source instance
379          * @param evt
380          *            the event data
381          * @see com.mxgraph.util.mxEventSource.mxIEventListener#invoke(java.lang.Object,
382          *      com.mxgraph.util.mxEventObject)
383          */
384         @Override
385         public void invoke(final Object source, final mxEventObject evt) {
386             final mxGraphModel model = (mxGraphModel) source;
387             @SuppressWarnings("unchecked")
388             final List<mxAtomicGraphModelChange> changes = (List<mxAtomicGraphModelChange>) (evt
389                     .getProperty("changes"));
390
391             final List<Object> objects = new ArrayList<Object>();
392             model.beginUpdate();
393             try {
394
395                 for (int i = 0; i < changes.size(); ++i) {
396                     if (changes.get(i) instanceof mxChildChange) {
397                         if (((mxChildChange) changes.get(i)).getChild() instanceof SplitBlock) {
398                             continue;
399                         }
400
401                         if (((mxChildChange) changes.get(i)).getChild() instanceof BasicBlock) {
402                             final BasicBlock currentCell = (BasicBlock) ((mxChildChange) changes
403                                     .get(i)).getChild();
404                             objects.add(currentCell);
405                         }
406                     }
407                 }
408                 if (!objects.isEmpty()) {
409                     final Object[] firedCells = new Object[objects.size()];
410                     for (int j = 0; j < objects.size(); ++j) {
411                         firedCells[j] = objects.get(j);
412                     }
413
414                     model.fireEvent(new mxEventObject(
415                             XcosEvent.FORCE_CELL_VALUE_UPDATE, CELLS,
416                             firedCells));
417                 }
418
419             } finally {
420                 model.endUpdate();
421             }
422         }
423     }
424
425     /**
426      * SuperBlockUpdateTracker Called when adding some port in a SuperBlock
427      * diagram to update current sub-diagram (i.e SuperBlock) representation.
428      */
429     private static final class SuperBlockUpdateTracker implements
430             mxIEventListener {
431         private static SuperBlockUpdateTracker instance;
432
433         /**
434          * Constructor
435          */
436         private SuperBlockUpdateTracker() {
437         }
438
439         /**
440          * @return the instance
441          */
442         public static SuperBlockUpdateTracker getInstance() {
443             if (instance == null) {
444                 instance = new SuperBlockUpdateTracker();
445             }
446             return instance;
447         }
448
449         /**
450          * Update the superblock values (rpar) on update
451          * 
452          * @param source
453          *            the source instance
454          * @param evt
455          *            the event data
456          * @see com.mxgraph.util.mxEventSource.mxIEventListener#invoke(java.lang.Object,
457          *      com.mxgraph.util.mxEventObject)
458          */
459         @Override
460         public void invoke(final Object source, final mxEventObject evt) {
461             assert evt.getProperty(XcosConstants.EVENT_BLOCK_UPDATED) instanceof SuperBlock;
462
463             final XcosDiagram diagram = (XcosDiagram) source;
464             final SuperBlock updatedBlock = (SuperBlock) evt
465                     .getProperty(XcosConstants.EVENT_BLOCK_UPDATED);
466
467             assert diagram == updatedBlock.getParentDiagram();
468
469             /*
470              * TODO: is this really necessary on the EDT ? It might be a huge
471              * improvement of the user experience.
472              */
473             updatedBlock.setRealParameters(new DiagramElement()
474                     .encode(updatedBlock.getChild()));
475
476             if (diagram instanceof SuperBlockDiagram) {
477                 final SuperBlock parentBlock = ((SuperBlockDiagram) diagram)
478                         .getContainer();
479                 parentBlock.getParentDiagram()
480                         .fireEvent(
481                                 new mxEventObject(
482                                         XcosEvent.SUPER_BLOCK_UPDATED,
483                                         XcosConstants.EVENT_BLOCK_UPDATED,
484                                         parentBlock));
485             }
486
487             BlockPositioning.updateBlockView(updatedBlock);
488
489             diagram.getView().clear(updatedBlock, true, true);
490             diagram.getView().validate();
491             diagram.repaint();
492         }
493     }
494
495     /**
496      * Update the modified block on undo/redo
497      */
498     private static final class UndoUpdateTracker implements mxIEventListener {
499         private static UndoUpdateTracker instance;
500
501         /**
502          * Constructor
503          */
504         public UndoUpdateTracker() {
505         }
506
507         /**
508          * @return the instance
509          */
510         public static UndoUpdateTracker getInstance() {
511             if (instance == null) {
512                 instance = new UndoUpdateTracker();
513             }
514             return instance;
515         }
516
517         /**
518          * Update the block and style on undo
519          * 
520          * @param source
521          *            the source instance
522          * @param evt
523          *            the event data
524          * @see com.mxgraph.util.mxEventSource.mxIEventListener#invoke(java.lang.Object,
525          *      com.mxgraph.util.mxEventObject)
526          */
527         @Override
528         public void invoke(final Object source, final mxEventObject evt) {
529             final mxUndoableEdit edit = (mxUndoableEdit) evt
530                     .getProperty(ScilabGraphConstants.EVENT_CHANGE_EDIT);
531
532             final mxGraphModel model = (mxGraphModel) edit.getSource();
533             final List<mxUndoableChange> changes = edit.getChanges();
534
535             final Object[] changedCells = getSelectionCellsForChanges(changes,
536                     model);
537             model.beginUpdate();
538             try {
539                 for (final Object object : changedCells) {
540                     if (object instanceof BasicBlock) {
541                         final BasicBlock current = (BasicBlock) object;
542
543                         // When we change the style property we have to update
544                         // some BasiBlock fields
545                         if (changes.get(0) instanceof mxStyleChange) {
546                             current.updateFieldsFromStyle();
547                         }
548
549                         // Update the block position
550                         BlockPositioning.updateBlockView(current);
551                     }
552                 }
553             } finally {
554                 model.endUpdate();
555             }
556         }
557     }
558
559     /**
560      * Refresh each block on modification (update port position, etc...)
561      */
562     private static final class RefreshBlockTracker implements mxIEventListener {
563         private static RefreshBlockTracker instance;
564
565         /**
566          * Default constructor
567          */
568         private RefreshBlockTracker() {
569         }
570
571         /**
572          * @return the instance
573          */
574         public static RefreshBlockTracker getInstance() {
575             if (instance == null) {
576                 instance = new RefreshBlockTracker();
577             }
578             return instance;
579         }
580
581         /**
582          * Refresh the block on port added
583          * 
584          * @param sender
585          *            the diagram
586          * @param evt
587          *            the event
588          * @see com.mxgraph.util.mxEventSource.mxIEventListener#invoke(java.lang.Object,
589          *      com.mxgraph.util.mxEventObject)
590          */
591         @Override
592         public void invoke(Object sender, mxEventObject evt) {
593             final XcosDiagram diagram = (XcosDiagram) sender;
594
595             diagram.getModel().beginUpdate();
596             try {
597                 final BasicBlock updatedBlock = (BasicBlock) evt
598                         .getProperty(XcosConstants.EVENT_BLOCK_UPDATED);
599                 BlockPositioning.updateBlockView(updatedBlock);
600
601                 diagram.getView().clear(updatedBlock, true, true);
602
603                 diagram.getAsComponent().validateGraph();
604                 diagram.getView().validate();
605             } finally {
606                 diagram.getModel().endUpdate();
607             }
608         }
609     }
610
611     /**
612      * Hook method that creates the new edge for insertEdge. This implementation
613      * does not set the source and target of the edge, these are set when the
614      * edge is added to the model.
615      * 
616      * @param parent
617      *            Cell that specifies the parent of the new edge.
618      * @param id
619      *            Optional string that defines the Id of the new edge.
620      * @param value
621      *            Object to be used as the user object.
622      * @param source
623      *            Cell that defines the source of the edge.
624      * @param target
625      *            Cell that defines the target of the edge.
626      * @param style
627      *            Optional string that defines the cell style.
628      * @return Returns the new edge to be inserted.
629      * @see com.mxgraph.view.mxGraph#createEdge(java.lang.Object,
630      *      java.lang.String, java.lang.Object, java.lang.Object,
631      *      java.lang.Object, java.lang.String)
632      */
633     @Override
634     public Object createEdge(Object parent, String id, Object value,
635             Object source, Object target, String style) {
636         Object ret = null;
637
638         if (source instanceof BasicPort) {
639             BasicPort src = (BasicPort) source;
640             BasicLink link = null;
641
642             if (src.getType() == Type.EXPLICIT) {
643                 link = new ExplicitLink();
644             } else if (src.getType() == Type.IMPLICIT) {
645                 link = new ImplicitLink();
646             } else {
647                 link = new CommandControlLink();
648             }
649
650             // allocate the associated geometry
651             link.setGeometry(new mxGeometry());
652             ret = link;
653         } else if (source instanceof SplitBlock) {
654             SplitBlock src = (SplitBlock) source;
655             return createEdge(parent, id, value, src.getIn(), target, style);
656         } else if (source instanceof BasicLink) {
657             BasicLink src = (BasicLink) source;
658             BasicLink link = null;
659
660             try {
661                 link = src.getClass().newInstance();
662
663                 // allocate the associated geometry
664                 link.setGeometry(new mxGeometry());
665
666             } catch (InstantiationException e) {
667                 LOG.error(e);
668             } catch (IllegalAccessException e) {
669                 LOG.error(e);
670             }
671
672             ret = link;
673         }
674
675         if (ret == null) {
676             ret = super.createEdge(parent, id, value, source, target, style);
677             LOG.debug("Creating a non typed edge");
678         }
679
680         return ret;
681     }
682
683     /**
684      * Add an edge from a source to the target.
685      * 
686      * @param cell
687      *            the edge to add (may be null)
688      * @param parent
689      *            the parent of the source and the target
690      * @param source
691      *            the source cell
692      * @param target
693      *            the target cell
694      * @param index
695      *            the index of the edge
696      * @return the added edge or null.
697      * @see com.mxgraph.view.mxGraph#addEdge(java.lang.Object, java.lang.Object,
698      *      java.lang.Object, java.lang.Object, java.lang.Integer)
699      */
700     @Override
701     public Object addCell(Object cell, Object parent, Integer index,
702             Object source, Object target) {
703
704         // already connected edge or normal block
705         if (source == null && target == null) {
706             return super.addCell(cell, parent, index, source, target);
707         }
708
709         // Command -> Control
710         if (source instanceof CommandPort && target instanceof ControlPort
711                 && cell instanceof CommandControlLink) {
712             return super.addCell(cell, parent, index, source, target);
713         }
714
715         // Control -> Command
716         // Switch source and target !
717         if (target instanceof CommandPort && source instanceof ControlPort
718                 && cell instanceof CommandControlLink) {
719             BasicLink current = (BasicLink) cell;
720             current.invertDirection();
721
722             return super.addCell(cell, parent, index, target, source);
723         }
724
725         // ExplicitOutput -> ExplicitInput
726         if (source instanceof ExplicitOutputPort
727                 && target instanceof ExplicitInputPort
728                 && cell instanceof ExplicitLink) {
729             return super.addCell(cell, parent, index, source, target);
730         }
731         // ExplicitInput -> ExplicitOutput
732         // Switch source and target !
733         if (target instanceof ExplicitOutputPort
734                 && source instanceof ExplicitInputPort
735                 && cell instanceof ExplicitLink) {
736             BasicLink current = (BasicLink) cell;
737             current.invertDirection();
738
739             return super.addCell(cell, parent, index, target, source);
740         }
741
742         // ImplicitOutput -> ImplicitInput
743         if (source instanceof ImplicitOutputPort
744                 && target instanceof ImplicitInputPort
745                 && cell instanceof ImplicitLink) {
746             return super.addCell(cell, parent, index, source, target);
747         }
748         // ImplicitInput -> ImplicitOutput
749         // Switch source and target !
750         if (target instanceof ImplicitOutputPort
751                 && source instanceof ImplicitInputPort
752                 && 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
761                 && target instanceof ImplicitInputPort
762                 && cell instanceof ImplicitLink) {
763             return super.addCell(cell, parent, index, source, target);
764         }
765         // ImplicitOutputPort -> ImplicitOutput
766         // Switch source and target !
767         if (target instanceof ImplicitOutputPort
768                 && source instanceof ImplicitOutputPort
769                 && cell instanceof ImplicitLink) {
770             BasicLink current = (BasicLink) cell;
771             current.invertDirection();
772
773             return super.addCell(cell, parent, index, target, source);
774         }
775
776         /*
777          * Split management
778          */
779
780         // ExplicitLink -> ExplicitInputPort
781         if (source instanceof ExplicitLink
782                 && target instanceof ExplicitInputPort
783                 && cell instanceof ExplicitLink) {
784             SplitBlock split = addSplitEdge(((BasicLink) cell).getGeometry()
785                     .getSourcePoint(), (BasicLink) source);
786             return addCell(cell, parent, index, split.getOut2(), target);
787         }
788         // ExplicitOutput -> ExpliciLink
789         // Switch source and target !
790         if (target instanceof ExplicitLink
791                 && source instanceof ExplicitInputPort
792                 && cell instanceof ExplicitLink) {
793             final BasicLink current = (BasicLink) cell;
794             final SplitBlock split = addSplitEdge(current.getGeometry()
795                     .getTargetPoint(), (BasicLink) target);
796
797             current.invertDirection();
798
799             return addCell(cell, parent, index, split.getOut2(), source);
800         }
801
802         // ImplicitLink -> ImplicitInputPort
803         if (source instanceof ImplicitLink
804                 && target instanceof ImplicitInputPort
805                 && cell instanceof ImplicitLink) {
806             SplitBlock split = addSplitEdge(((BasicLink) cell).getGeometry()
807                     .getSourcePoint(), (BasicLink) source);
808             return addCell(cell, parent, index, split.getOut2(), target);
809         }
810         // ImplicitInputPort -> ImplicitLink
811         // Switch source and target !
812         if (target instanceof ImplicitLink
813                 && source instanceof ImplicitInputPort
814                 && cell instanceof ImplicitLink) {
815             final BasicLink current = (BasicLink) cell;
816             final SplitBlock split = addSplitEdge(current.getGeometry()
817                     .getTargetPoint(), (BasicLink) target);
818
819             current.invertDirection();
820
821             return addCell(cell, parent, index, split.getOut2(), source);
822         }
823
824         // ImplicitLink -> ImplicitOutputPort
825         if (source instanceof ImplicitLink
826                 && target instanceof ImplicitOutputPort
827                 && cell instanceof ImplicitLink) {
828             final BasicLink current = (BasicLink) cell;
829             final SplitBlock split = addSplitEdge(current.getGeometry()
830                     .getTargetPoint(), (BasicLink) source);
831             return addCell(cell, parent, index, split.getOut2(), source);
832         }
833         // ImplicitOutputPort -> ImplicitLink
834         // Switch source and target !
835         if (target instanceof ImplicitLink
836                 && source instanceof ImplicitOutputPort
837                 && cell instanceof ImplicitLink) {
838             final BasicLink current = (BasicLink) cell;
839             final SplitBlock split = addSplitEdge(current.getGeometry()
840                     .getTargetPoint(), (ImplicitLink) target);
841             return addCell(cell, parent, index, split.getOut2(), source);
842         }
843
844         // CommandControlLink -> ControlPort
845         if (source instanceof CommandControlLink
846                 && target instanceof ControlPort
847                 && cell instanceof CommandControlLink) {
848             SplitBlock split = addSplitEdge(((BasicLink) cell).getGeometry()
849                     .getSourcePoint(), (BasicLink) source);
850             return addCell(cell, parent, index, split.getOut2(), target);
851         }
852         // ControlPort -> CommandControlLink
853         // Switch source and target !
854         if (target instanceof CommandControlLink
855                 && source instanceof ControlPort
856                 && cell instanceof CommandControlLink) {
857             final BasicLink current = (BasicLink) cell;
858             final SplitBlock split = addSplitEdge(current.getGeometry()
859                     .getTargetPoint(), (BasicLink) target);
860
861             current.invertDirection();
862
863             return addCell(cell, parent, index, split.getOut2(), source);
864         }
865
866         if (cell instanceof BasicLink && source != null && target != null) {
867             LOG.error("Unable to add a typed link");
868             return null;
869         } else {
870             LOG.error("Adding an untyped edge");
871             return super.addCell(cell, parent, index, source, target);
872         }
873     }
874
875     /**
876      * Add a split on a edge.
877      * 
878      * @param splitPoint
879      *            the split point (center of the split block)
880      * @param link
881      *            source link
882      * @return split block
883      */
884     public SplitBlock addSplitEdge(final mxPoint splitPoint,
885             final BasicLink link) {
886         final BasicPort linkSource = (BasicPort) link.getSource();
887         final BasicPort linkTarget = (BasicPort) link.getTarget();
888
889         final SplitBlock splitBlock = (SplitBlock) BlockFactory
890                 .createBlock(BlockInterFunction.SPLIT_f);
891
892         getModel().beginUpdate();
893         try {
894             // Origin of the parent, (0,0) as default may be different in case
895             mxPoint orig = link.getParent().getGeometry();
896             if (orig == null) {
897                 orig = new mxPoint();
898             }
899
900             splitBlock.addConnection(linkSource);
901
902             addCell(splitBlock);
903             // force resize and align on the grid
904             resizeCell(splitBlock, new mxRectangle(splitPoint.getX(),
905                     splitPoint.getY(), 0, 0));
906
907             // Update old link
908
909             // get breaking segment and related point
910             mxPoint splitTr = new mxPoint(splitPoint.getX() - orig.getX(),
911                     splitPoint.getY() - orig.getY());
912             final int pos = link.findNearestSegment(splitTr);
913
914             // save points after breaking point
915             final List<mxPoint> saveStartPoints = link.getPoints(pos, true);
916             final List<mxPoint> saveEndPoints = link.getPoints(pos, false);
917
918             // remove the first end point if the position is near the split
919             // position
920             if (saveEndPoints.size() > 0) {
921                 final mxPoint p = saveEndPoints.get(0);
922                 final double dx = p.getX() - splitTr.getX();
923                 final double dy = p.getY() - splitTr.getY();
924
925                 if (!getAsComponent().isSignificant(dx, dy)) {
926                     saveEndPoints.remove(0);
927                 }
928             }
929
930             // disable events
931             getModel().beginUpdate();
932             getModel().remove(link);
933             getModel().endUpdate();
934
935             connect(linkSource, splitBlock.getIn(), saveStartPoints, orig);
936             connect(splitBlock.getOut1(), linkTarget, saveEndPoints, orig);
937
938             refresh();
939         } finally {
940             getModel().endUpdate();
941         }
942
943         return splitBlock;
944     }
945
946     /**
947      * Connect two port together with the associated points.
948      * 
949      * This method perform the connection in two step in order to generate the
950      * right UndoableChangeEdits.
951      * 
952      * @param src
953      *            the source port
954      * @param trg
955      *            the target port
956      * @param points
957      *            the points
958      * @param orig
959      *            the origin point (may be (0,0))
960      */
961     public void connect(BasicPort src, BasicPort trg, List<mxPoint> points,
962             mxPoint orig) {
963         mxGeometry geometry;
964
965         /*
966          * Add the link with a default geometry
967          */
968         final Object newLink1 = createEdge(null, null, null, src, trg, null);
969         addCell(newLink1, null, null, src, trg);
970         geometry = getModel().getGeometry(newLink1);
971         geometry.setPoints(points);
972         getModel().setGeometry(newLink1, geometry);
973
974         /*
975          * Update the geometry
976          */
977         // should be cloned to generate an event
978         geometry = (mxGeometry) getModel().getGeometry(newLink1).clone();
979         final double dx = orig.getX();
980         final double dy = orig.getY();
981
982         geometry.translate(dx, dy);
983         getModel().setGeometry(newLink1, geometry);
984     }
985
986     /**
987      * Initialize component settings for a graph.
988      * 
989      * This method *must* be used to setup the component after any
990      * reassociation.
991      */
992     public void initComponent() {
993         getAsComponent().setToolTips(true);
994
995         // This enable stop editing cells when pressing Enter.
996         getAsComponent().setEnterStopsCellEditing(false);
997
998         getAsComponent().setTolerance(1);
999
1000         getAsComponent().getViewport().setOpaque(false);
1001         getAsComponent().setBackground(Color.WHITE);
1002
1003         setGridVisible(true);
1004
1005         /*
1006          * Reinstall related listeners
1007          */
1008
1009         // Property change Listener
1010         // Will say if a diagram has been modified or not.
1011         final PropertyChangeListener p = new PropertyChangeListener() {
1012             @Override
1013             public void propertyChange(final PropertyChangeEvent e) {
1014                 if (e.getPropertyName().compareTo(MODIFIED) == 0) {
1015                     if (!e.getOldValue().equals(e.getNewValue())) {
1016                         updateTabTitle();
1017                     }
1018                 }
1019             }
1020         };
1021         getAsComponent().removePropertyChangeListener(MODIFIED, p);
1022         getAsComponent().addPropertyChangeListener(MODIFIED, p);
1023     }
1024
1025     /**
1026      * Install the default style sheet and the user stylesheet on the diagram.
1027      */
1028     public void installStylesheet() {
1029         final mxStylesheet styleSheet = Xcos.getInstance().getStyleSheet();
1030         setStylesheet(styleSheet);
1031     }
1032
1033     /**
1034      * Install the multiplicities (use for link checking)
1035      */
1036     private void setMultiplicities() {
1037         final List<mxMultiplicity> multiplicities = new ArrayList<mxMultiplicity>();
1038
1039         // Input data port
1040         multiplicities.add(new PortCheck(ExplicitInputPort.class, Collections
1041                 .unmodifiableList(new ArrayList<Class<? extends mxCell>>() {
1042                     private static final long serialVersionUID = -4987163442006736665L;
1043                     {
1044                         add(ExplicitOutputPort.class);
1045                         add(ExplicitLink.class);
1046                     }
1047                 }), XcosMessages.LINK_ERROR_EXPLICIT_IN));
1048         multiplicities.add(new PortCheck(ImplicitInputPort.class, Collections
1049                 .unmodifiableList(new ArrayList<Class<? extends mxCell>>() {
1050                     private static final long serialVersionUID = 886376532181210926L;
1051                     {
1052                         add(ImplicitOutputPort.class);
1053                         add(ImplicitInputPort.class);
1054                         add(ImplicitLink.class);
1055                     }
1056                 }), XcosMessages.LINK_ERROR_IMPLICIT_IN));
1057
1058         // Output data port
1059         multiplicities.add(new PortCheck(ExplicitOutputPort.class, Collections
1060                 .unmodifiableList(new ArrayList<Class<? extends mxCell>>() {
1061                     private static final long serialVersionUID = 4594127972486054821L;
1062                     {
1063                         add(ExplicitInputPort.class);
1064                     }
1065                 }), XcosMessages.LINK_ERROR_EXPLICIT_OUT));
1066         multiplicities.add(new PortCheck(ImplicitOutputPort.class, Collections
1067                 .unmodifiableList(new ArrayList<Class<? extends mxCell>>() {
1068                     private static final long serialVersionUID = -3719677806532507973L;
1069                     {
1070                         add(ImplicitInputPort.class);
1071                         add(ImplicitOutputPort.class);
1072                         add(ImplicitLink.class);
1073                     }
1074                 }), XcosMessages.LINK_ERROR_IMPLICIT_OUT));
1075
1076         // Control port
1077         multiplicities.add(new PortCheck(ControlPort.class, Collections
1078                 .unmodifiableList(new ArrayList<Class<? extends mxCell>>() {
1079                     private static final long serialVersionUID = 2941077191386058497L;
1080                     {
1081                         add(CommandPort.class);
1082                         add(CommandControlLink.class);
1083                     }
1084                 }), XcosMessages.LINK_ERROR_EVENT_IN));
1085
1086         // Command port
1087         multiplicities.add(new PortCheck(CommandPort.class, Collections
1088                 .unmodifiableList(new ArrayList<Class<? extends mxCell>>() {
1089                     private static final long serialVersionUID = -3470370027962480362L;
1090                     {
1091                         add(ControlPort.class);
1092                     }
1093                 }), XcosMessages.LINK_ERROR_EVENT_OUT));
1094
1095         // ExplicitLink connections
1096         multiplicities.add(new PortCheck(ExplicitLink.class, Collections
1097                 .unmodifiableList(new ArrayList<Class<? extends mxCell>>() {
1098                     private static final long serialVersionUID = 7423543162930147373L;
1099
1100                     {
1101                         add(ExplicitInputPort.class);
1102                     }
1103                 }), XcosMessages.LINK_ERROR_EVENT_OUT));
1104
1105         // ImplicitLink connections
1106         multiplicities.add(new PortCheck(ImplicitLink.class, Collections
1107                 .unmodifiableList(new ArrayList<Class<? extends mxCell>>() {
1108                     private static final long serialVersionUID = 7775100011122283282L;
1109
1110                     {
1111                         add(ImplicitInputPort.class);
1112                         add(ImplicitOutputPort.class);
1113                     }
1114                 }), XcosMessages.LINK_ERROR_EVENT_OUT));
1115
1116         // CommandControlLink connections
1117         multiplicities.add(new PortCheck(CommandControlLink.class, Collections
1118                 .unmodifiableList(new ArrayList<Class<? extends mxCell>>() {
1119                     private static final long serialVersionUID = 3260421433507192386L;
1120
1121                     {
1122                         add(ControlPort.class);
1123                     }
1124                 }), XcosMessages.LINK_ERROR_EVENT_OUT));
1125
1126         // Already connected port
1127         multiplicities.add(new PortCheck(BasicPort.class, Collections
1128                 .unmodifiableList(new ArrayList<Class<? extends mxCell>>() {
1129                     private static final long serialVersionUID = 6376349598052836660L;
1130
1131                     {
1132                         add(BasicPort.class);
1133                     }
1134                 }), XcosMessages.LINK_ERROR_ALREADY_CONNECTED));
1135
1136         setMultiplicities(multiplicities
1137                 .toArray(new mxMultiplicity[multiplicities.size()]));
1138     }
1139
1140     /**
1141      * Install all needed Listeners.
1142      */
1143     public void installListeners() {
1144         /*
1145          * First remove all listeners if present
1146          */
1147         removeListener(SuperBlockUpdateTracker.getInstance());
1148         removeListener(CellAddedTracker.getInstance());
1149         removeListener(getEngine());
1150         getModel().removeListener(getEngine());
1151         removeListener(CellResizedTracker.getInstance());
1152         getUndoManager().removeListener(UndoUpdateTracker.getInstance());
1153         removeListener(RefreshBlockTracker.getInstance());
1154
1155         // Track when superblock ask a parent refresh.
1156         addListener(XcosEvent.SUPER_BLOCK_UPDATED,
1157                 SuperBlockUpdateTracker.getInstance());
1158
1159         // Track when cells are added.
1160         addListener(mxEvent.CELLS_ADDED, CellAddedTracker.getInstance());
1161         addListener(mxEvent.CELLS_ADDED, getEngine());
1162
1163         // Track when cells are deleted.
1164         addListener(mxEvent.CELLS_REMOVED, getEngine());
1165
1166         // Track when resizing a cell.
1167         addListener(mxEvent.CELLS_RESIZED, CellResizedTracker.getInstance());
1168
1169         // Track when we have to force a Block value
1170         addListener(XcosEvent.FORCE_CELL_VALUE_UPDATE, getEngine());
1171         getModel().addListener(XcosEvent.FORCE_CELL_VALUE_UPDATE, getEngine());
1172
1173         // Update the blocks view on undo/redo
1174         getUndoManager().addListener(mxEvent.UNDO,
1175                 UndoUpdateTracker.getInstance());
1176         getUndoManager().addListener(mxEvent.REDO,
1177                 UndoUpdateTracker.getInstance());
1178
1179         // Refresh port position on update
1180         addListener(XcosEvent.ADD_PORTS, RefreshBlockTracker.getInstance());
1181
1182     }
1183
1184     /**
1185      * Removes the given cells from the graph including all connected edges if
1186      * includeEdges is true. The change is carried out using cellsRemoved.
1187      * 
1188      * @param cells
1189      *            the cells to be removed
1190      * @param includeEdges
1191      *            true if the edges must be removed, false otherwise.
1192      * @return the deleted cells
1193      * @see com.mxgraph.view.mxGraph#removeCells(java.lang.Object[], boolean)
1194      */
1195     @Override
1196     public Object[] removeCells(final Object[] cells, final boolean includeEdges) {
1197         if (cells == null || cells.length == 0) {
1198             return super.removeCells(cells, includeEdges);
1199         }
1200
1201         /*
1202          * First remove all links connected to a removed Split if applicable
1203          */
1204         final Object[] initialCells;
1205         if (includeEdges) {
1206             initialCells = addAllEdges(cells);
1207         } else {
1208             initialCells = cells;
1209         }
1210
1211         // stash used on the loop
1212         final Queue<Object> loopCells = new LinkedList<Object>(
1213                 Arrays.asList(initialCells));
1214         // the cells that need to be really
1215         final Set<Object> removedCells = new HashSet<Object>(loopCells);
1216         // couple of cells to reconnect
1217         final List<BasicPort[]> connectedCells = new ArrayList<BasicPort[]>();
1218         final List<List<mxPoint>> connectedPoints = new ArrayList<List<mxPoint>>();
1219
1220         /*
1221          * Then loop on the algorithm to select the right edges
1222          */
1223         // /!\ not bounded algorithm
1224         while (loopCells.size() > 0) {
1225             Object cell = loopCells.remove();
1226
1227             if (cell instanceof BasicLink) {
1228                 /*
1229                  * Continue on non fully connected links
1230                  */
1231                 if (((BasicLink) cell).getSource() == null) {
1232                     continue;
1233                 }
1234                 if (((BasicLink) cell).getTarget() == null) {
1235                     continue;
1236                 }
1237
1238                 /*
1239                  * Add any split to a link
1240                  */
1241                 addTerminalParent(((BasicLink) cell).getSource(), removedCells,
1242                         loopCells);
1243                 addTerminalParent(((BasicLink) cell).getTarget(), removedCells,
1244                         loopCells);
1245
1246             } else if (cell instanceof SplitBlock) {
1247                 final SplitBlock splitBlock = (SplitBlock) cell;
1248
1249                 /*
1250                  * Remove related connection or not and reconnect.
1251                  */
1252
1253                 if (splitBlock.getIn().getEdgeCount() == 0
1254                         || splitBlock.getOut1().getEdgeCount() == 0
1255                         || splitBlock.getOut2().getEdgeCount() == 0) {
1256                     // corner case, all links will be removed
1257                     continue;
1258                 }
1259
1260                 final mxICell inLink = splitBlock.getIn().getEdgeAt(0);
1261                 final mxICell out1Link = splitBlock.getOut1().getEdgeAt(0);
1262                 final mxICell out2Link = splitBlock.getOut2().getEdgeAt(0);
1263
1264                 final boolean inRemoved = removedCells.contains(inLink);
1265                 final boolean out1Removed = removedCells.contains(out1Link);
1266                 final boolean out2Removed = removedCells.contains(out2Link);
1267
1268                 /*
1269                  * Explicit case, if the in link is deleted; all the out links
1270                  * also should.
1271                  */
1272                 if (inLink instanceof ExplicitLink && inRemoved) {
1273                     if (removedCells.add(out1Link)) {
1274                         loopCells.add(out1Link);
1275                     }
1276
1277                     if (removedCells.add(out2Link)) {
1278                         loopCells.add(out2Link);
1279                     }
1280                 }
1281
1282                 /*
1283                  * Global case reconnect if not removed
1284                  */
1285                 final BasicPort[] connection;
1286                 List<mxPoint> points = null;
1287                 if (!inRemoved && !out1Removed && out2Removed) {
1288                     connection = findTerminals(inLink, out1Link, removedCells);
1289                     points = getDirectPoints(splitBlock, inLink, out1Link);
1290                 } else if (!inRemoved && out1Removed && !out2Removed) {
1291                     connection = findTerminals(inLink, out2Link, removedCells);
1292                     points = getDirectPoints(splitBlock, inLink, out2Link);
1293                 } else if (inRemoved && !out1Removed && !out2Removed) {
1294                     // only implicit or event case, log otherwise
1295                     if (out1Link instanceof ExplicitLink
1296                             || out2Link instanceof ExplicitLink) {
1297                         LOG.error("Reconnection failed for explicit links");
1298                         connection = null;
1299                     } else {
1300                         connection = findTerminals(out1Link, out2Link,
1301                                 removedCells);
1302                         points = getDirectPoints(splitBlock, out1Link, out2Link);
1303                     }
1304                 } else {
1305                     connection = null;
1306                 }
1307
1308                 if (connection != null) {
1309                     connectedCells.add(connection);
1310                     connectedPoints.add(points);
1311                 }
1312             }
1313         }
1314
1315         final Object[] ret;
1316         getModel().beginUpdate();
1317         try {
1318             ret = super.removeCells(removedCells.toArray(), includeEdges);
1319             for (int i = 0; i < connectedCells.size(); i++) {
1320                 final BasicPort[] connection = connectedCells.get(i);
1321                 final List<mxPoint> points = connectedPoints.get(i);
1322                 if (!removedCells.contains(connection[0].getParent())
1323                         && !removedCells.contains(connection[1].getParent())) {
1324                     connect(connection[0], connection[1], points, new mxPoint());
1325                 }
1326             }
1327         } finally {
1328             getModel().endUpdate();
1329         }
1330         return ret;
1331     }
1332
1333     /**
1334      * Add any terminal parent to the removed cells
1335      * 
1336      * @param terminal
1337      *            the current terminal (instance of BasicPort)
1338      * @param removedCells
1339      *            the "to be removed" set
1340      * @param loopCells
1341      *            the "while loop" set
1342      */
1343     private void addTerminalParent(mxICell terminal,
1344             Collection<Object> removedCells, Collection<Object> loopCells) {
1345         assert (terminal == null || terminal instanceof BasicPort);
1346         assert (removedCells != null);
1347         assert (loopCells != null);
1348
1349         // getting terminal parent
1350         mxICell target = null;
1351         if (terminal != null) {
1352             target = terminal.getParent();
1353         } else {
1354             target = null;
1355         }
1356
1357         // add target if applicable
1358         if (target instanceof SplitBlock) {
1359             if (removedCells.add(target)) {
1360                 loopCells.add(target);
1361             }
1362         }
1363     }
1364
1365     /**
1366      * Find the terminals when relinking the 2 links
1367      * 
1368      * This method ensure that {source, target} are not child of removed blocks.
1369      * 
1370      * @param linkSource
1371      *            the normal source link
1372      * @param linkTerminal
1373      *            the normal target link
1374      * @param removedCells
1375      *            the set of removed objects
1376      * @return the {source, target} connection
1377      */
1378     private BasicPort[] findTerminals(final mxICell linkSource,
1379             final mxICell linkTerminal, final Set<Object> removedCells) {
1380         BasicPort src = (BasicPort) linkSource.getTerminal(true);
1381         BasicPort tgt = (BasicPort) linkTerminal.getTerminal(false);
1382         if (linkSource instanceof ImplicitLink) {
1383             if (removedCells.contains(src.getParent())) {
1384                 src = (BasicPort) linkSource.getTerminal(false);
1385             }
1386             if (removedCells.contains(tgt.getParent())) {
1387                 tgt = (BasicPort) linkTerminal.getTerminal(true);
1388             }
1389         }
1390
1391         return new BasicPort[] { src, tgt };
1392     }
1393
1394     /**
1395      * Get the direct points from inLink.getSource() to outLink.getTarget().
1396      * 
1397      * @param splitBlock
1398      *            the current splitblock (added as a mid-point)
1399      * @param inLink
1400      *            the link before the split
1401      * @param outLink
1402      *            the link after the split
1403      * @return the points
1404      */
1405     private List<mxPoint> getDirectPoints(final SplitBlock splitBlock,
1406             final mxICell inLink, final mxICell outLink) {
1407         List<mxPoint> points;
1408         // add the points before the split
1409         points = new ArrayList<mxPoint>();
1410         if (inLink.getGeometry().getPoints() != null) {
1411             points.addAll(inLink.getGeometry().getPoints());
1412         }
1413
1414         // add a new point at the split location
1415         points.add(new mxPoint(snap(splitBlock.getGeometry().getCenterX()),
1416                 snap(splitBlock.getGeometry().getCenterY())));
1417
1418         // add the points after the split
1419         if (outLink.getGeometry().getPoints() != null) {
1420             points.addAll(outLink.getGeometry().getPoints());
1421         }
1422
1423         return points;
1424     }
1425
1426     /**
1427      * Manage Group to be CellFoldable i.e with a (-) to reduce and a (+) to
1428      * expand them. Labels (mxCell instance with value) should not have a
1429      * visible foldable sign.
1430      * 
1431      * @param cell
1432      *            the selected cell
1433      * @param collapse
1434      *            the collapse settings
1435      * @return always <code>false</code>
1436      * @see com.mxgraph.view.mxGraph#isCellFoldable(java.lang.Object, boolean)
1437      */
1438     @Override
1439     public boolean isCellFoldable(final Object cell, final boolean collapse) {
1440         return false;
1441     }
1442
1443     /**
1444      * Not BasicBLock cell have a moveable label.
1445      * 
1446      * @param cell
1447      *            the cell
1448      * @return true if the corresponding label is moveable
1449      * @see com.mxgraph.view.mxGraph#isLabelMovable(java.lang.Object)
1450      */
1451     @Override
1452     public boolean isLabelMovable(Object cell) {
1453         return !(cell instanceof BasicBlock);
1454     }
1455
1456     /**
1457      * Return true if selectable
1458      * 
1459      * @param cell
1460      *            the cell
1461      * @return status
1462      * @see com.mxgraph.view.mxGraph#isCellSelectable(java.lang.Object)
1463      */
1464     @Override
1465     public boolean isCellSelectable(final Object cell) {
1466         if (cell instanceof BasicPort) {
1467             return false;
1468         }
1469         return super.isCellSelectable(cell);
1470     }
1471
1472     /**
1473      * Return true if movable
1474      * 
1475      * @param cell
1476      *            the cell
1477      * @return status
1478      * @see com.mxgraph.view.mxGraph#isCellMovable(java.lang.Object)
1479      */
1480     @Override
1481     public boolean isCellMovable(final Object cell) {
1482         if (cell instanceof BasicPort) {
1483             return false;
1484         }
1485
1486         boolean movable = false;
1487         final Object[] cells = getSelectionCells();
1488
1489         // don't move if selection is only links
1490         for (Object c : cells) {
1491             if (!(c instanceof BasicLink)) {
1492                 movable = true;
1493                 break;
1494             }
1495         }
1496
1497         return movable && super.isCellMovable(cell);
1498     }
1499
1500     /**
1501      * Return true if resizable
1502      * 
1503      * @param cell
1504      *            the cell
1505      * @return status
1506      * @see com.mxgraph.view.mxGraph#isCellResizable(java.lang.Object)
1507      */
1508     @Override
1509     public boolean isCellResizable(final Object cell) {
1510         if (cell instanceof SplitBlock) {
1511             return false;
1512         }
1513         return (cell instanceof BasicBlock) && super.isCellResizable(cell);
1514     }
1515
1516     /**
1517      * A cell is deletable is it is not a locked block or an identifier cell
1518      * 
1519      * @param cell
1520      *            the cell
1521      * @return status
1522      * @see com.mxgraph.view.mxGraph#isCellDeletable(java.lang.Object)
1523      */
1524     @Override
1525     public boolean isCellDeletable(final Object cell) {
1526         if (cell instanceof BasicBlock && ((BasicBlock) cell).isLocked()) {
1527             return false;
1528         }
1529
1530         return !cell.getClass().equals(mxCell.class)
1531                 && super.isCellDeletable(cell);
1532     }
1533
1534     /**
1535      * Return true if editable
1536      * 
1537      * @param cell
1538      *            the cell
1539      * @return status
1540      * @see com.mxgraph.view.mxGraph#isCellEditable(java.lang.Object)
1541      */
1542     @Override
1543     public boolean isCellEditable(final Object cell) {
1544         return (cell instanceof TextBlock) && super.isCellDeletable(cell);
1545     }
1546
1547     /**
1548      * Get the label for the cell according to its style.
1549      * 
1550      * @param cell
1551      *            the cell object
1552      * @return a representative the string (block name) or a style specific
1553      *         style.
1554      * @see com.mxgraph.view.mxGraph#convertValueToString(java.lang.Object)
1555      */
1556     @Override
1557     public String convertValueToString(Object cell) {
1558         String ret = null;
1559
1560         if (cell != null) {
1561             final Map<String, Object> style = getCellStyle(cell);
1562
1563             final String displayedLabel = (String) style.get("displayedLabel");
1564             if (displayedLabel != null) {
1565                 if (cell instanceof BasicBlock) {
1566                     try {
1567                         ret = String.format(displayedLabel, ((BasicBlock) cell).getExprsFormat());
1568                     } catch (IllegalFormatException e) {
1569                         LOG.error(e);
1570                         ret = displayedLabel;
1571                     }
1572                 } else {
1573                     ret = displayedLabel;
1574                 }
1575             } else {
1576                 final String label = super.convertValueToString(cell);
1577                 if (label.isEmpty() && cell instanceof BasicBlock) {
1578                     ret = ((BasicBlock) cell).getInterfaceFunctionName();
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
1621                     .getOrientation() == Orientation.SOUTH)
1622                     && super.isExtendParent(p);
1623         } else {
1624             extendsParents = super.isExtendParent(cell);
1625         }
1626         return extendsParents;
1627     }
1628
1629     /**
1630      * @param fileName
1631      *            HDF5 filename
1632      */
1633     public void dumpToHdf5File(final String fileName) {
1634         String writeFile = fileName;
1635         if (fileName == null) {
1636             final FileChooser fc = ScilabFileChooser.createFileChooser();
1637             if (getSavedFile() != null) {
1638                 try {
1639                     fc.setInitialDirectory(getSavedFile().getCanonicalPath());
1640                 } catch (final IOException e) {
1641                     LOG.error(e);
1642                 }
1643             }
1644             fc.setMultipleSelection(false);
1645             fc.displayAndWait();
1646
1647             if (fc.getSelection() == null || fc.getSelection().length == 0
1648                     || fc.getSelection()[0].equals("")) {
1649                 return;
1650             }
1651             writeFile = fc.getSelection()[0];
1652         }
1653
1654         new H5RWHandler(writeFile).writeDiagram(this);
1655     }
1656
1657     /**
1658      * @return the parameters
1659      */
1660     public ScicosParameters getScicosParameters() {
1661         return scicosParameters;
1662     }
1663
1664     /**
1665      * @param scicosParameters
1666      *            the simulation parameters
1667      */
1668     public void setScicosParameters(final ScicosParameters scicosParameters) {
1669         this.scicosParameters = scicosParameters;
1670
1671         scicosParameters.addPropertyChangeListener(getEngine());
1672     }
1673
1674     /**
1675      * @return the engine
1676      */
1677     public CompilationEngineStatus getEngine() {
1678         return engine;
1679     }
1680
1681     /**
1682      * @param finalIntegrationTime
1683      *            set integration time
1684      * @throws PropertyVetoException
1685      *             when the value is invalid
1686      * @category XMLCompatibility
1687      */
1688     public void setFinalIntegrationTime(final double finalIntegrationTime)
1689             throws PropertyVetoException {
1690         scicosParameters.setFinalIntegrationTime(finalIntegrationTime);
1691     }
1692
1693     /**
1694      * @param integratorAbsoluteTolerance
1695      *            set integrator absolute tolerance
1696      * @throws PropertyVetoException
1697      *             when the value is invalid
1698      * @category XMLCompatibility
1699      */
1700     public void setIntegratorAbsoluteTolerance(
1701             final double integratorAbsoluteTolerance)
1702             throws PropertyVetoException {
1703         scicosParameters
1704                 .setIntegratorAbsoluteTolerance(integratorAbsoluteTolerance);
1705     }
1706
1707     /**
1708      * @param integratorRelativeTolerance
1709      *            integrator relative tolerance
1710      * @throws PropertyVetoException
1711      *             when the value is invalid
1712      * @category XMLCompatibility
1713      */
1714     public void setIntegratorRelativeTolerance(
1715             final double integratorRelativeTolerance)
1716             throws PropertyVetoException {
1717         scicosParameters
1718                 .setIntegratorRelativeTolerance(integratorRelativeTolerance);
1719     }
1720
1721     /**
1722      * @param maximumStepSize
1723      *            set max step size
1724      * @throws PropertyVetoException
1725      *             when the value is invalid
1726      * @category XMLCompatibility
1727      */
1728     public void setMaximumStepSize(final double maximumStepSize)
1729             throws PropertyVetoException {
1730         scicosParameters.setMaximumStepSize(maximumStepSize);
1731     }
1732
1733     /**
1734      * @param maxIntegrationTimeinterval
1735      *            set max integration time
1736      * @throws PropertyVetoException
1737      *             when the value is invalid
1738      * @category XMLCompatibility
1739      */
1740     public void setMaxIntegrationTimeinterval(
1741             final double maxIntegrationTimeinterval)
1742             throws PropertyVetoException {
1743         scicosParameters
1744                 .setMaxIntegrationTimeInterval(maxIntegrationTimeinterval);
1745     }
1746
1747     /**
1748      * @param realTimeScaling
1749      *            set real time scaling
1750      * @throws PropertyVetoException
1751      *             when the value is invalid
1752      * @category XMLCompatibility
1753      */
1754     public void setRealTimeScaling(final double realTimeScaling)
1755             throws PropertyVetoException {
1756         scicosParameters.setRealTimeScaling(realTimeScaling);
1757     }
1758
1759     /**
1760      * @param solver
1761      *            set solver
1762      * @throws PropertyVetoException
1763      *             when the value is invalid
1764      * @category XMLCompatibility
1765      */
1766     public void setSolver(final double solver) throws PropertyVetoException {
1767         scicosParameters.setSolver(solver);
1768     }
1769
1770     /**
1771      * @param toleranceOnTime
1772      *            set tolerance time
1773      * @throws PropertyVetoException
1774      *             when the value is invalid
1775      * @category XMLCompatibility
1776      */
1777     public void setToleranceOnTime(final double toleranceOnTime)
1778             throws PropertyVetoException {
1779         scicosParameters.setToleranceOnTime(toleranceOnTime);
1780     }
1781
1782     /**
1783      * Get the current diagram context
1784      * 
1785      * @return the context at the current node
1786      */
1787     public String[] getContext() {
1788         return scicosParameters.getContext();
1789     }
1790
1791     /**
1792      * Get the view port tab uuid
1793      * 
1794      * @return the view port tab
1795      */
1796     public String getViewPortTab() {
1797         return viewPortTab;
1798     }
1799
1800     /**
1801      * Set the view port tab uuid
1802      * 
1803      * @param uuid
1804      *            the view port tab
1805      */
1806     public void setViewPortTab(String uuid) {
1807         this.viewPortTab = uuid;
1808     }
1809
1810     /**
1811      * Get the diagram tab uuid
1812      * 
1813      * @return
1814      */
1815     public String getDiagramTab() {
1816         return diagramTab;
1817     }
1818
1819     /**
1820      * Set the diagram tab uuid
1821      * 
1822      * @param uuid
1823      *            the diagram tab
1824      */
1825     public void setDiagramTab(String uuid) {
1826         this.diagramTab = uuid;
1827     }
1828
1829     /**
1830      * Manage the visibility of the grid and the associated menu
1831      * 
1832      * @param status
1833      *            new status
1834      */
1835     public void setGridVisible(final boolean status) {
1836         setGridEnabled(status);
1837         getAsComponent().setGridVisible(status);
1838         getAsComponent().repaint();
1839     }
1840
1841     /**
1842      * @return save status
1843      */
1844     public boolean saveDiagram() {
1845         boolean isSuccess = false;
1846         if (getSavedFile() == null) {
1847             isSuccess = saveDiagramAs(null);
1848         } else {
1849             isSuccess = saveDiagramAs(getSavedFile());
1850         }
1851
1852         if (isSuccess) {
1853             setModified(false);
1854             Xcos.getInstance().addDiagram(getSavedFile(), this);
1855         }
1856
1857         return isSuccess;
1858     }
1859
1860     /**
1861      * @param fileName
1862      *            diagram filename
1863      * @return save status
1864      */
1865     public boolean saveDiagramAs(final File fileName) {
1866         boolean isSuccess = false;
1867         File writeFile = fileName;
1868         info(XcosMessages.SAVING_DIAGRAM);
1869         if (fileName == null) {
1870             // Choose a filename
1871             final SwingScilabFileChooser fc = ((SwingScilabFileChooser) ScilabFileChooser
1872                     .createFileChooser().getAsSimpleFileChooser());
1873             fc.setTitle(XcosMessages.SAVE_AS);
1874             fc.setUiDialogType(JFileChooser.SAVE_DIALOG);
1875
1876             // Xcos files or anything are supported
1877             final XcosFileType defaultFileType = XcosFileType.getDefault();
1878             final FileNameExtensionFilter xcosFilter = new FileNameExtensionFilter(
1879                     defaultFileType.getDescription(),
1880                     defaultFileType.getExtension());
1881             fc.addChoosableFileFilter(xcosFilter);
1882             fc.setAcceptAllFileFilterUsed(true);
1883             fc.setFileFilter(xcosFilter);
1884
1885             fc.setMultipleSelection(false);
1886             if (getSavedFile() != null) {
1887                 fc.setSelectedFile(getSavedFile());
1888             } else {
1889                 final String title = getTitle();
1890                 if (title != null) {
1891                     fc.setSelectedFile(new File(title
1892                             + XcosFileType.XCOS.getDottedExtension()));
1893                 }
1894                 ConfigurationManager.configureCurrentDirectory(fc);
1895             }
1896
1897             int status = fc.showSaveDialog(this.getAsComponent());
1898             if (status != JFileChooser.APPROVE_OPTION) {
1899                 info(XcosMessages.EMPTY_INFO);
1900                 return isSuccess;
1901             }
1902
1903             writeFile = fc.getSelectedFile();
1904         }
1905
1906         /* Extension checks */
1907         if (!writeFile.exists()) {
1908             final String filename = writeFile.getName();
1909             if (!filename.endsWith(XcosFileType.XCOS.getDottedExtension())) {
1910                 /* No extension given --> .xcos added */
1911                 writeFile = new File(writeFile.getParent(), filename
1912                         + XcosFileType.XCOS.getDottedExtension());
1913             }
1914         }
1915
1916         try {
1917             save(writeFile);
1918             setSavedFile(writeFile);
1919
1920             setTitle(writeFile.getName().substring(0,
1921                     writeFile.getName().lastIndexOf('.')));
1922             ConfigurationManager.getInstance().addToRecentFiles(writeFile);
1923             setModified(false);
1924             isSuccess = true;
1925         } catch (final TransformerException e) {
1926             LogFactory.getLog(XcosDiagram.class).error(e);
1927             XcosDialogs.couldNotSaveFile(this);
1928         }
1929
1930         info(XcosMessages.EMPTY_INFO);
1931         return isSuccess;
1932     }
1933
1934     /**
1935      * Save to a file
1936      * 
1937      * @param file
1938      *            the file
1939      * @throws TransformerException
1940      *             on error
1941      */
1942     private void save(final File file) throws TransformerException {
1943         final XcosCodec codec = new XcosCodec();
1944         final TransformerFactory tranFactory = ScilabTransformerFactory
1945                 .newInstance();
1946         final Transformer aTransformer = tranFactory.newTransformer();
1947
1948         final DOMSource src = new DOMSource(codec.encode(this));
1949         final StreamResult result = new StreamResult(file);
1950         aTransformer.transform(src, result);
1951     }
1952
1953     /**
1954      * Perform post loading initialization.
1955      * 
1956      * @param file
1957      *            the loaded file
1958      */
1959     public void postLoad(final File file) {
1960         final String name = file.getName();
1961
1962         setModified(false);
1963         if (name.endsWith(XcosFileType.getDefault().getExtension())) {
1964             setSavedFile(file);
1965             Xcos.getInstance().addDiagram(file, this);
1966         }
1967         setTitle(name.substring(0, name.lastIndexOf('.')));
1968         generateUID();
1969
1970         fireEvent(new mxEventObject(mxEvent.ROOT));
1971
1972         info(XcosMessages.EMPTY_INFO);
1973     }
1974
1975     /**
1976      * Set the title of the diagram
1977      * 
1978      * @param title
1979      *            the title
1980      * @see org.scilab.modules.graph.ScilabGraph#setTitle(java.lang.String)
1981      */
1982     @Override
1983     public void setTitle(final String title) {
1984         super.setTitle(title);
1985         updateTabTitle();
1986     }
1987
1988     /**
1989      * Update the title
1990      */
1991     public void updateTabTitle() {
1992         // get the modifier string
1993         final String modified;
1994         if (isModified()) {
1995             modified = "*";
1996         } else {
1997             modified = "";
1998         }
1999
2000         // get the title string
2001         final String title = getTitle();
2002
2003         // get the path
2004         CharSequence formattedPath = "";
2005         final File savedFile = getSavedFile();
2006         if (savedFile != null) {
2007             try {
2008                 final String path = savedFile.getCanonicalPath();
2009                 formattedPath = new StringBuilder().append(" (").append(path)
2010                         .append(')');
2011             } catch (final IOException e) {
2012                 LOG.debug(e);
2013             }
2014         }
2015
2016         // Product name
2017         final String product = Xcos.TRADENAME;
2018
2019         final String tabTitle = new StringBuilder().append(modified)
2020                 .append(title).append(formattedPath).append(" - ")
2021                 .append(product).toString();
2022
2023         final SwingScilabTab tab = ScilabTabFactory.getInstance().getFromCache(
2024                 getDiagramTab());
2025         if (tab != null) {
2026             tab.setName(tabTitle);
2027         }
2028     }
2029
2030     /**
2031      * @param context
2032      *            set context
2033      * @throws PropertyVetoException
2034      *             when the new value is invalid
2035      */
2036     public void setContext(final String[] context) throws PropertyVetoException {
2037         scicosParameters.setContext(context);
2038         fireEvent(new mxEventObject(XcosEvent.DIAGRAM_UPDATED));
2039         updateCellsContext();
2040     }
2041
2042     /**
2043      * Update context value in diagram children
2044      */
2045     public void updateCellsContext() {
2046         final Object rootParent = getDefaultParent();
2047         final int childCount = getModel().getChildCount(rootParent);
2048         for (int i = 0; i < childCount; ++i) {
2049             final Object obj = getModel().getChildAt(rootParent, i);
2050             if (obj instanceof ContextUpdate) {
2051                 final String[] globalContext = getContext();
2052
2053                 /* Determine if the context is not empty */
2054                 int nbOfDetectedChar = 0;
2055                 for (int j = 0; j < globalContext.length; j++) {
2056                     globalContext[j] = globalContext[j].replaceFirst("\\s", "");
2057                     nbOfDetectedChar += globalContext[j].length();
2058                     if (nbOfDetectedChar != 0) {
2059                         break;
2060                     }
2061                 }
2062
2063                 if (nbOfDetectedChar != 0) {
2064                     ((ContextUpdate) obj).onContextChange(globalContext);
2065                 }
2066
2067             } else if (obj instanceof SuperBlock) {
2068                 final SuperBlock superBlock = (SuperBlock) obj;
2069                 if (superBlock.getChild() != null) {
2070                     superBlock.getChild().updateCellsContext();
2071                 }
2072             }
2073         }
2074     }
2075
2076     /**
2077      * @param debugLevel
2078      *            change debug level
2079      * @category XMLCompatibility
2080      * @throws PropertyVetoException
2081      *             when the new value is invalid
2082      */
2083     public void setDebugLevel(final int debugLevel)
2084             throws PropertyVetoException {
2085         scicosParameters.setDebugLevel(debugLevel);
2086     }
2087
2088     /**
2089      * Read a diagram from an HDF5 file (ask for creation if the file does not
2090      * exist)
2091      * 
2092      * @param diagram
2093      *            file to open
2094      * @return the diagram instance or null on error
2095      */
2096     public XcosDiagram openDiagramFromFile(final File diagram) {
2097         info(XcosMessages.LOADING_DIAGRAM);
2098
2099         if (diagram.exists()) {
2100             try {
2101                 transformAndLoadFile(diagram.getCanonicalPath());
2102             } catch (IOException e) {
2103                 LogFactory.getLog(XcosDiagram.class).error(e);
2104             }
2105         } else {
2106             AnswerOption answer;
2107             try {
2108                 answer = ScilabModalDialog.show(getAsComponent(),
2109                         new String[] { String.format(
2110                                 XcosMessages.FILE_DOESNT_EXIST,
2111                                 diagram.getCanonicalFile()) },
2112                         XcosMessages.XCOS, IconType.QUESTION_ICON,
2113                         ButtonType.YES_NO);
2114             } catch (final IOException e) {
2115                 LOG.error(e);
2116                 answer = AnswerOption.YES_OPTION;
2117             }
2118
2119             if (answer == AnswerOption.YES_OPTION) {
2120                 saveDiagramAs(diagram);
2121             }
2122
2123             info(XcosMessages.EMPTY_INFO);
2124         }
2125
2126         return this;
2127     }
2128
2129     /**
2130      * Load a file with different method depending on it extension
2131      * 
2132      * @param file
2133      *            File to load
2134      * @param wait
2135      *            wait end transform
2136      * @return the status of the operation
2137      */
2138     protected boolean transformAndLoadFile(final String file) {
2139         final XcosFileType filetype = XcosFileType.findFileType(file);
2140
2141         if (!exists(file) || filetype == null) {
2142             XcosDialogs.couldNotLoadFile(this);
2143             return false;
2144         }
2145
2146         new SwingWorker<XcosDiagram, ActionEvent>() {
2147             int counter = 0;
2148             final Timer t = new Timer(1000, new ActionListener() {
2149                 @Override
2150                 public void actionPerformed(ActionEvent e) {
2151                         counter = (counter + 1) % (XcosMessages.DOTS.length() + 1);
2152                     String str = XcosMessages.LOADING_DIAGRAM + XcosMessages.DOTS.substring(0, counter);
2153
2154                     XcosDiagram.this.info(str);
2155                 }
2156             });
2157             
2158             @Override
2159             protected XcosDiagram doInBackground() throws Exception {
2160                 t.start();
2161                 XcosDiagram.this.setReadOnly(true);
2162
2163                 /*
2164                  * Load
2165                  */
2166                 filetype.load(file, XcosDiagram.this);
2167                 return XcosDiagram.this;
2168             }
2169             
2170             @Override
2171             protected void done() {
2172                 t.stop();
2173                 XcosDiagram.this.setReadOnly(false);
2174                 XcosDiagram.this.getUndoManager().clear();
2175
2176                 /*
2177                  * Load has finished
2178                  */
2179                 postLoad(new File(file));
2180                 XcosDiagram.this.info(XcosMessages.EMPTY_INFO);
2181             }
2182
2183         }.execute();
2184         return true;
2185     }
2186
2187     /**
2188      * generate unique id to all blocks in diagram
2189      */
2190     public void generateUID() {
2191         for (int i = 0; i < getModel().getChildCount(getDefaultParent()); ++i) {
2192             if (getModel().getChildAt(getDefaultParent(), i) instanceof BasicBlock) {
2193                 final BasicBlock block = (BasicBlock) getModel().getChildAt(
2194                         getDefaultParent(), i);
2195                 if (block.getRealParameters() instanceof ScilabMList) {
2196                     if (block instanceof SuperBlock) {
2197                         // generate a child diagram with UID
2198                         ((SuperBlock) block).createChildDiagram(true);
2199                     } else {
2200                         // we have a hidden SuperBlock, create a real one
2201                         final SuperBlock newSP = (SuperBlock) BlockFactory
2202                                 .createBlock(SuperBlock.INTERFUNCTION_NAME);
2203                         newSP.setRealParameters(block.getRealParameters());
2204                         newSP.createChildDiagram(true);
2205                         newSP.setParentDiagram(this);
2206                         block.setRealParameters(new DiagramElement()
2207                                 .encode(newSP.getChild()));
2208                     }
2209                 } else if (block.getId() == null
2210                         || block.getId().compareTo("") == 0) {
2211                     block.generateId();
2212                 }
2213             }
2214         }
2215     }
2216
2217     /**
2218      * Update all the children of the current graph.
2219      */
2220     public void setChildrenParentDiagram() {
2221         getModel().beginUpdate();
2222         try {
2223             for (int i = 0; i < getModel().getChildCount(getDefaultParent()); i++) {
2224                 final mxCell cell = (mxCell) getModel().getChildAt(
2225                         getDefaultParent(), i);
2226                 if (cell instanceof BasicBlock) {
2227                     final BasicBlock block = (BasicBlock) cell;
2228                     block.setParentDiagram(this);
2229                 }
2230             }
2231         } finally {
2232             getModel().endUpdate();
2233         }
2234     }
2235
2236     /**
2237      * Getting the root diagram of a decomposed diagram
2238      * 
2239      * @return Root parent of the whole parent
2240      */
2241     public XcosDiagram getRootDiagram() {
2242         XcosDiagram rootGraph = this;
2243         while (rootGraph instanceof SuperBlockDiagram) {
2244             rootGraph = ((SuperBlockDiagram) rootGraph).getContainer()
2245                     .getParentDiagram();
2246         }
2247         return rootGraph;
2248     }
2249
2250     /**
2251      * Returns the tooltip to be used for the given cell.
2252      * 
2253      * @param cell
2254      *            block
2255      * @return cell tooltip
2256      */
2257     @Override
2258     public String getToolTipForCell(final Object cell) {
2259         if (cell instanceof BasicBlock) {
2260             return ((BasicBlock) cell).getToolTipText();
2261         } else if (cell instanceof BasicPort) {
2262             return ((BasicPort) cell).getToolTipText();
2263         }
2264         return "";
2265     }
2266
2267     /**
2268      * Display the message in info bar.
2269      * 
2270      * @param message
2271      *            Informations
2272      */
2273     public void info(final String message) {
2274         final XcosTab tab = XcosTab.get(this);
2275
2276         if (tab != null && tab.getInfoBar() != null) {
2277             tab.getInfoBar().setText(message);
2278         }
2279     }
2280
2281     /**
2282      * Display the message into an error popup
2283      * 
2284      * @param message
2285      *            Error of the message
2286      */
2287     public void error(final String message) {
2288         JOptionPane.showMessageDialog(getAsComponent(), message,
2289                 XcosMessages.XCOS, JOptionPane.ERROR_MESSAGE);
2290     }
2291
2292     /**
2293      * Find the block corresponding to the given uid and display a warning
2294      * message.
2295      * 
2296      * @param uid
2297      *            - A String as UID.
2298      * @param message
2299      *            - The message to display.
2300      */
2301     public void warnCellByUID(final String uid, final String message) {
2302         final Object cell = ((mxGraphModel) getModel()).getCell(uid);
2303         if (cell == null) {
2304             return;
2305         }
2306
2307         // open the tab
2308         if (XcosTab.get(this) == null) {
2309             XcosTab.restore(this);
2310         }
2311
2312         if (message.isEmpty()) {
2313             getAsComponent().clearCellOverlays(cell);
2314         } else {
2315             getAsComponent().setCellWarning(cell, message, null, true);
2316         }
2317     }
2318
2319     /**
2320      * Set the current diagram in a modified state
2321      * 
2322      * @param modified
2323      *            True or False whether the current diagram must be saved or
2324      *            not.
2325      */
2326     @Override
2327     public void setModified(final boolean modified) {
2328         super.setModified(modified);
2329         updateTabTitle();
2330     }
2331
2332     /**
2333      * Evaluate the current context
2334      * 
2335      * @return The resulting data. Keys are variable names and Values are
2336      *         evaluated values.
2337      */
2338     public Map<String, String> evaluateContext() {
2339         Map<String, String> result = null;
2340
2341         try {
2342             final Pattern p = Pattern.compile("('|\")");
2343
2344             final StringBuilder str = new StringBuilder();
2345             str.append('[');
2346             for (final String s : getContext()) {
2347                 str.append('\"');
2348                 str.append(p.matcher(s).replaceAll("''"));
2349                 str.append("\" ");
2350             }
2351             str.append(']');
2352
2353             final String temp = FileUtils.createTempFile();
2354
2355             ScilabInterpreterManagement
2356                     .synchronousScilabExec("vars = script2var("
2357                             + str.toString() + ", struct());"
2358                             + "export_to_hdf5('" + temp + "', 'vars');");
2359
2360             result = new H5RWHandler(temp).readContext();
2361         } catch (final IOException e) {
2362             info("Unable to create file");
2363             e.printStackTrace();
2364         } catch (final InterpreterException e) {
2365             info("Unable to evaluate the contexte");
2366             e.printStackTrace();
2367         }
2368
2369         return result;
2370     }
2371
2372     /**
2373      * @param uid
2374      *            block ID
2375      * @return block
2376      */
2377     public BasicBlock getChildById(final String uid) {
2378         BasicBlock returnBlock = null;
2379         for (int i = 0; i < getModel().getChildCount(getDefaultParent()); ++i) {
2380             if (getModel().getChildAt(getDefaultParent(), i) instanceof BasicBlock) {
2381                 final BasicBlock block = (BasicBlock) getModel().getChildAt(
2382                         getDefaultParent(), i);
2383                 if (block.getId().compareTo(uid) == 0) { // find it
2384                     returnBlock = block;
2385                 } else {
2386                     if (block instanceof SuperBlock) {
2387                         boolean created = false;
2388                         if (((SuperBlock) block).getChild() == null) {
2389                             // create temporary SuperBlock to find child
2390                             ((SuperBlock) block).createChildDiagram();
2391                             created = true;
2392                         }
2393
2394                         // search in child
2395                         returnBlock = ((SuperBlock) block).getChild()
2396                                 .getChildById(uid);
2397
2398                         if (created) { // if temporary, destroy it
2399                             ((SuperBlock) block).syncParameters();
2400                         }
2401                     } else if (block.getRealParameters() instanceof ScilabMList) {
2402                         // we have a hidden SuperBlock, create a real one
2403                         SuperBlock newSP = (SuperBlock) BlockFactory
2404                                 .createBlock(SuperBlock.INTERFUNCTION_NAME);
2405                         newSP.setParentDiagram(block.getParentDiagram());
2406                         newSP.setRealParameters(block.getRealParameters());
2407                         newSP.createChildDiagram();
2408                         // search in child
2409                         returnBlock = newSP.getChild().getChildById(uid);
2410                         newSP.syncParameters();
2411                         newSP = null;
2412                     }
2413                 }
2414             }
2415
2416             if (returnBlock != null) {
2417                 return returnBlock;
2418             }
2419         }
2420         return returnBlock;
2421     }
2422
2423     /**
2424      * Returns true if the given cell is a not a block nor a port.
2425      * 
2426      * @param cell
2427      *            the drop target
2428      * @param cells
2429      *            the cells to be dropped
2430      * @return the drop status
2431      * @see com.mxgraph.view.mxGraph#isValidDropTarget(java.lang.Object,
2432      *      java.lang.Object[])
2433      */
2434     @Override
2435     public boolean isValidDropTarget(final Object cell, final Object[] cells) {
2436         return !(cell instanceof BasicBlock) && !(cell instanceof BasicBlock)
2437                 && !(cell instanceof BasicPort)
2438                 && super.isValidDropTarget(cell, cells);
2439     }
2440
2441     /**
2442      * @return child visibility
2443      */
2444     @Deprecated
2445     public boolean isChildVisible() {
2446         // FIXME check
2447
2448         return false;
2449
2450         // for (int i = 0; i < getModel().getChildCount(getDefaultParent());
2451         // i++) {
2452         // final Object child = getModel().getChildAt(getDefaultParent(), i);
2453         // if (child instanceof SuperBlock) {
2454         // final XcosDiagram diag = ((SuperBlock) child).getChild();
2455         // if (diag != null && (diag.isChildVisible() || diag.isVisible())) {
2456         // // if child or sub child is visible
2457         // return true;
2458         // }
2459         // }
2460         // }
2461         // return false;
2462     }
2463
2464     /**
2465      * @return close capability
2466      */
2467     public boolean canClose() {
2468         if (!isChildVisible()) {
2469             return true;
2470         }
2471         return false;
2472     }
2473
2474     /**
2475      * Construct a new selection model used on this graph.
2476      * 
2477      * @return a new selection model instance.
2478      * @see com.mxgraph.view.mxGraph#createSelectionModel()
2479      */
2480     @Override
2481     protected mxGraphSelectionModel createSelectionModel() {
2482         return new mxGraphSelectionModel(this) {
2483             /**
2484              * When we only want to select a cell which is a port, select the
2485              * parent block.
2486              * 
2487              * @param cell
2488              *            the cell
2489              * @see com.mxgraph.view.mxGraphSelectionModel#setCell(java.lang.Object)
2490              */
2491             @Override
2492             public void setCell(final Object cell) {
2493                 final Object current;
2494                 if (cell instanceof BasicPort) {
2495                     current = getModel().getParent(cell);
2496                 } else {
2497                     current = cell;
2498                 }
2499                 super.setCell(current);
2500             }
2501         };
2502     }
2503 }