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