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