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