API compatibility with jgraphx-1.2.0.5
[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 - DIGITEO - Bruno JOFRET
4  *
5  * This file must be used under the terms of the CeCILL.
6  * This source file is licensed as described in the file COPYING, which
7  * you should have received as part of this distribution.  The terms
8  * are also available at
9  * http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt
10  *
11  */
12
13 package org.scilab.modules.xcos.graph;
14
15 import java.awt.Color;
16 import java.awt.MouseInfo;
17 import java.awt.event.KeyEvent;
18 import java.awt.event.KeyListener;
19 import java.awt.event.MouseEvent;
20 import java.awt.event.MouseListener;
21 import java.beans.PropertyChangeEvent;
22 import java.beans.PropertyChangeListener;
23 import java.io.File;
24 import java.io.FileWriter;
25 import java.io.IOException;
26 import java.rmi.server.UID;
27 import java.util.ArrayList;
28 import java.util.Collections;
29 import java.util.HashMap;
30 import java.util.List;
31 import javax.swing.JFileChooser;
32 import javax.swing.JOptionPane;
33 import javax.swing.SwingUtilities;
34 import javax.xml.parsers.DocumentBuilder;
35 import javax.xml.parsers.DocumentBuilderFactory;
36 import javax.xml.parsers.ParserConfigurationException;
37
38 import org.scilab.modules.graph.ScilabGraph;
39 import org.scilab.modules.graph.actions.PasteAction;
40 import org.scilab.modules.graph.actions.RedoAction;
41 import org.scilab.modules.graph.actions.SelectAllAction;
42 import org.scilab.modules.graph.actions.UndoAction;
43 import org.scilab.modules.graph.actions.ZoomInAction;
44 import org.scilab.modules.graph.actions.ZoomOutAction;
45 import org.scilab.modules.gui.bridge.contextmenu.SwingScilabContextMenu;
46 import org.scilab.modules.gui.bridge.filechooser.SwingScilabFileChooser;
47 import org.scilab.modules.gui.checkboxmenuitem.CheckBoxMenuItem;
48 import org.scilab.modules.gui.contextmenu.ContextMenu;
49 import org.scilab.modules.gui.contextmenu.ScilabContextMenu;
50 import org.scilab.modules.gui.filechooser.FileChooser;
51 import org.scilab.modules.gui.filechooser.ScilabFileChooser;
52 import org.scilab.modules.gui.messagebox.ScilabModalDialog;
53 import org.scilab.modules.gui.messagebox.ScilabModalDialog.AnswerOption;
54 import org.scilab.modules.gui.messagebox.ScilabModalDialog.ButtonType;
55 import org.scilab.modules.gui.messagebox.ScilabModalDialog.IconType;
56 import org.scilab.modules.gui.tab.Tab;
57 import org.scilab.modules.gui.utils.SciFileFilter;
58 import org.scilab.modules.gui.utils.UIElementMapper;
59 import org.scilab.modules.gui.window.ScilabWindow;
60 import org.scilab.modules.hdf5.scilabTypes.ScilabMList;
61 import org.scilab.modules.xcos.Xcos;
62 import org.scilab.modules.xcos.XcosTab;
63 import org.scilab.modules.xcos.actions.DiagramBackgroundAction;
64 import org.scilab.modules.xcos.actions.SetContextAction;
65 import org.scilab.modules.xcos.actions.SetupAction;
66 import org.scilab.modules.xcos.actions.XcosDocumentationAction;
67 import org.scilab.modules.xcos.actions.XcosShortCut;
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.io.ContextUpdate;
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.BlockFactory.BlockInterFunction;
76 import org.scilab.modules.xcos.block.actions.ShowParentAction;
77 import org.scilab.modules.xcos.io.BlockReader;
78 import org.scilab.modules.xcos.io.BlockWriter;
79 import org.scilab.modules.xcos.io.XcosCodec;
80 import org.scilab.modules.xcos.link.BasicLink;
81 import org.scilab.modules.xcos.link.commandcontrol.CommandControlLink;
82 import org.scilab.modules.xcos.link.explicit.ExplicitLink;
83 import org.scilab.modules.xcos.link.implicit.ImplicitLink;
84 import org.scilab.modules.xcos.port.BasicPort;
85 import org.scilab.modules.xcos.port.PortCheck;
86 import org.scilab.modules.xcos.port.command.CommandPort;
87 import org.scilab.modules.xcos.port.control.ControlPort;
88 import org.scilab.modules.xcos.port.input.ExplicitInputPort;
89 import org.scilab.modules.xcos.port.input.ImplicitInputPort;
90 import org.scilab.modules.xcos.port.input.InputPort;
91 import org.scilab.modules.xcos.port.output.ExplicitOutputPort;
92 import org.scilab.modules.xcos.port.output.ImplicitOutputPort;
93 import org.scilab.modules.xcos.port.output.OutputPort;
94 import org.scilab.modules.xcos.utils.BlockPositioning;
95 import org.scilab.modules.xcos.utils.ConfigXcosManager;
96 import org.scilab.modules.xcos.utils.XcosDialogs;
97 import org.scilab.modules.xcos.utils.XcosEvent;
98 import org.scilab.modules.xcos.utils.XcosFileType;
99 import org.scilab.modules.xcos.utils.XcosMessages;
100 import org.w3c.dom.Document;
101 import org.xml.sax.SAXException;
102
103 import com.mxgraph.io.mxCodec;
104 import com.mxgraph.model.mxCell;
105 import com.mxgraph.model.mxGeometry;
106 import com.mxgraph.model.mxGraphModel;
107 import com.mxgraph.model.mxICell;
108 import com.mxgraph.model.mxGraphModel.mxChildChange;
109 import com.mxgraph.model.mxGraphModel.mxStyleChange;
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.mxUtils;
117 import com.mxgraph.util.mxUndoableEdit.mxUndoableChange;
118 import com.mxgraph.view.mxMultiplicity;
119
120 public class XcosDiagram extends ScilabGraph {
121
122     // Default values : SCI/modules/scicos/macros/scicos_scicos/scicos_params.sci
123     private double finalIntegrationTime = 100000;
124     private double integratorAbsoluteTolerance = 1e-4;
125     private double integratorRelativeTolerance = 1e-6;
126     private double toleranceOnTime = 1e-10;
127     private double maxIntegrationTimeinterval = finalIntegrationTime + 1;
128     private double realTimeScaling = 0;
129     private double solver = 0;
130     private double maximumStepSize = 0;
131     private int debugLevel = 0;
132     private String[] context = new String[]{""};
133     private String version = "scicos4.2";
134     //private Window palette;
135     private Tab viewPort;
136     
137     /*to manage splitLink*/
138     private BasicLink splitLink = null;
139     private BasicPort splitPort = null;
140     private mxPoint dragSplitPos = null;
141     private boolean waitSplitRelease = false;
142     
143     /*to manage path link*/
144     private BasicLink drawLink = null;
145     private boolean waitPathAddEdge = false;
146     private boolean waitPathRelease = false;
147
148     private CheckBoxMenuItem viewPortMenu;
149     private CheckBoxMenuItem gridMenu;
150     private SetContextAction action;
151     
152     protected mxIEventListener undoEnabler = new mxIEventListener()
153     {
154         public void invoke(Object source, mxEventObject evt) {
155                 if (getParentTab() != null) {
156                         ((XcosTab)getParentTab()).setEnabledUndo(true);
157                 }
158         }
159     };
160
161     public Object addEdge(Object edge, Object parent, Object source,
162                 Object target, Integer index) { 
163         
164         // Command -> Control
165         if (source instanceof CommandPort) {
166                 if (target instanceof ControlPort) {
167                         return super.addEdge(new CommandControlLink(), parent, source, target, index);
168                 }
169         }
170
171         // Control -> Command
172         // Switch source and target !
173         if (target instanceof CommandPort) {
174                 if (source instanceof ControlPort) {
175                         return super.addEdge(new CommandControlLink(), parent, target, source, index);
176                 }
177         }
178
179         // ExplicitOutput -> ExplicitInput
180         if (source instanceof ExplicitOutputPort) {
181                 if (target instanceof ExplicitInputPort) {
182                         return super.addEdge(new ExplicitLink(), parent, source, target, index);
183                 }
184         }
185         // ExplicitInput -> ExplicitOutput
186         // Switch source and target !
187         if (target instanceof ExplicitOutputPort) {
188                 if (source instanceof ExplicitInputPort) {
189                         return super.addEdge(new ExplicitLink(), parent, target, source, index);
190                 }
191         }
192
193         // ImplicitOutput -> ImplicitInput
194         if (source instanceof ImplicitOutputPort) {
195                 if (target instanceof ImplicitInputPort) {
196                         return super.addEdge(new ImplicitLink(), parent, source, target, index);
197                 }
198         }
199         // ImplicitInput -> ImplicitOutput
200         // Switch source and target !
201         if (target instanceof ImplicitOutputPort) {
202                 if (source instanceof ImplicitInputPort) {
203                         return super.addEdge(new ImplicitLink(), parent, target, source, index);
204                 }
205         }
206
207         // ImplicitInput -> ImplicitInput
208         if (source instanceof ImplicitInputPort) {
209                 if (target instanceof ImplicitInputPort) {
210                         return super.addEdge(new ImplicitLink(), parent, source, target, index);
211                 }
212         }
213         // ImplicitOutputPort -> ImplicitOutput
214         // Switch source and target !
215         if (target instanceof ImplicitOutputPort) {
216                 if (source instanceof ImplicitOutputPort) {
217                         return super.addEdge(new ImplicitLink(), parent, target, source, index);
218                 }
219         }
220         
221         // ExplicitLink -> ExplicitInputPort
222         if (source instanceof ExplicitLink) {
223                 if (target instanceof ExplicitInputPort) {
224                         return addSplitEdge((BasicLink) source, (BasicPort) target);
225                 }
226         }
227         // ExplicitOutput -> ExpliciLink
228         // Switch source and target !
229         if (target instanceof ExplicitLink) {
230                 if (source instanceof ExplicitInputPort) {
231                         waitSplitRelease = true;
232                         splitLink = (BasicLink) target;
233                         splitPort = (BasicPort) source;
234                         return null;
235                         //return addSplitEdge((BasicLink) target, (BasicPort)source);
236                 }
237         }
238
239         // ImplicitLink -> ImplicitInputPort
240         if (source instanceof ImplicitLink) {
241                 if (target instanceof ImplicitInputPort) {
242                         return addSplitEdge((BasicLink) source, (BasicPort) target);
243                 }
244         }
245         // ImplicitInputPort -> ImplicitLink
246         // Switch source and target !
247         if (target instanceof ImplicitLink) {
248                 if (source instanceof ImplicitInputPort) {
249                         waitSplitRelease = true;
250                         splitLink = (BasicLink) target;
251                         splitPort = (BasicPort) source;
252                         return null;
253                         //return addSplitEdge((BasicLink) target, (BasicPort)source);
254                 }
255         }
256         
257         // ImplicitLink -> ImplicitOutputPort
258         if (source instanceof ImplicitLink) {
259                 if (target instanceof ImplicitOutputPort) {
260                         return addSplitEdge((BasicLink) source, (BasicPort) target);
261                 }
262         }
263         // ImplicitOutputPort -> ImplicitLink
264         // Switch source and target !
265         if (target instanceof ImplicitLink) {
266                 if (source instanceof ImplicitOutputPort) {
267                         waitSplitRelease = true;
268                         splitLink = (BasicLink) target;
269                         splitPort = (BasicPort) source;
270                         return null;
271                         //return addSplitEdge((BasicLink) target, (BasicPort)source);
272                 }
273         }
274
275         // CommandControlLink -> ControlPort
276         if (source instanceof CommandControlLink) {
277                 if (target instanceof ControlPort) {
278                         return addSplitEdge((BasicLink) source, (BasicPort) target);
279                 }
280         }
281         // ControlPort -> CommandControlLink
282         // Switch source and target !
283         if (target instanceof CommandControlLink) {
284                 if (source instanceof ControlPort) {
285                         waitSplitRelease = true;
286                         splitLink = (BasicLink) target;
287                         splitPort = (BasicPort) source;
288                         return null;
289                         //return addSplitEdge((BasicLink) target, (BasicPort)source);
290                 }
291         }
292
293         
294         if(source != null && target == null) {
295                 drawLink = null;
296                 if(source instanceof ExplicitInputPort || source instanceof ExplicitOutputPort) {
297                         drawLink = (BasicLink) super.addEdge(new ExplicitLink(), getDefaultParent(), source, target, index);
298                 } else if(source instanceof ImplicitInputPort || source instanceof ImplicitOutputPort) {
299                         drawLink = (BasicLink) super.addEdge(new ImplicitLink(), getDefaultParent(), source, target, index);
300                 } else if(source instanceof ControlPort || source instanceof CommandPort) {
301                         drawLink = (BasicLink) super.addEdge(new CommandControlLink(), getDefaultParent(), source, target, index);
302                 } else if(source instanceof BasicLink) {
303                         SplitBlock split = (SplitBlock) addSplitEdge((BasicLink)source, (BasicPort)target);
304                         drawLink = (BasicLink) split.getOut2().getEdgeAt(0);
305             }
306
307                 if(drawLink != null) {
308                 waitPathRelease = true;
309             }
310             
311                 info(XcosMessages.DRAW_LINK);
312             return drawLink;
313         }
314         return null;
315     }
316
317     private Object addSplitEdge(BasicLink link, mxICell target) {
318         BasicPort linkSource =  (BasicPort) link.getSource();
319         BasicPort linkTarget =  (BasicPort) link.getTarget();
320
321         getModel().beginUpdate();
322         if (dragSplitPos == null) {
323                 dragSplitPos = new mxPoint();
324
325             //check splitPosition values
326             double srcX = linkSource.getParent().getGeometry().getX() + linkSource.getGeometry().getCenterX();
327             double tgtX = linkTarget.getParent().getGeometry().getX() + linkTarget.getGeometry().getCenterX();
328             double srcY = linkSource.getParent().getGeometry().getY() + linkSource.getGeometry().getCenterY();
329             double tgtY = linkTarget.getParent().getGeometry().getY() + linkTarget.getGeometry().getCenterY();
330
331             double offsetX = (tgtX - srcX) / 2;
332             double offsetY = (tgtY - srcY) / 2;
333             dragSplitPos.setX(srcX + offsetX);
334             dragSplitPos.setY(srcY + offsetY);
335         }
336         
337         SplitBlock splitBlock = (SplitBlock) BlockFactory.createBlock(BlockInterFunction.SPLIT_f);
338         if (target instanceof BasicLink) {
339             splitBlock.setConnection(linkSource, linkTarget, (BasicPort) ((BasicLink)target).getSource());
340         } else {
341                 splitBlock.setConnection(linkSource, linkTarget, (BasicPort) target);
342         }
343         
344         mxGeometry geom = splitBlock.getGeometry();
345         geom.setX(dragSplitPos.getX() - (SplitBlock.DEFAULT_SIZE/2));
346         geom.setY(dragSplitPos.getY() - (SplitBlock.DEFAULT_SIZE/2));
347         addCell(splitBlock);
348         
349         
350         //Update old link
351         
352         //get breaking segment
353         int pos = link.findNearestSegment(dragSplitPos);
354
355         //save points after breaking point
356         mxPoint[] saveStartPoints = link.getPoints(pos, true);
357         mxPoint[] saveEndPoints = link.getPoints(pos, false);
358         
359         
360         //disable events
361         getModel().beginUpdate();
362         getModel().remove(link);
363         getModel().endUpdate();
364
365         BasicLink newLink1 = BasicLink.createLinkFromPorts(linkSource, splitBlock.getIn());
366         newLink1.setGeometry(new mxGeometry(0,0,80,80));
367         newLink1.setSource(linkSource);
368         newLink1.setTarget(splitBlock.getIn());
369
370         //add points after breaking point in the new link
371         if (saveStartPoints != null) {
372                 for (int i = 0; i < saveStartPoints.length; i++) {
373                         newLink1.addPoint(saveStartPoints[i].getX(), saveStartPoints[i].getY());
374                 }
375         }
376         addCell(newLink1);
377         
378         BasicLink newLink2 = BasicLink.createLinkFromPorts(splitBlock.getOut1(), linkTarget);
379         newLink2.setGeometry(new mxGeometry(0,0,80,80));
380         newLink2.setSource(splitBlock.getOut1());
381         newLink2.setTarget(linkTarget);
382         //add points after breaking point in the new link
383         if (saveEndPoints != null) {
384                 for (int i = 0; i < saveEndPoints.length; i++) {
385                         newLink2.addPoint(saveEndPoints[i].getX(), saveEndPoints[i].getY());
386                 }
387         }
388         addCell(newLink2);
389
390         if(target instanceof BasicLink) {
391             //build link inverted ! it will be invert later
392             ((BasicLink)target).setTarget(splitBlock.getOut2());
393             addCell(target);
394         } else {
395             BasicLink newLink3 = BasicLink.createLinkFromPorts(splitBlock.getOut2(), (BasicPort) target);
396             newLink3.setGeometry(new mxGeometry(0,0,80,80));
397             newLink3.setSource(splitBlock.getOut2());
398             newLink3.setTarget((mxCell) target);
399             addCell(newLink3);
400         }
401         
402         dragSplitPos = null;
403         refresh();
404         getModel().endUpdate();
405         
406         return splitBlock;
407     }
408     
409     public XcosDiagram() {
410         super();
411         getModel().addListener(mxEvent.UNDO, undoEnabler);
412         getView().addListener(mxEvent.UNDO, undoEnabler);
413         // The association is inverted there (by the parameter)
414         new XcosShortCut(this);
415         mxCodec codec = new mxCodec();
416
417         try {
418             File uri = new File(System.getenv("SCI"));
419             String xml = mxUtils.readFile(System.getenv("SCI")+ "/modules/xcos/etc/Xcos-style.xml");
420             xml = xml.replaceAll("\\$SCILAB", uri.toURI().toURL().toString());
421             Document document = mxUtils.parse(xml);
422             codec.decode(document.getDocumentElement(), getStylesheet());
423         } catch (IOException e) {
424             e.printStackTrace();
425         }
426
427         getAsComponent().setToolTips(true);
428
429         // Forbid disconnecting cells once it is connected.
430         setCellsDisconnectable(false);
431
432         // Forbid pending edges.
433         //setAllowDanglingEdges(false);
434         setAllowDanglingEdges(true);
435
436         // Cannot connect port to itself.
437         setAllowLoops(false);
438
439         // Override isCellResizable to filter what the user can resize
440         setCellsResizable(true);
441
442         // force auto resize cell
443         setAutoSizeCells(true);
444
445         /* Labels use HTML if not equal to interface function name */
446         setHtmlLabels(true);
447
448         //
449         //setCloneInvalidEdges(false);
450         setCloneInvalidEdges(true);
451
452         // Override isCellEditable to filter what the user can edit
453         setCellsEditable(true);
454         // This enable stop editing cells when pressing Enter.
455         getAsComponent().setEnterStopsCellEditing(false);
456
457         setConnectableEdges(true);
458         getAsComponent().setTolerance(1);
459         
460         getAsComponent().getViewport().setOpaque(false);
461         getAsComponent().setBackground(Color.WHITE);
462
463         setMultiplicities();
464         
465         // Add a listener to track when model is changed
466         getModel().addListener(XcosEvent.CHANGE, new ModelTracker());
467         
468         setGridVisible(true);
469         
470         ((mxCell) getDefaultParent()).setId((new UID()).toString());
471         ((mxCell) getModel().getRoot()).setId((new UID()).toString());
472     }
473
474     /**
475      * Install the multiplicities (use for link checking)
476      */
477     private void setMultiplicities() {
478         mxMultiplicity[] multiplicities = new mxMultiplicity[10];
479
480         
481         // Input data port
482         multiplicities[0] = new PortCheck(ExplicitInputPort.class, Collections
483                 .unmodifiableList(new ArrayList<Class<? extends mxCell>>() {
484                     private static final long serialVersionUID = -4987163442006736665L;
485                     {
486                         add(ExplicitOutputPort.class);
487                         add(ExplicitLink.class);
488                     }
489                 }), XcosMessages.LINK_ERROR_EXPLICIT_IN);
490         multiplicities[1] = new PortCheck(ImplicitInputPort.class, Collections
491                 .unmodifiableList(new ArrayList<Class<? extends mxCell>>() {
492                     private static final long serialVersionUID = 886376532181210926L;
493                     {
494                         add(ImplicitOutputPort.class);
495                         add(ImplicitInputPort.class);
496                         add(ImplicitLink.class);
497                     }
498                 }), XcosMessages.LINK_ERROR_IMPLICIT_IN);
499
500         // Output data port
501         multiplicities[2] = new PortCheck(ExplicitOutputPort.class, Collections
502                 .unmodifiableList(new ArrayList<Class<? extends mxCell>>() {
503                     private static final long serialVersionUID = 4594127972486054821L;
504                     {
505                         add(ExplicitInputPort.class);
506                     }
507                 }), XcosMessages.LINK_ERROR_EXPLICIT_OUT);
508         multiplicities[3] = new PortCheck(ImplicitOutputPort.class, Collections
509                 .unmodifiableList(new ArrayList<Class<? extends mxCell>>() {
510                     private static final long serialVersionUID = -3719677806532507973L;
511                     {
512                         add(ImplicitInputPort.class);
513                         add(ImplicitOutputPort.class);
514                         add(ImplicitLink.class);
515                     }
516                 }), XcosMessages.LINK_ERROR_IMPLICIT_OUT);
517
518         // Control port
519         multiplicities[4] = new PortCheck(ControlPort.class, Collections
520                 .unmodifiableList(new ArrayList<Class<? extends mxCell>>() {
521                     private static final long serialVersionUID = 2941077191386058497L;
522                     {
523                         add(CommandPort.class);
524                         add(CommandControlLink.class);
525                     }
526                 }), XcosMessages.LINK_ERROR_EVENT_IN);
527
528         // Command port
529         multiplicities[5] = new PortCheck(CommandPort.class, Collections
530                 .unmodifiableList(new ArrayList<Class<? extends mxCell>>() {
531                     private static final long serialVersionUID = -3470370027962480362L;
532                     {
533                         add(ControlPort.class);
534                     }
535                 }), XcosMessages.LINK_ERROR_EVENT_OUT);
536
537         // ExplicitLink connections
538         multiplicities[6] = new PortCheck(ExplicitLink.class, Collections
539                 .unmodifiableList(new ArrayList<Class<? extends mxCell>>() {
540                     private static final long serialVersionUID = 7423543162930147373L;
541
542                     {
543                         add(ExplicitInputPort.class);
544                     }
545                 }), XcosMessages.LINK_ERROR_EVENT_OUT);
546
547         // ImplicitLink connections
548         multiplicities[7] = new PortCheck(ImplicitLink.class, Collections
549                 .unmodifiableList(new ArrayList<Class<? extends mxCell>>() {
550                     private static final long serialVersionUID = 7775100011122283282L;
551
552                     {
553                         add(ImplicitInputPort.class);
554                         add(ImplicitOutputPort.class);
555                     }
556                 }), XcosMessages.LINK_ERROR_EVENT_OUT);
557
558         // CommandControlLink connections
559         multiplicities[8] = new PortCheck(CommandControlLink.class, Collections
560                 .unmodifiableList(new ArrayList<Class<? extends mxCell>>() {
561                     private static final long serialVersionUID = 3260421433507192386L;
562
563                     {
564                         add(ControlPort.class);
565                     }
566                 }), XcosMessages.LINK_ERROR_EVENT_OUT);
567
568         // Already connected port
569         multiplicities[9] = new PortCheck(BasicPort.class, Collections
570                 .unmodifiableList(new ArrayList<Class<? extends mxCell>>() {
571                     private static final long serialVersionUID = 6376349598052836660L;
572
573                     {
574                         add(BasicPort.class);
575                     }
576                 }), XcosMessages.LINK_ERROR_ALREADY_CONNECTED);
577
578         setMultiplicities(multiplicities);
579     }
580
581     /**
582      * Install all needed Listeners.
583      */
584     public void installListeners() {
585
586         // Property change Listener
587         // Will say if a diagram has been modified or not.
588         getAsComponent().addPropertyChangeListener(new PropertyChangeListener() {
589             public void propertyChange(PropertyChangeEvent e) {
590                 if (e.getPropertyName().compareTo("modified") == 0) {
591                     if ((Boolean) e.getOldValue() != (Boolean) e.getNewValue()) {
592                         updateTabTitle();
593                     }
594                 }
595             }
596         });
597
598         // Track when superblock ask a parent refresh.
599         addListener(XcosEvent.SUPER_BLOCK_UPDATED, new SuperBlockUpdateTracker()); 
600
601         // Track when cells are added.
602         addListener(XcosEvent.CELLS_ADDED, new CellAddedTracker(this)); 
603
604         // Track when cells are deleted.
605         addListener(XcosEvent.CELLS_REMOVED, new CellRemovedTracker(this)); 
606                 
607         // Track when resizing a cell.
608         addListener(XcosEvent.CELLS_RESIZED, new CellResizedTracker());
609         
610         // Track when we have to force a Block value
611         addListener(XcosEvent.FORCE_CELL_VALUE_UPDATE, new ForceCellValueUpdate());
612         
613         // Update the blocks view on undo/redo
614         getUndoManager().addListener(mxEvent.UNDO, new UndoUpdateTracker());
615         getUndoManager().addListener(mxEvent.REDO, new UndoUpdateTracker());
616         
617         getAsComponent().getGraphControl().addMouseListener(new XcosMouseListener(this));
618
619         getAsComponent().addKeyListener(new XcosKeyListener(this));
620
621         addListener(XcosEvent.ADD_PORTS, new mxIEventListener() {
622             public void invoke(Object source, mxEventObject evt) {
623                 getModel().beginUpdate();
624                 refresh();
625                 BasicBlock updatedBlock = (BasicBlock) evt.getProperty("block");
626                 BlockPositioning.updateBlockView(updatedBlock);
627                 getModel().endUpdate();
628             }
629         });     
630         
631     }
632
633     /**
634      * modelTracker
635      * Called when mxEvents.CHANGE occurs on a model
636      */
637     private class ModelTracker implements mxIEventListener {
638         public void invoke(Object source, mxEventObject evt) {
639             List<mxAtomicGraphModelChange> changes = (List<mxAtomicGraphModelChange>) (evt.getProperty("changes"));
640             List<Object> objects = new ArrayList<Object>();
641             getModel().beginUpdate();
642             for (int i = 0; i < changes.size(); ++i) {
643                 if (changes.get(i) instanceof mxChildChange) {
644                     if (((mxChildChange) changes.get(i)).getChild() instanceof SplitBlock) {
645                         continue;
646                     }
647
648                     if (((mxChildChange) changes.get(i)).getChild() instanceof BasicBlock) {
649                         BasicBlock currentCell = (BasicBlock) ((mxChildChange) changes.get(i)).getChild();
650                         objects.add(currentCell);
651                     }
652                 }
653             }
654             if (!objects.isEmpty()) {
655                 Object[] firedCells = new Object[objects.size()];
656                 for (int j = 0;  j < objects.size(); ++j) {
657                     firedCells[j] = objects.get(j);
658                 }
659                 //fireEvent(XcosEvent.FORCE_CELL_RESHAPE, new mxEventObject(new Object[] {firedCells}));
660                 fireEvent(new mxEventObject(XcosEvent.FORCE_CELL_VALUE_UPDATE, "cells", firedCells));
661             }
662             getModel().endUpdate();
663         }
664     }
665     /**
666      * ForceCellValueUpdate
667      * Called when we want a block content to update.
668      */
669     private class ForceCellValueUpdate implements mxIEventListener {
670         public void invoke(Object source, mxEventObject evt) {
671             Object[] cells = (Object[]) evt.getProperty("cells");
672
673             getModel().beginUpdate();
674
675             for (int i = 0; i < cells.length; ++i) {
676                 
677                 Object cell = cells[i];
678                 
679                 if (cell instanceof BasicBlock) {
680                     if (getCellStyle(cell).get("displayedLabel") != null) {
681                         ((mxCell) cell).setValue("<html><body> " + getCellStyle(cell).get("displayedLabel") + " </body></html>");
682                     }
683
684                     mxRectangle preferedSize = getPreferredSizeForCell(cell);
685                     mxGeometry cellSize = ((mxCell) cell).getGeometry();
686
687                     ((mxCell) cell).setGeometry(new mxGeometry(cellSize.getX(), cellSize.getY(),
688                             Math.max(preferedSize.getWidth(), cellSize.getWidth()),
689                             Math.max(preferedSize.getHeight(), cellSize.getHeight())));
690                     cellsResized(new Object[] {cell}, new mxRectangle[]{((mxCell) cell).getGeometry()});
691                 }
692             }
693             getModel().endUpdate();
694             refresh();
695         }
696     }
697     
698     /**
699 <<<<<<< HEAD:scilab/modules/xcos/src/java/org/scilab/modules/xcos/graph/XcosDiagram.java
700 =======
701      *  ForceCellReshapeTracker
702      *  Called when we want a Block to reshape for it's ports positions.
703      */
704     private class ForceCellReshapeTracker implements mxIEventListener {
705         public void invoke(Object source, mxEventObject evt) {
706             Object[] cells =  (Object[]) evt.getProperty("cells");
707             getModel().beginUpdate();
708             for (int i = 0; i <  cells.length; ++i) {
709                 Object cell = cells[i];
710                 if (cell instanceof BasicBlock) {
711                     BlockPositioning.updateBlockView((BasicBlock) cell);
712                 }
713             }
714             getModel().endUpdate();
715         }
716     }
717     
718     /**
719 >>>>>>> e48febf... API compatibility with jgraphx-1.2.0.5:scilab/modules/xcos/src/java/org/scilab/modules/xcos/graph/XcosDiagram.java
720      *  SuperBlockUpdateTracker
721      *  Called when adding some port in a SuperBlock diagram
722      *  to update current sub-diagram (i.e SuperBlock) representation.
723      */
724     private class SuperBlockUpdateTracker implements mxIEventListener {
725         public void invoke(Object source, mxEventObject evt) {
726             assert evt.getProperty("block") instanceof SuperBlock;
727             SuperBlock updatedBlock = (SuperBlock) evt.getProperty("block");
728             updatedBlock.setRealParameters(BlockWriter
729                     .convertDiagramToMList(updatedBlock.getChild()));
730             if (updatedBlock.getParentDiagram() instanceof SuperBlockDiagram) {
731                 SuperBlock parentBlock = ((SuperBlockDiagram) updatedBlock
732                         .getParentDiagram()).getContainer();
733                 parentBlock.getParentDiagram().fireEvent(new mxEventObject(XcosEvent.SUPER_BLOCK_UPDATED, "block", parentBlock));
734             }
735             BlockPositioning.updateBlockView(updatedBlock);
736             refresh();
737         }
738     }
739
740     /**
741      * CellAddedTracker
742      * Called when mxEvents.CELLS_ADDED is fired.
743      */
744     private class CellAddedTracker implements mxIEventListener {
745         private XcosDiagram diagram = null;
746
747         public CellAddedTracker(XcosDiagram diagram) {
748                 this.diagram = diagram;
749         }
750
751         public void invoke(Object source, mxEventObject evt) {
752                 Object[] cells = (Object[]) evt.getProperty("cells");
753                 
754                 diagram.getModel().beginUpdate();
755                 for (int i = 0; i < cells.length; ++i) {
756
757 //                      ((mxCell) cells[i]).setId((new UID()).toString());
758
759                                 if (cells[i] instanceof BasicBlock) {
760                                         // Store all AfficheBlocks in a dedicated HasMap
761                                         if (cells[i] instanceof AfficheBlock) {
762                                                 AfficheBlock affich = (AfficheBlock) cells[i];
763                                                 XcosTab.getAfficheBlocks().put(affich.getHashCode(), affich);
764                                         }
765                                         // Update parent on cell addition
766                                         ((BasicBlock) cells[i]).setParentDiagram(diagram);
767                                 }
768                 }
769                 //fireEvent(XcosEvent.FORCE_CELL_VALUE_UPDATE, new mxEventObject(new Object[] {cells}));
770                 diagram.getModel().endUpdate();
771         }
772     }
773
774     /**
775      * CellRemovedTracker
776      * Called when mxEvents.CELLS_REMOVED is fired.
777      */
778     private class CellRemovedTracker implements mxIEventListener {
779         public CellRemovedTracker(XcosDiagram diagram) {
780         }
781
782         public void invoke(Object source, mxEventObject evt) {
783             Object[] cells = (Object[]) evt.getProperty("cells");
784             for (int i = 0; i < cells.length; i++) {
785                 if (cells[i] instanceof BasicLink) {
786                     BasicLink link = (BasicLink) cells[i];
787                     removeLink(link);
788                     if(waitPathAddEdge) {
789                         cancelDrawLinkAction();
790                     }
791                 }
792             }
793
794         }
795     }
796
797     private void removeLink(BasicLink link) {
798         if(!(link.getSource() instanceof BasicPort)) {
799             return;
800         }
801         
802         if(!(link.getTarget() instanceof BasicPort)) {
803             return;
804         }
805
806         BasicPort portSource = (BasicPort) link.getSource();
807         BasicPort portTarget = (BasicPort) link.getTarget();
808
809         SplitBlock split = null;
810         BasicPort saveSource = null;
811         BasicPort saveTarget = null;
812
813         if (portSource == null) { return; }
814         if (portTarget == null) { return; }
815
816         //remove input link
817         if (portTarget.getParent() instanceof SplitBlock) {
818                 split = (SplitBlock) portTarget.getParent();
819                 
820                 Object[] outLinks = getAllEdges(new Object[] {split.getOut1(), split.getOut2()});
821                 for (int i = 0; i < outLinks.length; i++) {
822                         BasicLink outLink = (BasicLink) outLinks[i];
823                         if (outLink.getTarget().getParent() instanceof SplitBlock) {
824                                 removeCells(new Object[]{outLink});
825                         }
826                 }
827         }
828         
829         //Finally delete split and old associated links
830         if (split != null) {
831                 removeCells(new Object[]{split});
832         }
833
834         //reset variables
835         split = null;
836         saveSource = null;
837         saveTarget = null;
838
839         if (portSource.getParent() instanceof SplitBlock) {
840                 split = (SplitBlock) portSource.getParent();
841
842                 //remove out1, so link between in.source and out2.target
843                 if (split.getOut1() == portSource) {
844                         //save source and target ports 
845                         saveSource = getOppositePort(split.getIn());
846                         saveTarget = getOppositePort(split.getOut2());
847                 } else if (split.getOut2() == portSource) {
848                         //save source and target ports 
849                         saveSource = getOppositePort(split.getIn());
850                         saveTarget = getOppositePort(split.getOut1());
851                 }
852         }
853
854         if (saveSource != null && saveTarget != null) {
855                 //create new link
856                 BasicLink newLink = BasicLink.createLinkFromPorts(saveSource, saveTarget);
857                 newLink.setGeometry(new mxGeometry(0,0,80,80));
858
859                 Object[] saveLinks = getAllEdges(new Object[]{saveSource, saveTarget});
860                 for (int k = 0; k < saveLinks.length; k++) {
861                         mxPoint[] savePts = ((BasicLink) saveLinks[k]).getPoints(0, false);
862                         if (savePts != null) {
863                                 for (int j = 0; j < savePts.length; j++) {
864                                         newLink.addPoint(savePts[j].getX(), savePts[j].getY());
865                                 }
866                         }
867                 }
868
869                 newLink.setSource(saveSource);
870                 newLink.setTarget(saveTarget);
871                 addCell(newLink);
872
873                 //unlink split and delete unlinked links
874         }
875
876         if (split != null) {
877                 split.unlinkAndClean();
878                 removeCells(new Object[]{split});
879         }
880     }
881
882     private BasicPort getOppositePort(BasicPort source) {
883         Object[] objs = getAllEdges(new Object[]{source});
884         if (objs.length == 0 || objs.length > 1) {
885                 return null;
886         }
887         
888         BasicLink link = (BasicLink) objs[0];
889         if (link.getSource() == source) {
890                 return (BasicPort) link.getTarget();
891         } else {
892                 return (BasicPort) link.getSource();
893         }
894     }
895     /**
896      * CellResizedTracker
897      * Called when mxEvents.CELLS_RESIZED is fired. 
898      */
899     private class CellResizedTracker implements mxIEventListener {
900         public void invoke(Object source, mxEventObject evt) {
901             Object[] cells = (Object[]) evt.getProperty("cells");
902             getModel().beginUpdate();
903             for (int i = 0; i < cells.length; ++i) {
904                 if (cells[i] instanceof BasicBlock) {
905                     BlockPositioning.updateBlockView((BasicBlock) cells[i]);
906                 }
907             }
908             getModel().endUpdate();
909         }
910     }
911
912     /**
913      * Update the modified block on undo/redo
914      */
915    private class UndoUpdateTracker implements mxIEventListener {
916         public void invoke(Object source, mxEventObject evt) {
917             List<mxUndoableChange> changes = ((mxUndoableEdit) evt.getProperty("edit")).getChanges();
918             Object[] changedCells = getSelectionCellsForChanges(changes);
919             getModel().beginUpdate();
920             for (Object object : changedCells) {
921                 if (object instanceof BasicBlock) {
922                     BasicBlock current = (BasicBlock) object;
923                     
924                     // When we change the style property we have to update some
925                     // BasiBlock fields
926                     if (changes.get(0) instanceof mxStyleChange) {
927                         current.updateFieldsFromStyle();
928                     }
929                     
930                     // Update the block position
931                     BlockPositioning.updateBlockView(current);
932                 }
933             }
934             getModel().endUpdate();
935             refresh();
936         }
937     };
938     
939     private class XcosKeyListener implements KeyListener{
940
941         public XcosKeyListener(XcosDiagram diagram) {}
942
943         public void keyTyped(KeyEvent e) {}
944
945         public void keyPressed(KeyEvent e) {}   
946
947         public void keyReleased(KeyEvent e) {
948             if(e.getKeyChar() == KeyEvent.VK_ESCAPE) {
949                 if(drawLink != null) {
950                     getModel().remove(drawLink);
951                     cancelDrawLinkAction();
952                 }                   
953             }
954         }
955     }
956     /**
957      * MouseListener inner class
958      */
959     private class XcosMouseListener implements MouseListener {
960         private XcosDiagram diagram = null;
961
962         public XcosMouseListener(XcosDiagram diagram) {
963                 this.diagram = diagram;
964         }
965
966         public void mouseClicked(MouseEvent e) {
967                 Object cell = getAsComponent().getCellAt(e.getX(), e.getY());
968
969                 // Double Click within empty diagram Area
970                 if (e.getClickCount() >= 2 && SwingUtilities.isLeftMouseButton(e) && cell == null) {
971                         TextBlock textBlock = (TextBlock) BlockFactory.createBlock(BlockInterFunction.TEXT_f);
972                         textBlock.getGeometry().setX(e.getX() - textBlock.getGeometry().getWidth() / 2.0);
973                         textBlock.getGeometry().setY(e.getY() - textBlock.getGeometry().getWidth() / 2.0);
974                         addCell(textBlock);
975                         return;
976                 }
977
978                 // Double Click within some component
979                 if (e.getClickCount() >= 2 && SwingUtilities.isLeftMouseButton(e) && cell != null)
980                 {
981                         getModel().beginUpdate();
982                         if (cell instanceof BasicBlock) {
983                                 BasicBlock block = (BasicBlock) cell;
984                                 e.consume();
985                                 block.openBlockSettings(buildEntireContext());
986                         }
987                         if (cell instanceof BasicLink) {
988                                 ((BasicLink) cell).insertPoint(e.getX(), e.getY());
989                         }
990                         getModel().endUpdate();
991                         refresh();
992                 }
993
994                 // Ctrl + Shift + Right Middle Click : for debug !!
995                 if (e.getClickCount() >= 2 && SwingUtilities.isMiddleMouseButton(e)
996                                 && e.isShiftDown() && e.isControlDown())
997                 {
998                         System.err.println("[DEBUG] Click at position : " + e.getX() + " , " + e.getY());
999                         if (cell == null) {
1000                                 System.err.println("[DEBUG] Click on diagram");
1001                                 System.err.println("Default Parent ID : " + ((mxCell) getDefaultParent()).getId());
1002                                 System.err.println("Model root ID : " + ((mxCell) getModel().getRoot()).getId());
1003                                 System.err.println("getParentWindow : " + (getParentTab() == null ? null : getParentTab().getParentWindow()));
1004                         } else {
1005                                 System.err.println("[DEBUG] Click on : " + cell);
1006                                 System.err.println("[DEBUG] Style : " + ((mxCell) cell).getStyle());
1007                                 System.err.println("[DEBUG] NbEdges : " + ((mxCell) cell).getEdgeCount());
1008                                 System.err.println("[DEBUG] NbChildren : " + ((mxCell) cell).getChildCount());
1009                                 for (int i = 0; i < ((mxCell) cell).getChildCount(); i++) {
1010                                         System.err.println("[DEBUG] Child NbEdges : " + ((mxCell) cell).getChildAt(i).getEdgeCount());
1011                                 }
1012
1013                                 if(cell instanceof BasicLink) {
1014                                         System.err.println("[DEBUG] Link Points : " + ((BasicLink) cell).getPointCount());
1015                                 }
1016                         }
1017
1018                 }
1019
1020                 // Context menu
1021                 if ((e.getClickCount() == 1 && SwingUtilities.isRightMouseButton(e))
1022                                 || e.isPopupTrigger()
1023                                 || XcosMessages.isMacOsPopupTrigger(e)) {
1024
1025                         if (cell == null) {
1026                                 // Display diagram context menu
1027                                 ContextMenu menu = ScilabContextMenu.createContextMenu();
1028
1029                                 menu.add(UndoAction.undoMenu((ScilabGraph) getAsComponent().getGraph()));
1030                                 menu.add(RedoAction.redoMenu((ScilabGraph) getAsComponent().getGraph()));
1031                                 menu.add(PasteAction.pasteMenu((ScilabGraph) getAsComponent().getGraph()));
1032                                 menu.add(SelectAllAction.createMenu((ScilabGraph) getAsComponent().getGraph()));
1033                                 /*---*/
1034                                 menu.getAsSimpleContextMenu().addSeparator();
1035                                 /*---*/
1036                                 menu.add(SetContextAction.createMenu((ScilabGraph) getAsComponent().getGraph()));
1037                                 menu.add(SetupAction.createMenu((ScilabGraph) getAsComponent().getGraph()));
1038
1039                                 if(diagram instanceof SuperBlockDiagram) {
1040                                         /*---*/
1041                                         menu.getAsSimpleContextMenu().addSeparator();
1042                                         /*---*/
1043                                         menu.add(ShowParentAction.createMenu(diagram));
1044                                 }
1045                                 /*---*/
1046                                 menu.getAsSimpleContextMenu().addSeparator();
1047                                 /*---*/
1048                                 menu.add(ZoomInAction.zoominMenu((ScilabGraph) getAsComponent().getGraph()));
1049                                 menu.add(ZoomOutAction.zoomoutMenu((ScilabGraph) getAsComponent().getGraph()));
1050                                 /*---*/
1051                                 menu.getAsSimpleContextMenu().addSeparator();
1052                                 /*---*/
1053                                 menu.add(DiagramBackgroundAction.createMenu((ScilabGraph) getAsComponent().getGraph()));
1054                                 /*---*/
1055                                 menu.getAsSimpleContextMenu().addSeparator();
1056                                 /*---*/
1057                                 menu.add(XcosDocumentationAction.createMenu((ScilabGraph) getAsComponent().getGraph()));
1058
1059                                 ((SwingScilabContextMenu) menu.getAsSimpleContextMenu()).setLocation(MouseInfo.getPointerInfo().getLocation().x, MouseInfo.getPointerInfo().getLocation().y);
1060
1061                                 menu.setVisible(true);
1062
1063                         } else {
1064                                 // Display object context menu
1065                                 if (cell instanceof BasicBlock) {
1066                                         BasicBlock block = (BasicBlock) cell;
1067                                         block.openContextMenu((ScilabGraph) getAsComponent().getGraph());
1068                                 }
1069                                 if (cell instanceof BasicLink) {
1070                                         BasicLink link = (BasicLink) cell;
1071                                         link.openContextMenu((ScilabGraph) getAsComponent().getGraph());
1072                                 }
1073                         }
1074                 }
1075         }
1076
1077         public void mouseEntered(MouseEvent e) {
1078         }
1079
1080         public void mouseExited(MouseEvent e) {
1081         }
1082
1083         public void mousePressed(MouseEvent e) {
1084         }
1085
1086         public void mouseReleased(MouseEvent e) {
1087                 Object cell = getAsComponent().getCellAt(e.getX(), e.getY());
1088
1089                 if(SwingUtilities.isLeftMouseButton(e)) {
1090                         if (waitSplitRelease) {
1091                                 dragSplitPos = new mxPoint(e.getX(), e.getY());
1092                                 waitSplitRelease = false;
1093                                 addSplitEdge(splitLink, splitPort);
1094                         } else if(waitPathRelease){
1095                                 //Tips for ignore first mouse release after drag
1096                                 waitPathRelease = false;
1097                                 waitPathAddEdge = true;
1098
1099                                 if(!e.isControlDown()) {
1100                                         //adjust final point
1101                                         mxGeometry geoPort = drawLink.getSource().getGeometry();
1102                                         mxGeometry geoBlock = drawLink.getSource().getParent().getGeometry();
1103                                         mxPoint lastPoint = new mxPoint(geoBlock.getX() + geoPort.getCenterX(), geoBlock.getY() + geoPort.getCenterY());
1104                                         mxPoint point = getPointPosition(lastPoint, new mxPoint(e.getX(), e.getY()));
1105
1106                                         getModel().beginUpdate();
1107                                         drawLink.getGeometry().setTargetPoint(point);
1108                                         getModel().endUpdate();
1109                                         refresh();
1110                                 }
1111                         } else if(waitPathAddEdge){
1112                                 System.err.println("mouseReleased : waitPathAddEdge");
1113                                 if(drawLink != null) {
1114                                         getModel().beginUpdate();
1115                                         //move end of link and add point at old position
1116                                         mxGeometry geo = drawLink.getGeometry();
1117                                         drawLink.addPoint(geo.getTargetPoint().getX(), geo.getTargetPoint().getY());
1118                                         setSelectionCell(drawLink);
1119
1120                                         if(cell != null) {
1121                                                 mxICell source = drawLink.getSource();
1122
1123                                                 StringBuffer error = checkMultiplicities(drawLink, source, cell);
1124                                                 if(error == null) {
1125                                                         if(cell instanceof BasicLink && cell != drawLink) { //no loop
1126                                                                 //draw link with a SplitBlock
1127                                                                 dragSplitPos = new mxPoint(e.getX(), e.getY());
1128                                                                 addSplitEdge((BasicLink)cell, drawLink);
1129                                                         } else {
1130                                                                 getModel().setTerminal(drawLink, cell, false);
1131                                                         }
1132
1133                                                         //invert source and target if needed
1134                                                         if(checkEdgeDirection(source, cell) == false) {
1135                                                                 getModel().beginUpdate();
1136                                                                 drawLink.invertDirection();
1137                                                                 getModel().endUpdate();
1138                                                         }
1139
1140                                                         //reset info, flags and object
1141                                                         cancelDrawLinkAction();
1142                                                 } else {
1143                                                         if(cell != drawLink) {
1144                                                                 JOptionPane.showMessageDialog(getAsComponent(), error);
1145                                                         }
1146                                                         setSelectionCell(drawLink);
1147                                                 }
1148                                         } else {
1149                                                 if(!e.isControlDown()) {
1150                                                         geo.setTargetPoint(getPointPosition(geo.getTargetPoint(), new mxPoint(e.getX(), e.getY())));
1151                                                 } else {
1152                                                         geo.setTargetPoint(new mxPoint(e.getX(), e.getY()));
1153                                                 }
1154                                         }
1155                                         getModel().endUpdate();
1156                                         refresh();
1157                                 } else {
1158                                         cancelDrawLinkAction();
1159                                 }
1160                         } else {
1161                                 dragSplitPos = null;
1162                         }
1163                 }
1164         }
1165     }
1166
1167     static mxPoint getPointPosition(mxPoint origin, mxPoint click) {
1168         boolean signX = click.getX() > origin.getX();
1169         boolean signY = click.getY() > origin.getY();
1170         double diffX = Math.abs(click.getX() - origin.getX());
1171         double diffY = Math.abs(click.getY() - origin.getY());
1172
1173         if(diffX > diffY) {
1174             if(diffY > (diffX / 2)) { //diagonal
1175                 diffY = diffX;
1176             } else { //orthogonal
1177                 diffY = 0;
1178             }
1179         } else { // < or ==
1180             if(diffX > (diffY / 2)) { //diagonal
1181                 diffX = diffY;
1182             } else { //orthogonal
1183                 diffX = 0;
1184             }
1185         }
1186
1187         //restore signs
1188         if(signX == false) {
1189             diffX = -diffX;
1190         }
1191
1192         if(signY == false) {
1193             diffY = - diffY;
1194         }
1195
1196         return new mxPoint(origin.getX() + diffX, origin.getY() + diffY); 
1197     }
1198     /*
1199      * Manage Group to be CellFoldable i.e with a (-) to reduce
1200      * and a (+) to expand them.
1201      * Only non-Block / non-Port Cell are foldable. 
1202      * 
1203      * (non-Javadoc)
1204      * @see com.mxgraph.view.mxGraph#isCellFoldable(java.lang.Object, boolean)
1205      */
1206     public boolean isCellFoldable(Object cell, boolean collapse) {
1207                 return !(cell instanceof BasicBlock) && super.isCellFoldable(cell, collapse);
1208     }
1209
1210         public boolean isCellClonable(Object cell) {
1211                 return true;
1212         }
1213         
1214         public boolean isCellSelectable(Object cell) {
1215             if(cell instanceof BasicPort) {
1216                 return false;
1217             }
1218             return super.isCellSelectable(cell);
1219         }
1220         
1221         public boolean isCellMovable(Object cell) {
1222                 if (cell instanceof BasicPort) {
1223                         return false;
1224                 }
1225
1226                 boolean movable = false;
1227                 Object[] cells =  this.getSelectionCells();
1228
1229                 //don't move if selection is only links
1230                 for (int i = 0; i < cells.length; i++) {
1231                         if (!(cells[i] instanceof BasicLink)) {
1232                                 movable = true;
1233                                 break;
1234                         }
1235                 }
1236
1237                 return movable && super.isCellMovable(cell);
1238     }
1239
1240     public boolean isCellResizable(Object cell) {
1241         if (cell instanceof SplitBlock) {
1242                 return false;
1243         }
1244         return (cell instanceof BasicBlock) && super.isCellResizable(cell);
1245     }
1246
1247     public boolean isCellDeletable(Object cell) {
1248         if (cell instanceof BasicBlock && ((BasicBlock) cell).isLocked()) {
1249                 return false;
1250         }
1251
1252         return !(cell instanceof BasicPort)     && super.isCellDeletable(cell);
1253     }
1254
1255     public boolean isCellEditable(Object cell) {
1256         return (cell instanceof TextBlock) && super.isCellDeletable(cell);
1257     }
1258
1259     public boolean isCellDisconnectable(Object cell, Object terminal,boolean source) {
1260         return super.isCellDisconnectable(cell, terminal, source);
1261     }
1262     
1263     public boolean isCellConnectable(Object cell)
1264     {
1265         //currently in draw link action
1266         if(waitPathAddEdge) {
1267             if(drawLink != null) {
1268                 StringBuffer error = checkMultiplicities(drawLink, drawLink.getSource(), cell);
1269                 if(error == null) {
1270                     return true;
1271                 }
1272                 return false;
1273             }
1274         }
1275         
1276         if(cell instanceof BasicBlock)  {
1277             return false;
1278         }
1279         
1280         if(cell instanceof BasicPort) {
1281             int sourceOut = mxGraphModel.getDirectedEdgeCount(getModel(), cell, true);
1282             int targetIn = mxGraphModel.getDirectedEdgeCount(getModel(), cell, false);
1283             
1284             if(sourceOut > 0 || targetIn > 0) {
1285                 return false;
1286             }
1287         }
1288         return !(cell instanceof BasicBlock) && super.isCellConnectable(cell);
1289     }
1290
1291     public boolean isAutoSizeCell(Object cell) {
1292         return (cell instanceof AfficheBlock) || super.isAutoSizeCell(cell);
1293     }
1294
1295
1296     public void dumpToHdf5File(String fileName) {
1297         if (fileName == null) {
1298             FileChooser fc = ScilabFileChooser.createFileChooser();
1299             fc.setInitialDirectory(getSavedFile());
1300             fc.setMultipleSelection(false);
1301             fc.displayAndWait();
1302
1303             if (fc.getSelection() == null || fc.getSelection().length == 0 || fc.getSelection()[0].equals("")) {
1304                 return;
1305             }
1306             fileName = fc.getSelection()[0];
1307             System.out.println("Saving to file : {" + fileName + "}");
1308         }
1309
1310         BlockWriter.writeDiagramToFile(fileName, this);
1311     }
1312
1313     public double getFinalIntegrationTime() {
1314         return finalIntegrationTime;
1315     }
1316
1317     public void setFinalIntegrationTime(double finalIntegrationTime) {
1318         this.finalIntegrationTime = finalIntegrationTime;
1319     }
1320
1321     public double getIntegratorAbsoluteTolerance() {
1322         return integratorAbsoluteTolerance;
1323     }
1324
1325     public void setIntegratorAbsoluteTolerance(double integratorAbsoluteTolerance) {
1326         this.integratorAbsoluteTolerance = integratorAbsoluteTolerance;
1327     }
1328
1329     public double getIntegratorRelativeTolerance() {
1330         return integratorRelativeTolerance;
1331     }
1332
1333     public void setIntegratorRelativeTolerance(double integratorRelativeTolerance) {
1334         this.integratorRelativeTolerance = integratorRelativeTolerance;
1335     }
1336
1337     public double getMaximumStepSize() {
1338         return maximumStepSize;
1339     }
1340
1341     public void setMaximumStepSize(double maximumStepSize) {
1342         this.maximumStepSize = maximumStepSize;
1343     }
1344
1345     public double getMaxIntegrationTimeinterval() {
1346         return maxIntegrationTimeinterval;
1347     }
1348
1349     public void setMaxIntegrationTimeinterval(double maxIntegrationTimeinterval) {
1350         this.maxIntegrationTimeinterval = maxIntegrationTimeinterval;
1351     }
1352
1353     public double getRealTimeScaling() {
1354         return realTimeScaling;
1355     }
1356
1357     public void setRealTimeScaling(double realTimeScaling) {
1358         this.realTimeScaling = realTimeScaling;
1359     }
1360
1361     public double getSolver() {
1362         return solver;
1363     }
1364
1365     public void setSolver(double solver) {
1366         this.solver = solver;
1367     }
1368
1369     public double getToleranceOnTime() {
1370         return toleranceOnTime;
1371     }
1372
1373     public void setToleranceOnTime(double toleranceOnTime) {
1374         this.toleranceOnTime = toleranceOnTime;
1375     }
1376
1377     /**
1378      * Set the associated ViewPort
1379      * @param viewPort the Viewport
1380      */
1381     public void setViewPort(Tab viewPort) {
1382         this.viewPort = viewPort;
1383     }
1384
1385     /**
1386      * Get the associated ViewPort
1387      * @return the Viewport
1388      */
1389     public Tab getViewPort() {
1390         return viewPort;
1391     }
1392
1393     /**
1394      * Manage the visibility of the associated viewport
1395      * @param status new status
1396      */
1397     public void setViewPortVisible(boolean status) {
1398         // Hide/Show parent window if the viewport is the only tab
1399         if (viewPort.getParentWindow().getNbDockedObjects() == 1) {
1400             viewPort.getParentWindow().setVisible(status);
1401         }
1402         // Hide/Show viewport tab
1403         viewPort.setVisible(status);
1404
1405         // (Un)Check the corresponding menu
1406         viewPortMenu.setChecked(status);
1407     }
1408
1409     /**
1410      * Set menu used to manage Viewport visibility
1411      * @param menu the menu
1412      */
1413     public void setViewPortMenuItem(CheckBoxMenuItem menu) {
1414         this.viewPortMenu = menu;
1415     }
1416     /**
1417      * Manage the visibility of the grid and the associated menu
1418      * @param status new status
1419      */
1420     public void setGridVisible(boolean status) {
1421         setGridEnabled(status);
1422         getAsComponent().setGridVisible(status);
1423         getAsComponent().repaint();
1424
1425         // (Un)Check the corresponding menu
1426         if(gridMenu != null) {
1427             gridMenu.setChecked(status);
1428         }
1429     }
1430
1431     /**
1432      * Set menu used to manage Grid visibility
1433      * @param menu the menu
1434      */
1435     public void setGridMenuItem(CheckBoxMenuItem menu) {
1436         this.gridMenu = menu;
1437     }
1438
1439     /**
1440      * Close Xcos instance including all tabs
1441      */
1442     public void closeDiagram() {
1443         closeDiagram(false);
1444     }
1445     
1446     /**
1447      * Close Xcos instance including all tabs
1448      */
1449     public void closeDiagram(boolean fromScilab) {
1450
1451         boolean wantToClose = true;
1452
1453         if(canClose() == false) {
1454             setVisible(false);
1455             return;
1456         }
1457         
1458         if (isModified()) {
1459             // The diagram has been modified
1460             // Ask the user want he want to do !
1461             
1462             AnswerOption answer; 
1463             if(fromScilab == true) {
1464                 answer = ScilabModalDialog.show(getParentTab(), XcosMessages.DIAGRAM_MODIFIED, XcosMessages.XCOS, 
1465                         IconType.QUESTION_ICON, ButtonType.YES_NO);
1466             } else {
1467                 answer = ScilabModalDialog.show(getParentTab(), XcosMessages.DIAGRAM_MODIFIED, XcosMessages.XCOS, 
1468                         IconType.QUESTION_ICON, ButtonType.YES_NO_CANCEL);
1469             }
1470
1471             switch(answer) {
1472             case YES_OPTION :
1473                 // Save the diagram
1474                 if (!saveDiagram()) {
1475                         //if save is canceled, cancel close windows
1476                         wantToClose = false;
1477                 }
1478                 break;
1479             case NO_OPTION :
1480                 break;
1481             case CANCEL_OPTION :
1482                 // The user cancels
1483                 wantToClose = false;
1484                 break;
1485             }
1486         }
1487
1488         if (wantToClose) {
1489             if(getParentTab() != null) {
1490                 ScilabWindow xcosWindow = (ScilabWindow) UIElementMapper.getCorrespondingUIElement(getParentTab().getParentWindowId());
1491                 xcosWindow.removeTab(getParentTab());
1492                 viewPort.close();
1493             }
1494             XcosTab.closeDiagram(this);
1495             setOpened(false);
1496         }
1497     }
1498
1499     public boolean saveDiagram() {
1500         boolean isSuccess = false;
1501         if (getSavedFile() == null) {
1502             isSuccess = saveDiagramAs(null);
1503         } else {
1504             isSuccess = saveDiagramAs(getSavedFile());
1505         }
1506
1507         if (isSuccess) {
1508             setModified(false);
1509         }
1510
1511         return isSuccess;
1512     }
1513
1514     public boolean saveDiagramAs(String fileName) {
1515
1516         boolean isSuccess = false;
1517         info(XcosMessages.SAVING_DIAGRAM);
1518         this.getParentTab().getInfoBar().draw();
1519         if (fileName == null) {
1520             // Choose a filename
1521             SwingScilabFileChooser fc = ((SwingScilabFileChooser) ScilabFileChooser.createFileChooser().getAsSimpleFileChooser());
1522             fc.setTitle(XcosMessages.SAVE_AS);
1523             fc.setUiDialogType(JFileChooser.SAVE_DIALOG);
1524             fc.setMultipleSelection(false);
1525             if (this.getSavedFile() != null) {
1526                 fc.setSelectedFile(new File(this.getSavedFile()));
1527             }
1528
1529             SciFileFilter xcosFilter = new SciFileFilter("*.xcos", null, 0);
1530             SciFileFilter allFilter = new SciFileFilter("*.*", null, 1);
1531             fc.addChoosableFileFilter(xcosFilter);
1532             fc.addChoosableFileFilter(allFilter);
1533             fc.setFileFilter(xcosFilter);
1534
1535             fc.setAcceptAllFileFilterUsed(false);
1536             fc.displayAndWait();
1537
1538             if (fc.getSelection() == null || fc.getSelection().length == 0 || fc.getSelection()[0].equals("")) {
1539                 info(XcosMessages.EMPTY_INFO);
1540                 return isSuccess;
1541             }
1542             fileName = fc.getSelection()[0];
1543         }
1544         /* Extension checks */
1545         File file = new File(fileName);
1546         if(!file.exists()) {
1547             String extension = fileName.substring(fileName.lastIndexOf('.') + 1);
1548
1549             if (extension.equals(fileName)) {
1550                 /* No extension given --> .xcos added */
1551                 fileName += ".xcos";
1552             }
1553         }
1554
1555         XcosCodec codec = new XcosCodec();
1556         String xml = mxUtils.getXml(codec.encode(this));
1557
1558         try {
1559             mxUtils.writeFile(xml, fileName);
1560             isSuccess = true;
1561             resetUndoCounter();
1562         } catch (IOException e1) {
1563             e1.printStackTrace();
1564             isSuccess = false;
1565         }
1566
1567         if (isSuccess) {
1568             this.setSavedFile(fileName);
1569             File theFile = new File(fileName);
1570             setTitle(theFile.getName().substring(0, theFile.getName().lastIndexOf('.')));
1571             ConfigXcosManager.saveToRecentOpenedFiles(fileName);
1572             setModified(false);
1573         } else {
1574             XcosDialogs.couldNotSaveFile(this);
1575         }
1576         info(XcosMessages.EMPTY_INFO);
1577         return isSuccess;
1578     }
1579
1580     public void setTitle(String title) {
1581         super.setTitle(title);
1582         updateTabTitle();
1583     }
1584
1585     public void updateTabTitle() {
1586         String tabTitle = !isModified() ? getTitle() : "* " + getTitle();
1587         if (getParentTab() != null) {
1588             getParentTab().setName(tabTitle);
1589             getParentTab().draw();
1590         }
1591     }
1592
1593     public String[] buildEntireContext() {
1594         return getContext();
1595     }
1596     
1597     public void setContext(String[] context) {
1598         this.context = context;
1599         updateCellsContext();
1600     }
1601
1602     public String[] getContext() {
1603         return context;
1604     }
1605
1606         public void updateCellsContext() {
1607                 Object rootParent = getDefaultParent();
1608                 int child_counts = getModel().getChildCount(rootParent);
1609                 for (int i = 0; i < child_counts; ++i) {
1610                         Object obj = getModel().getChildAt(rootParent, i);
1611                         if (obj instanceof ContextUpdate) {
1612                                 String[] context = buildEntireContext();
1613
1614                                 /* Determine if the context is not empty */
1615                                 int nbOfDetectedChar = 0;
1616                                 for (int j = 0; j < context.length; j++) {
1617                                         context[j] = context[j].replaceFirst("\\s", "");
1618                                         nbOfDetectedChar += context[j].length();
1619                                         if (nbOfDetectedChar != 0)
1620                                                 break;
1621                                 }
1622
1623                                 if (nbOfDetectedChar != 0) {
1624                                         ((ContextUpdate) obj).onContextChange(context);
1625                                 }
1626
1627                         } else if (obj instanceof SuperBlock) {
1628                                 SuperBlock superBlock = (SuperBlock) obj;
1629                                 if (superBlock.getChild() != null) {
1630                                         superBlock.getChild().updateCellsContext();
1631                                 }
1632                         }
1633                 }
1634         }
1635
1636     public String getVersion() {
1637         return version;
1638     }
1639     
1640     public int getDebugLevel() {
1641         return debugLevel;
1642     }
1643     
1644     public void setDebugLevel(int debugLevel) {
1645         this.debugLevel = debugLevel;
1646     }
1647
1648     /**
1649      * Open a Diagram :
1650      * If current Diagram is empty, open within it
1651      * else open a new window.
1652      * 
1653      * @param diagramm
1654      */
1655     public void openDiagram(HashMap<String, Object> diagramm) {
1656         if (diagramm != null) {
1657             if (getModel().getChildCount(getDefaultParent()) == 0) {
1658                 loadDiagram(diagramm);
1659             } else {
1660                 XcosDiagram xcosDiagram = Xcos.createANotShownDiagram();
1661                 xcosDiagram.loadDiagram(diagramm);
1662                 setChildrenParentDiagram(xcosDiagram);
1663                 XcosTab.showTabFromDiagram(xcosDiagram);
1664             }
1665         } else {
1666             XcosDialogs.couldNotLoadFile(this);
1667         }
1668     }
1669
1670     /**
1671      * Load a Diagramm structure into current window.
1672      * 
1673      * @param diagramm
1674      */
1675     public void loadDiagram(HashMap<String, Object> diagramm) {
1676         List<BasicBlock> allBlocks = (List<BasicBlock>) diagramm.get("Blocks");
1677         List<TextBlock> allTextBlocks = (List<TextBlock>) diagramm.get("TextBlocks");
1678         HashMap<String, Object> allLinks = (HashMap<String, Object>) diagramm.get("Links");
1679         HashMap<String, Object> properties = (HashMap<String, Object>) diagramm.get("Properties");
1680
1681         setFinalIntegrationTime((Double) properties.get("finalIntegrationTime"));
1682         setIntegratorAbsoluteTolerance((Double) properties.get("integratorAbsoluteTolerance"));
1683         setIntegratorRelativeTolerance((Double) properties.get("integratorRelativeTolerance"));
1684         setToleranceOnTime((Double) properties.get("toleranceOnTime"));
1685         setMaxIntegrationTimeinterval((Double) properties.get("maxIntegrationTimeinterval"));
1686         setRealTimeScaling((Double) properties.get("realTimeScaling"));
1687         setSolver((Double) properties.get("solver"));
1688         setMaximumStepSize((Double) properties.get("maximumStepSize"));
1689         setContext((String[]) properties.get("context"));
1690
1691         List<BasicPort[]> linkPorts = (List<BasicPort[]>) allLinks.get("Ports");
1692         List<double[][]> linkPoints = (List<double[][]>) allLinks.get("Points");
1693
1694         Object[] objs = new Object[allBlocks.size() + linkPorts.size() + allTextBlocks.size()];
1695         getModel().beginUpdate();
1696         for (int i = 0; i < allBlocks.size(); ++i) {
1697                 objs[i] = allBlocks.get(i);
1698         }
1699
1700         for (int i = 0; i < linkPorts.size(); ++i) {
1701             BasicLink link = BasicLink.createLinkFromPorts(linkPorts.get(i)[0], linkPorts.get(i)[1]);
1702             link.setGeometry(new mxGeometry(0,0,80,80));
1703             link.setSource(linkPorts.get(i)[0]);
1704             link.setTarget(linkPorts.get(i)[1]);
1705             double[][] points = linkPoints.get(i);
1706
1707             if (points != null) {
1708                 for (int point = 0; point < points.length; point++) {
1709                     link.addPoint(points[point][0], points[point][1]);
1710                 }
1711             }
1712             objs[i + allBlocks.size()] = link;
1713         }
1714         
1715         for (int i = 0; i < allTextBlocks.size(); ++i) {
1716                 objs[i + allBlocks.size() + linkPorts.size() ] = allTextBlocks.get(i);
1717         }
1718         
1719         addCells(objs);
1720         getModel().endUpdate();
1721
1722         //this.setTitle(fileToLoad);
1723         //this.getParentTab().setName(fileToLoad);
1724
1725         setTitle((String) properties.get("title"));
1726         //getParentTab().setName((String) properties.get("title"));
1727
1728         // Clear all undo events in Undo Manager
1729         getUndoManager().clear();
1730         setModified(false);
1731     }
1732
1733     /**
1734      * Read a diagram from an HDF5 file (ask for creation if the file does not exist) 
1735      * @param diagramFileName file to open
1736      */
1737     public void openDiagramFromFile(String diagramFileName) {
1738         if (XcosTab.focusOnExistingFile(diagramFileName) == false) {
1739             File theFile = new File(diagramFileName);
1740             info(XcosMessages.LOADING_DIAGRAM);
1741             
1742             if (getParentTab() != null)
1743                 ((XcosTab) getParentTab()).setActionsEnabled(false);
1744
1745             if (theFile.exists()) {
1746                 transformAndLoadFile(theFile, false);
1747             } else {
1748                 AnswerOption answer = ScilabModalDialog.show(getParentTab(), String.format(
1749                         XcosMessages.FILE_DOESNT_EXIST, theFile.getAbsolutePath()),
1750                         XcosMessages.XCOS, IconType.QUESTION_ICON,
1751                         ButtonType.YES_NO);
1752
1753                 if (answer == AnswerOption.YES_OPTION) {
1754                     try {
1755                         FileWriter writer = new FileWriter(diagramFileName);
1756                         writer.write("");
1757                         writer.flush();
1758                         writer.close();
1759                         setSavedFile(diagramFileName);
1760                         setTitle(theFile.getName().substring(0,
1761                                 theFile.getName().lastIndexOf('.')));
1762                     } catch (IOException ioexc) {
1763                         JOptionPane.showMessageDialog(this.getAsComponent(), ioexc);
1764                     }
1765                 }
1766             }
1767             info(XcosMessages.EMPTY_INFO);
1768             if (getParentTab() != null)
1769                 ((XcosTab) getParentTab()).setActionsEnabled(true);
1770             this.resetUndoManager();
1771         }
1772     }
1773     
1774
1775     /**
1776      * Load a file with different method depending on it extension 
1777      * @param theFile File to load
1778      */
1779     protected boolean transformAndLoadFile(File theFile, boolean wait) {
1780         final File fileToLoad = theFile;
1781         final XcosFileType filetype = XcosFileType.findFileType(fileToLoad);
1782         boolean result = false;
1783
1784         switch (filetype) {
1785         case COSF:
1786         case COS:
1787             if(wait) {
1788                 File newFile;
1789                 newFile = filetype.exportToHdf5(fileToLoad);
1790                 result = transformAndLoadFile(newFile, wait);
1791             } else {
1792                 Thread transformAction = new Thread() {
1793                     public void run() {
1794                         File newFile;
1795                         newFile = filetype.exportToHdf5(fileToLoad);
1796                         transformAndLoadFile(newFile, false);
1797                     }
1798                 };
1799                 transformAction.start();
1800                 result = true;
1801             }
1802             break;
1803
1804         case XCOS:
1805             Document document = loadXcosDocument(theFile.getAbsolutePath());
1806             if(document == null) {
1807                 XcosDialogs.couldNotLoadFile(this);
1808                 return false;
1809             }
1810
1811             XcosCodec codec = new XcosCodec(document);
1812
1813             if (getModel().getChildCount(getDefaultParent()) == 0) {
1814                 codec.decode(document.getDocumentElement(), this);
1815                 setModified(false);
1816                 setSavedFile(theFile.getAbsolutePath());
1817                 setTitle(theFile.getName().substring(0, theFile.getName().lastIndexOf('.')));
1818                 setChildrenParentDiagram();
1819                 generateUID();
1820             } else {
1821                 XcosDiagram xcosDiagram = Xcos.createANotShownDiagram();
1822                 xcosDiagram.info(XcosMessages.LOADING_DIAGRAM);
1823                 codec.decode(document.getDocumentElement(), xcosDiagram);
1824                 xcosDiagram.setModified(false);
1825                 xcosDiagram.setSavedFile(theFile.getAbsolutePath());
1826                 xcosDiagram.setTitle(theFile.getName().substring(0,     theFile.getName().lastIndexOf('.')));
1827                 setChildrenParentDiagram(xcosDiagram);
1828                 XcosTab.showTabFromDiagram(xcosDiagram);
1829                 xcosDiagram.generateUID();
1830                 xcosDiagram.info(XcosMessages.EMPTY_INFO);
1831             }
1832             
1833             result = true;
1834             break;
1835
1836         case HDF5:
1837             openDiagram(BlockReader.readDiagramFromFile(fileToLoad.getAbsolutePath()));
1838             generateUID();
1839             setModified(false);
1840             result = true;
1841             break;
1842
1843         default:
1844             XcosDialogs.couldNotLoadFile(this);
1845             break;
1846         }
1847         return result;
1848     }
1849
1850     static Document loadXcosDocument(String xcosFile) {
1851         DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
1852         DocumentBuilder docBuilder;
1853         try {
1854             docBuilder = docBuilderFactory.newDocumentBuilder();
1855             return docBuilder.parse(xcosFile);
1856         } catch (ParserConfigurationException e) {
1857             return null;
1858         } catch (SAXException e) {
1859             return null;
1860         } catch (IOException e) {
1861             return null;
1862         }
1863     }
1864     
1865     public void generateUID() {
1866         for (int i = 0; i < getModel().getChildCount(getDefaultParent()); ++i) {
1867             if (getModel().getChildAt(getDefaultParent(), i) instanceof BasicBlock) {
1868                 BasicBlock block = (BasicBlock)getModel().getChildAt(getDefaultParent(), i);
1869                 if(block.getRealParameters() instanceof ScilabMList) {
1870                     //we have a hidden SuperBlock, create a real one
1871                     SuperBlock newSP = (SuperBlock)BlockFactory.createBlock("SUPER_f");
1872                     newSP.setRealParameters(block.getRealParameters());
1873                     newSP.createChildDiagram(true);
1874                     newSP.setParentDiagram(this);
1875                     block.setRealParameters(BlockWriter.convertDiagramToMList(newSP.getChild()));
1876                 } else if(block.getId() == null || block.getId().compareTo("") == 0) {
1877                     block.setId();
1878                 }
1879             }
1880         }
1881     }
1882
1883     /**
1884      * Update all the children of the current graph.
1885      */
1886     public void setChildrenParentDiagram() {
1887         setChildrenParentDiagram(this);
1888     }
1889
1890     /**
1891      * For each block in the argument, call its setParentDiagram method
1892      * @param diagram The new parent of the blocks.
1893      */
1894     private void setChildrenParentDiagram(XcosDiagram diagram) {
1895         for (int i = 0; i < diagram.getModel().getChildCount(diagram.getDefaultParent()); i++) {
1896             mxCell cell = (mxCell) diagram.getModel().getChildAt(diagram.getDefaultParent(), i);
1897             if (cell instanceof BasicBlock) {
1898                 BasicBlock block = (BasicBlock) cell;
1899                 block.setParentDiagram(diagram);
1900                 if (block instanceof AfficheBlock) {
1901                     AfficheBlock affich = (AfficheBlock) block;
1902                     XcosTab.getAfficheBlocks().put(affich.getHashCode(), affich);
1903                 }
1904             }
1905         }
1906     }
1907
1908     /**
1909      * Getting the root diagram of a decomposed diagram
1910      * @return Root parent of the whole parent
1911      */
1912     public XcosDiagram getRootDiagram() {
1913         XcosDiagram rootGraph = this;
1914         while (rootGraph instanceof SuperBlockDiagram) {
1915             rootGraph = ((SuperBlockDiagram) rootGraph).getContainer().getParentDiagram();
1916         }
1917         return rootGraph;
1918     }
1919
1920     /**
1921      * Returns the tooltip to be used for the given cell.
1922      */
1923     public String getToolTipForCell(Object cell)
1924     {
1925         if (cell instanceof BasicBlock) {
1926             return ((BasicBlock) cell).getToolTipText();
1927         } else if(cell instanceof BasicPort) {
1928             return ((BasicPort) cell).getToolTipText();
1929         }
1930         return "";
1931     }
1932
1933     /**
1934      * Set any text to an Afficheblock specified by its ID.
1935      * @param blockID ID of the AfficheBlock to be modified.
1936      * @param blockValue Content to be apply to the block.
1937      * @param iRows Number of Row in the blockValue.
1938      * @param iCols Number of Collumns in the blockValue.
1939      */
1940     public static void setBlockTextValue(int blockID, String[] blockValue, int iRows, int iCols) {
1941
1942         AfficheBlock block = XcosTab.getAfficheBlocks().get(blockID);
1943         if (block == null) {
1944             return;
1945         }
1946
1947         String blockResult = "";
1948         for (int i = 0; i < iRows; i++) {
1949             for (int j = 0; j < iCols; j++) {
1950                 if (iCols != 0) {
1951                     blockResult += "  ";
1952                 }
1953                 blockResult += blockValue[j * iRows + i];
1954             }
1955             blockResult += System.getProperty("line.separator");
1956         }
1957
1958         block.setValue(blockResult);
1959         block.getParentDiagram().refresh();
1960     }
1961
1962
1963     /**
1964      * Display the message in info bar.
1965      * @param message Informations
1966      */
1967     public void info(String message) {
1968         final String localMessage = message;
1969         if (getParentTab() != null && getParentTab().getInfoBar() != null) {
1970             getParentTab().getInfoBar().setText(localMessage);
1971         }
1972     }
1973
1974     /**
1975      * Display the message into an error popup
1976      * @param message Error of the message
1977      */
1978     public void error(String message) {
1979         JOptionPane.showMessageDialog(getAsComponent(), message, XcosMessages.XCOS, JOptionPane.ERROR_MESSAGE);
1980     }
1981
1982     /**
1983      * Find the block corresponding to the given uid
1984      * and display a warning message.
1985      * 
1986      * @param uid - A String as UID.
1987      * @param message - The message to display.
1988      */
1989     public void warnCellByUID(String uid, String message) {
1990         for (int i = 0; i < getModel().getChildCount(getDefaultParent()); ++i) {
1991             if (getModel().getChildAt(getDefaultParent(), i) instanceof mxCell) {
1992                 if (((mxCell) getModel().getChildAt(getDefaultParent(), i)).getId().compareTo(uid) == 0) {
1993                     //to put on top, only for new message, no for reset
1994                     if(message.compareTo("") != 0) {
1995                         setVisible(true);
1996                     }
1997
1998                     getAsComponent().setCellWarning(getModel().getChildAt(getDefaultParent(), i), message);
1999                 }
2000             }
2001         }
2002     }
2003
2004     /**
2005      * Set the current diagram in a modified state
2006      * @param modified True or False whether the current diagram must be saved or not. 
2007      */
2008     public void setModified(boolean modified) {
2009         super.setModified(modified);
2010         updateTabTitle();
2011     }
2012
2013     /**
2014      * Revert an action
2015      */
2016     public void undo() {
2017         super.undo();
2018
2019         if (getParentTab() != null) {
2020             if (getUndoManager().canUndo()) {
2021                 ((XcosTab) getParentTab()).setEnabledUndo(true);
2022             } else {
2023                 ((XcosTab) getParentTab()).setEnabledUndo(false);
2024             }
2025             ((XcosTab) getParentTab()).setEnabledRedo(true);
2026         }
2027
2028         updateUndoModifiedState();
2029         /*
2030          * if (undoManager.canRedo()){
2031          * ((Xcos)getParentTab()).setEnabledRedo(true); } else {
2032          * ((Xcos)getParentTab()).setEnabledRedo(false); }
2033          */
2034         
2035         if(waitPathAddEdge) {
2036             if(drawLink != null) {
2037                 getModel().remove(drawLink);
2038             }
2039             cancelDrawLinkAction();
2040         }
2041     }
2042
2043
2044
2045     /**
2046      * Apply the previously reverted action
2047      */
2048     public void redo() {
2049         super.redo();
2050
2051         updateUndoModifiedState();
2052
2053         if (getParentTab() != null) {
2054             if (getUndoManager().canUndo()) {
2055                 ((XcosTab) getParentTab()).setEnabledUndo(true);
2056             } else {
2057                 ((XcosTab) getParentTab()).setEnabledUndo(false);
2058             }
2059             if (getUndoManager().canRedo()) {
2060                 ((XcosTab) getParentTab()).setEnabledRedo(true);
2061             } else {
2062                 ((XcosTab) getParentTab()).setEnabledRedo(false);
2063             }
2064         }
2065     }
2066
2067     /**
2068      * This function will reset the UndoManager in a stable state.
2069      */
2070     public void resetUndoManager() {
2071         getUndoManager().clear();
2072
2073         resetUndoCounter();
2074
2075         if (getParentTab() != null) {
2076             ((XcosTab) getParentTab()).setEnabledRedo(false);
2077             ((XcosTab) getParentTab()).setEnabledUndo(false);
2078         }
2079     }
2080
2081     private void updateUndoModifiedState() {
2082         if (isZeroUndoCounter()) {
2083             setModified(false);
2084         }
2085         else
2086         {
2087             setModified(true);
2088         }
2089     }
2090
2091     public void setContextAction(SetContextAction action) {
2092         this.action = action;
2093     }
2094
2095    public SetContextAction getContextAction() {
2096         return action;
2097     }
2098
2099     public BasicBlock getChildById(String uid) {
2100         BasicBlock returnBlock = null;
2101         for (int i = 0; i < getModel().getChildCount(getDefaultParent()); ++i) {
2102             if (getModel().getChildAt(getDefaultParent(), i) instanceof BasicBlock) {
2103                 BasicBlock block = (BasicBlock)getModel().getChildAt(getDefaultParent(), i);
2104                 if (block.getId().compareTo(uid) == 0) { //find it
2105                     returnBlock = block;
2106                 } else {
2107                     if(block instanceof SuperBlock) {
2108                         boolean created = false;
2109                         if(((SuperBlock)block).getChild() == null) { 
2110                             //create temporary SuperBlock to find child
2111                             ((SuperBlock)block).createChildDiagram();
2112                             created = true;
2113                         }
2114
2115                         //search in child
2116                         returnBlock = ((SuperBlock)block).getChild().getChildById(uid);
2117
2118                         if(created) { //if temporary, destroy it
2119                             ((SuperBlock)block).getChild().closeDiagram();
2120                         }
2121                     } else if(block.getRealParameters() instanceof ScilabMList) { 
2122                         //we have a hidden SuperBlock, create a real one
2123                         SuperBlock newSP = (SuperBlock)BlockFactory.createBlock("SUPER_f");
2124                         newSP.setParentDiagram(block.getParentDiagram());
2125                         newSP.setRealParameters(block.getRealParameters());
2126                         newSP.createChildDiagram();
2127                         //search in child
2128                         returnBlock = newSP.getChild().getChildById(uid);
2129                         newSP.getChild().closeDiagram();
2130                         newSP = null;
2131                     }
2132                 }
2133             }
2134
2135             if(returnBlock != null) {
2136                 return returnBlock;
2137             }
2138         }
2139         return returnBlock;
2140     }
2141
2142     public boolean isChildVisible() {
2143         for (int i = 0; i < getModel().getChildCount(getDefaultParent()); i++) {
2144             Object child = getModel().getChildAt(getDefaultParent(), i);
2145             if (child instanceof SuperBlock) {
2146                 XcosDiagram diag = ((SuperBlock) child).getChild();
2147                 if (diag != null && diag.isOpened()) {
2148                     // if child or sub child is visible
2149                     if (diag.isChildVisible() || diag.isVisible()) {
2150                         return true;
2151                     }
2152                 }
2153             }
2154         }
2155         return false;
2156     }
2157
2158     public boolean canClose() {
2159         if (isChildVisible() == false) {
2160             return true;
2161         }
2162         return false;
2163     }
2164
2165     public void closeChildren() {
2166         for (int i = 0; i < getModel().getChildCount(getDefaultParent()); i++) {
2167             Object child = getModel().getChildAt(getDefaultParent(), i);
2168             if (child instanceof SuperBlock) {
2169                 SuperBlock diag = (SuperBlock) child;
2170
2171                 if (diag.getChild() != null && diag.getChild().isOpened()) {
2172                     diag.closeBlockSettings();
2173                 }
2174             }
2175         }
2176     }
2177     
2178     private StringBuffer checkMultiplicities(Object edge, Object source, Object target) {
2179         mxMultiplicity multi[] = getMultiplicities();
2180         StringBuffer error = new StringBuffer();
2181         for(mxMultiplicity current : multi) {
2182             if(current instanceof PortCheck) {
2183                 int sourceOut = mxGraphModel.getDirectedEdgeCount(getModel(), source, true);
2184                 int targetIn = mxGraphModel.getDirectedEdgeCount(getModel(), target, false);
2185                 
2186                 String str = ((PortCheck)current).checkDrawLink(this, edge, source, target, sourceOut, targetIn);
2187                 if(str != null) {
2188                     error.append(str);
2189                 }
2190             }
2191         }
2192         
2193         if(error.length() > 0) {
2194             return error;
2195         }
2196         return null;
2197     }
2198
2199     private boolean checkEdgeDirection(Object source, Object target) {
2200         
2201         if(source instanceof InputPort && target instanceof OutputPort) {
2202             Object temp = source;
2203             source = target;
2204             target = temp;
2205             return false;
2206         }
2207
2208         if(source instanceof ControlPort && target instanceof CommandPort) {
2209             Object temp = source;
2210             source = target;
2211             target = temp;
2212             return false;
2213         }
2214
2215         if((source instanceof InputPort || source instanceof ControlPort) &&  target instanceof BasicLink) {
2216             Object temp = source;
2217             source = target;
2218             target = temp;
2219             return false;
2220         }
2221         
2222         
2223         return true;
2224     }
2225     
2226     private void cancelDrawLinkAction() {
2227         waitPathAddEdge = false;
2228         waitPathRelease = false;
2229         drawLink = null;
2230         info(XcosMessages.EMPTY_INFO);
2231     }
2232     
2233 }
2234