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