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