2 * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3 * Copyright (C) 2009-2009 - DIGITEO - Bruno JOFRET
4 * Copyright (C) 2009-2010 - DIGITEO - Clement DAVID
6 * This file must be used under the terms of the CeCILL.
7 * This source file is licensed as described in the file COPYING, which
8 * you should have received as part of this distribution. The terms
9 * are also available at
10 * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt
14 package org.scilab.modules.xcos.graph;
16 import java.awt.GraphicsEnvironment;
17 import java.awt.event.ActionEvent;
18 import java.awt.event.ActionListener;
19 import java.beans.PropertyChangeEvent;
20 import java.beans.PropertyChangeListener;
21 import java.beans.PropertyVetoException;
23 import java.io.IOException;
24 import java.rmi.server.UID;
25 import java.util.ArrayList;
26 import java.util.Arrays;
27 import java.util.Collection;
28 import java.util.Collections;
29 import java.util.Comparator;
30 import java.util.HashSet;
31 import java.util.Hashtable;
32 import java.util.IllegalFormatException;
33 import java.util.LinkedList;
34 import java.util.List;
36 import java.util.Queue;
38 import java.util.logging.Logger;
40 import javax.swing.JFileChooser;
41 import javax.swing.JOptionPane;
42 import javax.swing.SwingWorker;
43 import javax.swing.Timer;
45 import org.scilab.modules.action_binding.highlevel.ScilabInterpreterManagement;
46 import org.scilab.modules.action_binding.highlevel.ScilabInterpreterManagement.InterpreterException;
47 import org.scilab.modules.graph.ScilabGraph;
48 import org.scilab.modules.graph.utils.ScilabGraphConstants;
49 import org.scilab.modules.gui.bridge.filechooser.SwingScilabFileChooser;
50 import org.scilab.modules.gui.bridge.tab.SwingScilabTab;
51 import org.scilab.modules.gui.messagebox.ScilabModalDialog;
52 import org.scilab.modules.gui.messagebox.ScilabModalDialog.AnswerOption;
53 import org.scilab.modules.gui.messagebox.ScilabModalDialog.ButtonType;
54 import org.scilab.modules.gui.messagebox.ScilabModalDialog.IconType;
55 import org.scilab.modules.gui.tabfactory.ScilabTabFactory;
56 import org.scilab.modules.types.ScilabDouble;
57 import org.scilab.modules.types.ScilabMList;
58 import org.scilab.modules.types.ScilabString;
59 import org.scilab.modules.xcos.Xcos;
60 import org.scilab.modules.xcos.XcosTab;
61 import org.scilab.modules.xcos.actions.SaveAsAction;
62 import org.scilab.modules.xcos.block.AfficheBlock;
63 import org.scilab.modules.xcos.block.BasicBlock;
64 import org.scilab.modules.xcos.block.BlockFactory;
65 import org.scilab.modules.xcos.block.BlockFactory.BlockInterFunction;
66 import org.scilab.modules.xcos.block.SplitBlock;
67 import org.scilab.modules.xcos.block.SuperBlock;
68 import org.scilab.modules.xcos.block.TextBlock;
69 import org.scilab.modules.xcos.block.io.ContextUpdate;
70 import org.scilab.modules.xcos.block.io.EventInBlock;
71 import org.scilab.modules.xcos.block.io.EventOutBlock;
72 import org.scilab.modules.xcos.block.io.ExplicitInBlock;
73 import org.scilab.modules.xcos.block.io.ExplicitOutBlock;
74 import org.scilab.modules.xcos.block.io.ImplicitInBlock;
75 import org.scilab.modules.xcos.block.io.ImplicitOutBlock;
76 import org.scilab.modules.xcos.configuration.ConfigurationManager;
77 import org.scilab.modules.xcos.graph.swing.GraphComponent;
78 import org.scilab.modules.xcos.io.XcosFileType;
79 import org.scilab.modules.xcos.io.scicos.ScilabDirectHandler;
80 import org.scilab.modules.xcos.link.BasicLink;
81 import org.scilab.modules.xcos.link.commandcontrol.CommandControlLink;
82 import org.scilab.modules.xcos.link.explicit.ExplicitLink;
83 import org.scilab.modules.xcos.link.implicit.ImplicitLink;
84 import org.scilab.modules.xcos.port.BasicPort;
85 import org.scilab.modules.xcos.port.BasicPort.Type;
86 import org.scilab.modules.xcos.port.Orientation;
87 import org.scilab.modules.xcos.port.PortCheck;
88 import org.scilab.modules.xcos.port.command.CommandPort;
89 import org.scilab.modules.xcos.port.control.ControlPort;
90 import org.scilab.modules.xcos.port.input.ExplicitInputPort;
91 import org.scilab.modules.xcos.port.input.ImplicitInputPort;
92 import org.scilab.modules.xcos.port.output.ExplicitOutputPort;
93 import org.scilab.modules.xcos.port.output.ImplicitOutputPort;
94 import org.scilab.modules.xcos.preferences.XcosOptions;
95 import org.scilab.modules.xcos.utils.BlockPositioning;
96 import org.scilab.modules.xcos.utils.XcosConstants;
97 import org.scilab.modules.xcos.utils.XcosDialogs;
98 import org.scilab.modules.xcos.utils.XcosEvent;
99 import org.scilab.modules.xcos.utils.XcosMessages;
101 import com.mxgraph.model.mxCell;
102 import com.mxgraph.model.mxGeometry;
103 import com.mxgraph.model.mxGraphModel;
104 import com.mxgraph.model.mxGraphModel.Filter;
105 import com.mxgraph.model.mxGraphModel.mxChildChange;
106 import com.mxgraph.model.mxGraphModel.mxStyleChange;
107 import com.mxgraph.model.mxICell;
108 import com.mxgraph.model.mxIGraphModel;
109 import com.mxgraph.model.mxIGraphModel.mxAtomicGraphModelChange;
110 import com.mxgraph.util.mxEvent;
111 import com.mxgraph.util.mxEventObject;
112 import com.mxgraph.util.mxPoint;
113 import com.mxgraph.util.mxRectangle;
114 import com.mxgraph.util.mxUndoableEdit;
115 import com.mxgraph.util.mxUndoableEdit.mxUndoableChange;
116 import com.mxgraph.util.mxUtils;
117 import com.mxgraph.view.mxGraphSelectionModel;
118 import com.mxgraph.view.mxMultiplicity;
119 import com.mxgraph.view.mxStylesheet;
122 * The base class for a diagram. This class contains jgraphx + Scicos data.
124 public class XcosDiagram extends ScilabGraph {
125 private static final Logger LOG = Logger.getLogger(XcosDiagram.class.getName());
127 private static final String MODIFIED = "modified";
128 private static final String CELLS = "cells";
129 public static final String IN = "in";
130 public static final String OUT = "out";
131 public static final String EIN = "ein";
132 public static final String EOUT = "eout";
135 * Prefix used to tag text node.
137 public static final String HASH_IDENTIFIER = "#identifier";
140 * Default geometry used while adding a label to a block (on the middle and
141 * below the bottom of the parent block)
143 private static final mxGeometry DEFAULT_LABEL_GEOMETRY = new mxGeometry(0.5, 1.1, 0.0, 0.0);
149 // the associated parameters
150 private ScicosParameters scicosParameters;
152 // the scicos engine current status
153 private final transient CompilationEngineStatus engine;
156 * Default constructor for a visible diagram
158 public XcosDiagram() {
165 * @param withVisibleFeatures true if the visible features should be activated, false otherwise. Disable it on encode/decode leads to a huge performance gain.
167 public XcosDiagram(final boolean withVisibleFeatures) {
170 // Scicos related setup
171 engine = new CompilationEngineStatus();
172 setScicosParameters(new ScicosParameters());
174 if (withVisibleFeatures) {
175 // Add a default listener to update the modification status when
176 // something has changed on the ScicosParameters
177 scicosParameters.addPropertyChangeListener(new PropertyChangeListener() {
179 public void propertyChange(final PropertyChangeEvent evt) {
184 setComponent(new GraphComponent(this));
188 // Forbid disconnecting cells once it is connected.
189 setCellsDisconnectable(false);
191 // Forbid pending edges.
192 setAllowDanglingEdges(false);
194 // Cannot connect port to itself.
195 setAllowLoops(false);
197 // Override isCellResizable to filter what the user can resize
198 setCellsResizable(true);
200 /* Labels use HTML if not equal to interface function name */
203 * by default every label is movable, see
204 * XcosDiagram##isLabelMovable(java.lang.Object) for restrictions
206 setVertexLabelsMovable(true);
207 setEdgeLabelsMovable(true);
210 setCloneInvalidEdges(true);
212 // Override isCellEditable to filter what the user can edit
213 setCellsEditable(true);
215 setConnectableEdges(true);
217 // Do not clear edge points on connect
218 setResetEdgesOnConnect(false);
224 // Add a listener to track when model is changed
225 getModel().addListener(mxEvent.CHANGE, ModelTracker.getInstance());
228 ((mxCell) getDefaultParent()).setId((new UID()).toString());
229 ((mxCell) getModel().getRoot()).setId((new UID()).toString());
237 * Only return the instanceof klass
240 * the selection to filter out
243 * @return the selection with only klass instance.
245 public static Object[] filterByClass(final Object[] selection, final Class<BasicBlock> klass) {
246 return mxGraphModel.filterCells(selection, new mxGraphModel.Filter() {
248 public boolean filter(Object cell) {
249 return klass.isInstance(cell);
255 * Sort the blocks per first integer parameter value
259 * @return the sorted block list (same instance)
261 public List <? extends BasicBlock > iparSort(final List <? extends BasicBlock > blocks) {
262 Collections.sort(blocks, new Comparator<BasicBlock>() {
264 public int compare(BasicBlock o1, BasicBlock o2) {
265 final ScilabDouble data1 = (ScilabDouble) o1.getIntegerParameters();
266 final ScilabDouble data2 = (ScilabDouble) o2.getIntegerParameters();
271 if (data1.getWidth() >= 1 && data1.getHeight() >= 1) {
272 value1 = (int) data1.getRealPart()[0][0];
274 if (data2.getWidth() >= 1 && data2.getHeight() >= 1) {
275 value2 = (int) data2.getRealPart()[0][0];
278 return value1 - value2;
286 * The type to work on
288 * the class instance to work on
289 * @return list of typed block
291 @SuppressWarnings("unchecked")
292 private <T extends BasicBlock> List<T> getAllTypedBlock(Class<T> klass) {
293 final List<T> list = new ArrayList<T>();
295 int blockCount = getModel().getChildCount(getDefaultParent());
297 for (int i = 0; i < blockCount; i++) {
298 Object cell = getModel().getChildAt(getDefaultParent(), i);
299 if (klass.isInstance(cell)) {
300 // According to the test we are sure that the cell is an
301 // instance of T. Thus we can safely cast it.
311 * The type to work on
313 * the class instance list to work on
314 * @return list of typed block
316 private <T extends BasicBlock> List<T> getAllTypedBlock(Class<T>[] klasses) {
317 final List<T> list = new ArrayList<T>();
318 for (Class<T> klass : klasses) {
319 list.addAll(getAllTypedBlock(klass));
325 * Fill the context with I/O port
328 * the context to fill
330 @SuppressWarnings("unchecked")
331 protected void fillContext(final Hashtable<Object, Object> context) {
332 if (!context.containsKey(IN)) {
333 context.put(IN, iparSort(getAllTypedBlock( new Class [] { ExplicitInBlock.class, ImplicitInBlock.class })));
335 if (!context.containsKey(OUT)) {
336 context.put(OUT, iparSort(getAllTypedBlock(new Class[] { ExplicitOutBlock.class, ImplicitOutBlock.class })));
338 if (!context.containsKey(EIN)) {
339 context.put(EIN, iparSort(getAllTypedBlock(new Class[] { EventInBlock.class })));
341 if (!context.containsKey(EOUT)) {
342 context.put(EOUT, iparSort(getAllTypedBlock(new Class[] { EventOutBlock.class })));
347 * Function to update IO block numbering
349 * @param ioBlockClass
351 @SuppressWarnings("unchecked")
352 private void updateIOBlockByType(BasicBlock block, Hashtable<Object, Object> context, String type) {
353 List <ContextUpdate> listOfBlocks = (List <ContextUpdate>) context.get(type);
354 if (listOfBlocks.contains(block)) {
357 /* Get an empty index :
358 * The list should always have a size greater of equal to one
359 * since new added element is always added to the list
361 if (listOfBlocks.size() > 1) {
362 // if a hole exists, then assign a port from this hole
363 for (int i = 0; i < listOfBlocks.size() - 1; i++) {
364 int indexNext = (int) ((ScilabDouble) listOfBlocks.get(i + 1).getIntegerParameters()).getRealPart()[0][0];
365 int indexPrevious = (int) ((ScilabDouble) listOfBlocks.get(i).getIntegerParameters()).getRealPart()[0][0];
366 if (indexNext - indexPrevious > 1) {
367 newIndex = indexPrevious + 1;
371 // if no hole is present, then detect multiple items
373 for (int i = 0; i < listOfBlocks.size() - 1; i++) {
374 int indexNext = (int) ((ScilabDouble) listOfBlocks.get(i + 1).getIntegerParameters()).getRealPart()[0][0];
375 int indexPrevious = (int) ((ScilabDouble) listOfBlocks.get(i).getIntegerParameters()).getRealPart()[0][0];
376 if (indexNext - indexPrevious == 0) {
377 newIndex = (int) ((ScilabDouble) listOfBlocks.get(listOfBlocks.size() - 1).getIntegerParameters()).getRealPart()[0][0] + 1;
381 // if no multiple items are present, item is already at the right place
383 newIndex = (int) ((ScilabDouble) block.getIntegerParameters()).getRealPart()[0][0];
391 * Update the IO block with this new index
393 block.setIntegerParameters(new ScilabDouble(newIndex));
394 block.setExprs(new ScilabString(Integer.toString(newIndex)));
395 block.setOrdering(newIndex);
400 * If the block is a IO block, update its index to a free index
403 private void updateIOBlocks(BasicBlock block) {
404 Hashtable<Object, Object> context = new Hashtable<Object, Object> ();
406 fillContext(context);
408 updateIOBlockByType(block, context, IN);
409 updateIOBlockByType(block, context, OUT);
410 updateIOBlockByType(block, context, EIN);
411 updateIOBlockByType(block, context, EOUT);
415 * Static diagram listeners
419 * CellAddedTracker Called when mxEvents.CELLS_ADDED is fired.
421 private static final class CellAddedTracker implements mxIEventListener {
423 private static CellAddedTracker instance;
426 * Default constructor
428 private CellAddedTracker() {
432 * @return the instance
434 public static synchronized CellAddedTracker getInstance() {
435 if (instance == null) {
436 instance = new CellAddedTracker();
442 * Update block values on add
445 * the source instance
448 * @see com.mxgraph.util.mxEventSource.mxIEventListener#invoke(java.lang.Object,
449 * com.mxgraph.util.mxEventObject)
452 public void invoke(final Object source, final mxEventObject evt) {
453 final XcosDiagram diagram = (XcosDiagram) source;
454 final Object[] cells = (Object[]) evt.getProperty(CELLS);
456 diagram.getModel().beginUpdate();
458 final Filter filter = new Filter() {
460 public boolean filter(Object cell) {
461 if (cell instanceof BasicBlock) {
462 final BasicBlock blk = (BasicBlock) cell;
464 // Update parent on cell addition
465 blk.setParentDiagram(diagram);
467 // update port numbering
468 diagram.updateIOBlocks(blk);
470 // Fire an identifier update to let the I/O ports update their labels
471 mxCell identifier = diagram.getCellIdentifier(blk);
472 if (identifier != null) {
473 final Object current = diagram.getModel().getValue(identifier);
474 if (current != null) {
475 final String text = mxUtils.getBodyMarkup(current.toString(), false);
476 diagram.fireEvent(new mxEventObject(mxEvent.LABEL_CHANGED, "cell", identifier, "value", text, "parent", blk));
480 diagram.getView().invalidate();
486 for (int i = 0; i < cells.length; ++i) {
487 mxGraphModel.filterDescendants(diagram.getModel(), filter, cells[i]);
490 diagram.getModel().endUpdate();
496 * CellResizedTracker Called when mxEvents.CELLS_RESIZED is fired.
498 private static final class CellResizedTracker implements mxIEventListener {
500 private static CellResizedTracker instance;
505 private CellResizedTracker() {
509 * @return the instance
511 public static CellResizedTracker getInstance() {
512 if (instance == null) {
513 instance = new CellResizedTracker();
519 * Update the cell view
522 * the source instance
525 * @see com.mxgraph.util.mxEventSource.mxIEventListener#invoke(java.lang.Object,
526 * com.mxgraph.util.mxEventObject)
529 public void invoke(final Object source, final mxEventObject evt) {
530 final XcosDiagram diagram = (XcosDiagram) source;
531 final Object[] cells = (Object[]) evt.getProperty(CELLS);
533 diagram.getModel().beginUpdate();
535 for (int i = 0; i < cells.length; ++i) {
536 if (cells[i] instanceof BasicBlock) {
537 BlockPositioning.updateBlockView((BasicBlock) cells[i]);
541 diagram.getModel().endUpdate();
547 * ModelTracker called when mxEvents.CHANGE occurs on a model
549 private static final class ModelTracker implements mxIEventListener {
550 private static ModelTracker instance;
555 private ModelTracker() {
559 * @return the instance
561 public static ModelTracker getInstance() {
562 if (instance == null) {
563 instance = new ModelTracker();
569 * Fire cell value update on any change
572 * the source instance
575 * @see com.mxgraph.util.mxEventSource.mxIEventListener#invoke(java.lang.Object,
576 * com.mxgraph.util.mxEventObject)
579 public void invoke(final Object source, final mxEventObject evt) {
580 final mxGraphModel model = (mxGraphModel) source;
581 @SuppressWarnings("unchecked")
582 final List<mxAtomicGraphModelChange> changes = (List<mxAtomicGraphModelChange>) (evt.getProperty("changes"));
584 final List<Object> objects = new ArrayList<Object>();
588 for (int i = 0; i < changes.size(); ++i) {
589 if (changes.get(i) instanceof mxChildChange) {
590 if (((mxChildChange) changes.get(i)).getChild() instanceof SplitBlock) {
594 if (((mxChildChange) changes.get(i)).getChild() instanceof BasicBlock) {
595 final BasicBlock currentCell = (BasicBlock) ((mxChildChange) changes.get(i)).getChild();
596 objects.add(currentCell);
600 if (!objects.isEmpty()) {
601 final Object[] firedCells = new Object[objects.size()];
602 for (int j = 0; j < objects.size(); ++j) {
603 firedCells[j] = objects.get(j);
606 model.fireEvent(new mxEventObject(XcosEvent.FORCE_CELL_VALUE_UPDATE, CELLS, firedCells));
616 * SuperBlockUpdateTracker Called when adding some port in a SuperBlock
617 * diagram to update current sub-diagram (i.e SuperBlock) representation.
619 private static final class SuperBlockUpdateTracker implements mxIEventListener {
620 private static SuperBlockUpdateTracker instance;
625 private SuperBlockUpdateTracker() {
629 * @return the instance
631 public static SuperBlockUpdateTracker getInstance() {
632 if (instance == null) {
633 instance = new SuperBlockUpdateTracker();
639 * Update the superblock values (rpar) on update
642 * the source instance
645 * @see com.mxgraph.util.mxEventSource.mxIEventListener#invoke(java.lang.Object,
646 * com.mxgraph.util.mxEventObject)
649 public void invoke(final Object source, final mxEventObject evt) {
650 assert evt.getProperty(XcosConstants.EVENT_BLOCK_UPDATED) instanceof SuperBlock;
652 final XcosDiagram diagram = (XcosDiagram) source;
653 final SuperBlock updatedBlock = (SuperBlock) evt.getProperty(XcosConstants.EVENT_BLOCK_UPDATED);
655 assert diagram == updatedBlock.getParentDiagram();
658 * The rpar value is set as invalid, encode the child on demand.
660 updatedBlock.invalidateRpar();
662 if (diagram instanceof SuperBlockDiagram) {
663 final SuperBlock parentBlock = ((SuperBlockDiagram) diagram).getContainer();
664 parentBlock.getParentDiagram().fireEvent(new mxEventObject(XcosEvent.SUPER_BLOCK_UPDATED, XcosConstants.EVENT_BLOCK_UPDATED, parentBlock));
667 BlockPositioning.updateBlockView(updatedBlock);
669 // force super block to refresh
670 diagram.getView().clear(updatedBlock, true, true);
672 // force links connected to super block to refresh
673 final int childCount = diagram.getModel().getChildCount(updatedBlock);
674 for (int i = 0; i < childCount; i++) {
675 final Object port = diagram.getModel().getChildAt(updatedBlock, i);
677 final int edgeCount = diagram.getModel().getEdgeCount(port);
678 for (int j = 0; j < edgeCount; j++) {
679 final Object edge = diagram.getModel().getEdgeAt(port, j);
680 diagram.getView().clear(edge, true, true);
684 diagram.getView().validate();
690 * Update the modified block on undo/redo
692 private static final class UndoUpdateTracker implements mxIEventListener {
693 private static UndoUpdateTracker instance;
698 public UndoUpdateTracker() {
702 * @return the instance
704 public static UndoUpdateTracker getInstance() {
705 if (instance == null) {
706 instance = new UndoUpdateTracker();
712 * Update the block and style on undo
715 * the source instance
718 * @see com.mxgraph.util.mxEventSource.mxIEventListener#invoke(java.lang.Object,
719 * com.mxgraph.util.mxEventObject)
722 public void invoke(final Object source, final mxEventObject evt) {
723 final mxUndoableEdit edit = (mxUndoableEdit) evt.getProperty(ScilabGraphConstants.EVENT_CHANGE_EDIT);
725 final mxGraphModel model = (mxGraphModel) edit.getSource();
726 final List<mxUndoableChange> changes = edit.getChanges();
728 final Object[] changedCells = getSelectionCellsForChanges(changes, model);
731 for (final Object object : changedCells) {
732 if (object instanceof BasicBlock) {
733 final BasicBlock current = (BasicBlock) object;
734 final XcosDiagram graph = current.getParentDiagram();
736 // When we change the style property we have to update
737 // some BasiBlock fields
738 if (changes.get(0) instanceof mxStyleChange) {
739 current.updateFieldsFromStyle();
742 // update the superblock container ports if the block is
743 // inside a superblock diagram
744 if (graph instanceof SuperBlockDiagram) {
745 SuperBlockDiagram superdiagram = (SuperBlockDiagram) current.getParentDiagram();
746 SuperBlock superblock = superdiagram.getContainer();
747 superblock.updateExportedPort();
750 // Update the block position
751 BlockPositioning.updateBlockView(current);
753 // force a refresh of the block ports and links
754 // connected to these ports
755 final int childCount = model.getChildCount(current);
756 for (int i = 0; i < childCount; i++) {
757 final Object port = model.getChildAt(current, i);
758 graph.getView().clear(port, true, true);
759 final int edgeCount = model.getEdgeCount(port);
760 for (int j = 0; j < edgeCount; j++) {
761 final Object edge = model.getEdgeAt(port, j);
762 graph.getView().clear(edge, true, true);
765 // force a refresh of the block
766 graph.getView().clear(current, true, true);
768 graph.getView().validate();
779 * Refresh each block on modification (update port position, etc...)
781 private static final class RefreshBlockTracker implements mxIEventListener {
782 private static RefreshBlockTracker instance;
785 * Default constructor
787 private RefreshBlockTracker() {
791 * @return the instance
793 public static RefreshBlockTracker getInstance() {
794 if (instance == null) {
795 instance = new RefreshBlockTracker();
801 * Refresh the block on port added
807 * @see com.mxgraph.util.mxEventSource.mxIEventListener#invoke(java.lang.Object,
808 * com.mxgraph.util.mxEventObject)
811 public void invoke(Object sender, mxEventObject evt) {
812 final XcosDiagram diagram = (XcosDiagram) sender;
814 diagram.getModel().beginUpdate();
816 final BasicBlock updatedBlock = (BasicBlock) evt.getProperty(XcosConstants.EVENT_BLOCK_UPDATED);
817 BlockPositioning.updateBlockView(updatedBlock);
819 diagram.getView().clear(updatedBlock, true, true);
821 // validate display errors
822 diagram.getAsComponent().clearCellOverlays();
823 diagram.getAsComponent().validateGraph();
825 diagram.getView().validate();
827 diagram.getModel().endUpdate();
833 * Hook method that creates the new edge for insertEdge. This implementation
834 * does not set the source and target of the edge, these are set when the
835 * edge is added to the model.
838 * Cell that specifies the parent of the new edge.
840 * Optional string that defines the Id of the new edge.
842 * Object to be used as the user object.
844 * Cell that defines the source of the edge.
846 * Cell that defines the target of the edge.
848 * Optional string that defines the cell style.
849 * @return Returns the new edge to be inserted.
850 * @see com.mxgraph.view.mxGraph#createEdge(java.lang.Object,
851 * java.lang.String, java.lang.Object, java.lang.Object,
852 * java.lang.Object, java.lang.String)
855 public Object createEdge(Object parent, String id, Object value, Object source, Object target, String style) {
858 if (source instanceof BasicPort) {
859 BasicPort src = (BasicPort) source;
860 BasicLink link = null;
862 if (src.getType() == Type.EXPLICIT) {
863 link = new ExplicitLink();
864 } else if (src.getType() == Type.IMPLICIT) {
865 link = new ImplicitLink();
867 link = new CommandControlLink();
870 // allocate the associated geometry
871 link.setGeometry(new mxGeometry());
873 } else if (source instanceof SplitBlock) {
874 SplitBlock src = (SplitBlock) source;
875 return createEdge(parent, id, value, src.getIn(), target, style);
876 } else if (source instanceof BasicLink) {
877 BasicLink src = (BasicLink) source;
878 BasicLink link = null;
881 link = src.getClass().newInstance();
883 // allocate the associated geometry
884 link.setGeometry(new mxGeometry());
886 } catch (InstantiationException e) {
887 LOG.severe(e.toString());
888 } catch (IllegalAccessException e) {
889 LOG.severe(e.toString());
896 ret = super.createEdge(parent, id, value, source, target, style);
897 LOG.warning("Creating a non typed edge");
904 * Add an edge from a source to the target.
907 * the edge to add (may be null)
909 * the parent of the source and the target
915 * the index of the edge
916 * @return the added edge or null.
917 * @see com.mxgraph.view.mxGraph#addEdge(java.lang.Object, java.lang.Object,
918 * java.lang.Object, java.lang.Object, java.lang.Integer)
921 public Object addCell(Object cell, Object parent, Integer index, Object source, Object target) {
923 // already connected edge or normal block
924 if (source == null && target == null) {
925 return super.addCell(cell, parent, index, source, target);
928 // Command -> Control
929 if (source instanceof CommandPort && target instanceof ControlPort && cell instanceof CommandControlLink) {
930 return super.addCell(cell, parent, index, source, target);
933 // Control -> Command
934 // Switch source and target !
935 if (target instanceof CommandPort && source instanceof ControlPort && cell instanceof CommandControlLink) {
936 BasicLink current = (BasicLink) cell;
937 current.invertDirection();
939 return super.addCell(cell, parent, index, target, source);
942 // ExplicitOutput -> ExplicitInput
943 if (source instanceof ExplicitOutputPort && target instanceof ExplicitInputPort && cell instanceof ExplicitLink) {
944 return super.addCell(cell, parent, index, source, target);
946 // ExplicitInput -> ExplicitOutput
947 // Switch source and target !
948 if (target instanceof ExplicitOutputPort && source instanceof ExplicitInputPort && cell instanceof ExplicitLink) {
949 BasicLink current = (BasicLink) cell;
950 current.invertDirection();
952 return super.addCell(cell, parent, index, target, source);
955 // ImplicitOutput -> ImplicitInput
956 if (source instanceof ImplicitOutputPort && target instanceof ImplicitInputPort && cell instanceof ImplicitLink) {
957 return super.addCell(cell, parent, index, source, target);
959 // ImplicitInput -> ImplicitOutput
960 // Switch source and target !
961 if (target instanceof ImplicitOutputPort && source instanceof ImplicitInputPort && cell instanceof ImplicitLink) {
962 BasicLink current = (BasicLink) cell;
963 current.invertDirection();
965 return super.addCell(cell, parent, index, target, source);
968 // ImplicitInput -> ImplicitInput
969 if (source instanceof ImplicitInputPort && target instanceof ImplicitInputPort && cell instanceof ImplicitLink) {
970 return super.addCell(cell, parent, index, source, target);
972 // ImplicitOutputPort -> ImplicitOutput
973 // Switch source and target !
974 if (target instanceof ImplicitOutputPort && source instanceof ImplicitOutputPort && cell instanceof ImplicitLink) {
975 BasicLink current = (BasicLink) cell;
976 current.invertDirection();
978 return super.addCell(cell, parent, index, target, source);
985 // ExplicitLink -> ExplicitInputPort
986 if (source instanceof ExplicitLink && target instanceof ExplicitInputPort && cell instanceof ExplicitLink) {
987 SplitBlock split = addSplitEdge(((BasicLink) cell).getGeometry().getSourcePoint(), (BasicLink) source);
988 return addCell(cell, parent, index, split.getOut2(), target);
990 // ExplicitOutput -> ExpliciLink
991 // Switch source and target !
992 if (target instanceof ExplicitLink && source instanceof ExplicitInputPort && cell instanceof ExplicitLink) {
993 final BasicLink current = (BasicLink) cell;
994 final SplitBlock split = addSplitEdge(current.getGeometry().getTargetPoint(), (BasicLink) target);
996 current.invertDirection();
998 return addCell(cell, parent, index, split.getOut2(), source);
1001 // ImplicitLink -> ImplicitInputPort
1002 if (source instanceof ImplicitLink && target instanceof ImplicitInputPort && cell instanceof ImplicitLink) {
1003 SplitBlock split = addSplitEdge(((BasicLink) cell).getGeometry().getSourcePoint(), (BasicLink) source);
1004 return addCell(cell, parent, index, split.getOut2(), target);
1006 // ImplicitInputPort -> ImplicitLink
1007 // Switch source and target !
1008 if (target instanceof ImplicitLink && source instanceof ImplicitInputPort && cell instanceof ImplicitLink) {
1009 final BasicLink current = (BasicLink) cell;
1010 final SplitBlock split = addSplitEdge(current.getGeometry().getTargetPoint(), (BasicLink) target);
1012 current.invertDirection();
1014 return addCell(cell, parent, index, split.getOut2(), source);
1017 // ImplicitLink -> ImplicitOutputPort
1018 if (source instanceof ImplicitLink && target instanceof ImplicitOutputPort && cell instanceof ImplicitLink) {
1019 final BasicLink current = (BasicLink) cell;
1020 final SplitBlock split = addSplitEdge(current.getGeometry().getTargetPoint(), (BasicLink) source);
1021 return addCell(cell, parent, index, split.getOut2(), source);
1023 // ImplicitOutputPort -> ImplicitLink
1024 // Switch source and target !
1025 if (target instanceof ImplicitLink && source instanceof ImplicitOutputPort && cell instanceof ImplicitLink) {
1026 final BasicLink current = (BasicLink) cell;
1027 final SplitBlock split = addSplitEdge(current.getGeometry().getTargetPoint(), (ImplicitLink) target);
1028 return addCell(cell, parent, index, split.getOut2(), source);
1031 // CommandControlLink -> ControlPort
1032 if (source instanceof CommandControlLink && target instanceof ControlPort && cell instanceof CommandControlLink) {
1033 SplitBlock split = addSplitEdge(((BasicLink) cell).getGeometry().getSourcePoint(), (BasicLink) source);
1034 return addCell(cell, parent, index, split.getOut2(), target);
1036 // ControlPort -> CommandControlLink
1037 // Switch source and target !
1038 if (target instanceof CommandControlLink && source instanceof ControlPort && cell instanceof CommandControlLink) {
1039 final BasicLink current = (BasicLink) cell;
1040 final SplitBlock split = addSplitEdge(current.getGeometry().getTargetPoint(), (BasicLink) target);
1042 current.invertDirection();
1044 return addCell(cell, parent, index, split.getOut2(), source);
1047 if (cell instanceof BasicLink && source != null && target != null) {
1048 LOG.severe("Unable to add a typed link");
1051 LOG.severe("Adding an untyped edge");
1052 return super.addCell(cell, parent, index, source, target);
1057 * Add a split on a edge.
1060 * the split point (center of the split block)
1063 * @return split block
1065 public SplitBlock addSplitEdge(final mxPoint splitPoint, final BasicLink link) {
1066 final BasicPort linkSource = (BasicPort) link.getSource();
1067 final BasicPort linkTarget = (BasicPort) link.getTarget();
1069 final SplitBlock splitBlock = (SplitBlock) BlockFactory.createBlock(BlockInterFunction.SPLIT_f);
1071 getModel().beginUpdate();
1073 // Origin of the parent, (0,0) as default may be different in case
1074 mxPoint orig = link.getParent().getGeometry();
1076 orig = new mxPoint();
1079 splitBlock.addConnection(linkSource);
1081 addCell(splitBlock);
1082 // force resize and align on the grid
1083 resizeCell(splitBlock, new mxRectangle(splitPoint.getX(), splitPoint.getY(), 0, 0));
1087 // get breaking segment and related point
1088 mxPoint splitTr = new mxPoint(splitPoint.getX() - orig.getX(), splitPoint.getY() - orig.getY());
1089 final int pos = link.findNearestSegment(splitTr);
1091 // save points after breaking point
1092 final List<mxPoint> saveStartPoints = link.getPoints(pos, true);
1093 final List<mxPoint> saveEndPoints = link.getPoints(pos, false);
1095 // remove the first end point if the position is near the split
1097 if (saveEndPoints.size() > 0) {
1098 final mxPoint p = saveEndPoints.get(0);
1099 final double dx = p.getX() - splitTr.getX();
1100 final double dy = p.getY() - splitTr.getY();
1102 if (!getAsComponent().isSignificant(dx, dy)) {
1103 saveEndPoints.remove(0);
1108 getModel().beginUpdate();
1109 getModel().remove(link);
1110 getModel().endUpdate();
1112 connect(linkSource, splitBlock.getIn(), saveStartPoints, orig);
1113 connect(splitBlock.getOut1(), linkTarget, saveEndPoints, orig);
1117 getModel().endUpdate();
1124 * Connect two port together with the associated points.
1126 * This method perform the connection in two step in order to generate the
1127 * right UndoableChangeEdits.
1136 * the origin point (may be (0,0))
1138 public void connect(BasicPort src, BasicPort trg, List<mxPoint> points, mxPoint orig) {
1139 mxGeometry geometry;
1142 * Add the link with a default geometry
1144 final Object newLink1 = createEdge(null, null, null, src, trg, null);
1145 addCell(newLink1, null, null, src, trg);
1146 geometry = getModel().getGeometry(newLink1);
1147 geometry.setPoints(points);
1148 getModel().setGeometry(newLink1, geometry);
1151 * Update the geometry
1153 // should be cloned to generate an event
1154 geometry = (mxGeometry) getModel().getGeometry(newLink1).clone();
1155 final double dx = orig.getX();
1156 final double dy = orig.getY();
1158 geometry.translate(dx, dy);
1159 getModel().setGeometry(newLink1, geometry);
1163 * Initialize component settings for a graph.
1165 * This method *must* be used to setup the component after any
1168 public void initComponent() {
1169 getAsComponent().setToolTips(true);
1171 // This enable stop editing cells when pressing Enter.
1172 getAsComponent().setEnterStopsCellEditing(false);
1174 getAsComponent().setTolerance(1);
1176 getAsComponent().getViewport().setOpaque(false);
1178 getAsComponent().setBackground(XcosOptions.getEdition().getGraphBackground());
1180 final boolean gridEnable = XcosOptions.getEdition().isGraphGridEnable();
1181 setGridVisible(gridEnable);
1183 setGridSize(XcosOptions.getEdition().getGraphGrid());
1187 * Reinstall related listeners
1190 // Property change Listener
1191 // Will say if a diagram has been modified or not.
1192 final PropertyChangeListener p = new PropertyChangeListener() {
1194 public void propertyChange(final PropertyChangeEvent e) {
1195 if (e.getPropertyName().compareTo(MODIFIED) == 0) {
1196 if (!e.getOldValue().equals(e.getNewValue())) {
1202 getAsComponent().removePropertyChangeListener(MODIFIED, p);
1203 getAsComponent().addPropertyChangeListener(MODIFIED, p);
1207 * Install the default style sheet and the user stylesheet on the diagram.
1209 public void installStylesheet() {
1210 final mxStylesheet styleSheet = Xcos.getInstance().getStyleSheet();
1211 setStylesheet(styleSheet);
1215 * Install the multiplicities (use for link checking)
1217 private void setMultiplicities() {
1218 final List<mxMultiplicity> multiplicities = new ArrayList<mxMultiplicity>();
1221 multiplicities.add(new PortCheck(ExplicitInputPort.class, Collections.unmodifiableList(new ArrayList < Class <? extends mxCell >> () {
1222 private static final long serialVersionUID = -4987163442006736665L;
1224 add(ExplicitOutputPort.class);
1225 add(ExplicitLink.class);
1227 }), XcosMessages.LINK_ERROR_EXPLICIT_IN));
1228 multiplicities.add(new PortCheck(ImplicitInputPort.class, Collections.unmodifiableList(new ArrayList < Class <? extends mxCell >> () {
1229 private static final long serialVersionUID = 886376532181210926L;
1231 add(ImplicitOutputPort.class);
1232 add(ImplicitInputPort.class);
1233 add(ImplicitLink.class);
1235 }), XcosMessages.LINK_ERROR_IMPLICIT_IN));
1238 multiplicities.add(new PortCheck(ExplicitOutputPort.class, Collections.unmodifiableList(new ArrayList < Class <? extends mxCell >> () {
1239 private static final long serialVersionUID = 4594127972486054821L;
1241 add(ExplicitInputPort.class);
1243 }), XcosMessages.LINK_ERROR_EXPLICIT_OUT));
1244 multiplicities.add(new PortCheck(ImplicitOutputPort.class, Collections.unmodifiableList(new ArrayList < Class <? extends mxCell >> () {
1245 private static final long serialVersionUID = -3719677806532507973L;
1247 add(ImplicitInputPort.class);
1248 add(ImplicitOutputPort.class);
1249 add(ImplicitLink.class);
1251 }), XcosMessages.LINK_ERROR_IMPLICIT_OUT));
1254 multiplicities.add(new PortCheck(ControlPort.class, Collections.unmodifiableList(new ArrayList < Class <? extends mxCell >> () {
1255 private static final long serialVersionUID = 2941077191386058497L;
1257 add(CommandPort.class);
1258 add(CommandControlLink.class);
1260 }), XcosMessages.LINK_ERROR_EVENT_IN));
1263 multiplicities.add(new PortCheck(CommandPort.class, Collections.unmodifiableList(new ArrayList < Class <? extends mxCell >> () {
1264 private static final long serialVersionUID = -3470370027962480362L;
1266 add(ControlPort.class);
1268 }), XcosMessages.LINK_ERROR_EVENT_OUT));
1270 // ExplicitLink connections
1271 multiplicities.add(new PortCheck(ExplicitLink.class, Collections.unmodifiableList(new ArrayList < Class <? extends mxCell >> () {
1272 private static final long serialVersionUID = 7423543162930147373L;
1275 add(ExplicitInputPort.class);
1277 }), XcosMessages.LINK_ERROR_EVENT_OUT));
1279 // ImplicitLink connections
1280 multiplicities.add(new PortCheck(ImplicitLink.class, Collections.unmodifiableList(new ArrayList < Class <? extends mxCell >> () {
1281 private static final long serialVersionUID = 7775100011122283282L;
1284 add(ImplicitInputPort.class);
1285 add(ImplicitOutputPort.class);
1287 }), XcosMessages.LINK_ERROR_EVENT_OUT));
1289 // CommandControlLink connections
1290 multiplicities.add(new PortCheck(CommandControlLink.class, Collections.unmodifiableList(new ArrayList < Class <? extends mxCell >> () {
1291 private static final long serialVersionUID = 3260421433507192386L;
1294 add(ControlPort.class);
1296 }), XcosMessages.LINK_ERROR_EVENT_OUT));
1298 // Already connected port
1299 multiplicities.add(new PortCheck(BasicPort.class, Collections.unmodifiableList(new ArrayList < Class <? extends mxCell >> () {
1300 private static final long serialVersionUID = 6376349598052836660L;
1303 add(BasicPort.class);
1305 }), XcosMessages.LINK_ERROR_ALREADY_CONNECTED));
1307 setMultiplicities(multiplicities.toArray(new mxMultiplicity[multiplicities.size()]));
1311 * Install all needed Listeners.
1313 public void installListeners() {
1315 * First remove all listeners if present
1317 removeListener(SuperBlockUpdateTracker.getInstance());
1318 removeListener(CellAddedTracker.getInstance());
1319 removeListener(getEngine());
1320 getModel().removeListener(getEngine());
1321 removeListener(CellResizedTracker.getInstance());
1322 getUndoManager().removeListener(UndoUpdateTracker.getInstance());
1323 removeListener(RefreshBlockTracker.getInstance());
1325 // Track when superblock ask a parent refresh.
1326 addListener(XcosEvent.SUPER_BLOCK_UPDATED, SuperBlockUpdateTracker.getInstance());
1328 // Track when cells are added.
1329 addListener(mxEvent.CELLS_ADDED, CellAddedTracker.getInstance());
1330 addListener(mxEvent.CELLS_ADDED, getEngine());
1332 // Track when cells are deleted.
1333 addListener(mxEvent.CELLS_REMOVED, getEngine());
1335 // Track when resizing a cell.
1336 addListener(mxEvent.CELLS_RESIZED, CellResizedTracker.getInstance());
1338 // Track when we have to force a Block value
1339 addListener(XcosEvent.FORCE_CELL_VALUE_UPDATE, getEngine());
1340 getModel().addListener(XcosEvent.FORCE_CELL_VALUE_UPDATE, getEngine());
1342 // Update the blocks view on undo/redo
1343 getUndoManager().addListener(mxEvent.UNDO, UndoUpdateTracker.getInstance());
1344 getUndoManager().addListener(mxEvent.REDO, UndoUpdateTracker.getInstance());
1346 // Refresh port position on update
1347 addListener(XcosEvent.ADD_PORTS, RefreshBlockTracker.getInstance());
1352 * Removes the given cells from the graph including all connected edges if
1353 * includeEdges is true. The change is carried out using cellsRemoved.
1356 * the cells to be removed
1357 * @param includeEdges
1358 * true if the edges must be removed, false otherwise.
1359 * @return the deleted cells
1360 * @see com.mxgraph.view.mxGraph#removeCells(java.lang.Object[], boolean)
1363 public Object[] removeCells(final Object[] cells, final boolean includeEdges) {
1364 if (cells == null || cells.length == 0) {
1365 return super.removeCells(cells, includeEdges);
1369 * First remove all links connected to a removed Split if applicable
1371 final Object[] initialCells;
1373 initialCells = addAllEdges(cells);
1375 initialCells = cells;
1378 // stash used on the loop
1379 final Queue<Object> loopCells = new LinkedList<Object>(Arrays.asList(initialCells));
1380 // the cells that need to be really
1381 final Set<Object> removedCells = new HashSet<Object>(loopCells);
1382 // couple of cells to reconnect
1383 final List<BasicPort[]> connectedCells = new ArrayList<BasicPort[]>();
1384 final List<List<mxPoint>> connectedPoints = new ArrayList<List<mxPoint>>();
1387 * Then loop on the algorithm to select the right edges
1389 // /!\ not bounded algorithm
1390 while (loopCells.size() > 0) {
1391 Object cell = loopCells.remove();
1393 if (cell instanceof BasicLink) {
1395 * Continue on non fully connected links
1397 if (((BasicLink) cell).getSource() == null) {
1400 if (((BasicLink) cell).getTarget() == null) {
1405 * Add any split to a link
1407 addTerminalParent(((BasicLink) cell).getSource(), removedCells, loopCells);
1408 addTerminalParent(((BasicLink) cell).getTarget(), removedCells, loopCells);
1410 } else if (cell instanceof SplitBlock) {
1411 final SplitBlock splitBlock = (SplitBlock) cell;
1414 * Remove related connection or not and reconnect.
1417 if (splitBlock.getIn().getEdgeCount() == 0 || splitBlock.getOut1().getEdgeCount() == 0 || splitBlock.getOut2().getEdgeCount() == 0) {
1418 // corner case, all links will be removed
1422 final mxICell inLink = splitBlock.getIn().getEdgeAt(0);
1423 final mxICell out1Link = splitBlock.getOut1().getEdgeAt(0);
1424 final mxICell out2Link = splitBlock.getOut2().getEdgeAt(0);
1426 final boolean inRemoved = removedCells.contains(inLink);
1427 final boolean out1Removed = removedCells.contains(out1Link);
1428 final boolean out2Removed = removedCells.contains(out2Link);
1431 * Explicit case, if the in link is deleted; all the out links
1434 if (inLink instanceof ExplicitLink && inRemoved) {
1435 if (removedCells.add(out1Link)) {
1436 loopCells.add(out1Link);
1439 if (removedCells.add(out2Link)) {
1440 loopCells.add(out2Link);
1445 * Global case reconnect if not removed
1447 final BasicPort[] connection;
1448 List<mxPoint> points = null;
1449 if (!inRemoved && !out1Removed && out2Removed) {
1450 connection = findTerminals(inLink, out1Link, removedCells);
1451 points = getDirectPoints(splitBlock, inLink, out1Link);
1452 } else if (!inRemoved && out1Removed && !out2Removed) {
1453 connection = findTerminals(inLink, out2Link, removedCells);
1454 points = getDirectPoints(splitBlock, inLink, out2Link);
1455 } else if (inRemoved && !out1Removed && !out2Removed) {
1456 // only implicit or event case, log otherwise
1457 if (out1Link instanceof ExplicitLink || out2Link instanceof ExplicitLink) {
1458 LOG.severe("Reconnection failed for explicit links");
1461 connection = findTerminals(out1Link, out2Link, removedCells);
1462 points = getDirectPoints(splitBlock, out1Link, out2Link);
1468 if (connection != null) {
1469 connectedCells.add(connection);
1470 connectedPoints.add(points);
1476 getModel().beginUpdate();
1478 ret = super.removeCells(removedCells.toArray(), includeEdges);
1479 for (int i = 0; i < connectedCells.size(); i++) {
1480 final BasicPort[] connection = connectedCells.get(i);
1481 final List<mxPoint> points = connectedPoints.get(i);
1482 if (!removedCells.contains(connection[0].getParent()) && !removedCells.contains(connection[1].getParent())) {
1483 connect(connection[0], connection[1], points, new mxPoint());
1487 getModel().endUpdate();
1493 * Add any terminal parent to the removed cells
1496 * the current terminal (instance of BasicPort)
1497 * @param removedCells
1498 * the "to be removed" set
1500 * the "while loop" set
1502 private void addTerminalParent(mxICell terminal, Collection<Object> removedCells, Collection<Object> loopCells) {
1503 assert (terminal == null || terminal instanceof BasicPort);
1504 assert (removedCells != null);
1505 assert (loopCells != null);
1507 // getting terminal parent
1508 mxICell target = null;
1509 if (terminal != null) {
1510 target = terminal.getParent();
1515 // add target if applicable
1516 if (target instanceof SplitBlock) {
1517 if (removedCells.add(target)) {
1518 loopCells.add(target);
1524 * Find the terminals when relinking the 2 links
1526 * This method ensure that {source, target} are not child of removed blocks.
1529 * the normal source link
1530 * @param linkTerminal
1531 * the normal target link
1532 * @param removedCells
1533 * the set of removed objects
1534 * @return the {source, target} connection
1536 private BasicPort[] findTerminals(final mxICell linkSource, final mxICell linkTerminal, final Set<Object> removedCells) {
1537 BasicPort src = (BasicPort) linkSource.getTerminal(true);
1538 BasicPort tgt = (BasicPort) linkTerminal.getTerminal(false);
1539 if (linkSource instanceof ImplicitLink) {
1540 if (removedCells.contains(src.getParent())) {
1541 src = (BasicPort) linkSource.getTerminal(false);
1543 if (removedCells.contains(tgt.getParent())) {
1544 tgt = (BasicPort) linkTerminal.getTerminal(true);
1548 return new BasicPort[] { src, tgt };
1552 * Get the direct points from inLink.getSource() to outLink.getTarget().
1555 * the current splitblock (added as a mid-point)
1557 * the link before the split
1559 * the link after the split
1560 * @return the points
1562 private List<mxPoint> getDirectPoints(final SplitBlock splitBlock, final mxICell inLink, final mxICell outLink) {
1563 List<mxPoint> points;
1564 // add the points before the split
1565 points = new ArrayList<mxPoint>();
1566 if (inLink.getGeometry().getPoints() != null) {
1567 points.addAll(inLink.getGeometry().getPoints());
1570 // add a new point at the split location
1571 points.add(new mxPoint(snap(splitBlock.getGeometry().getCenterX()), snap(splitBlock.getGeometry().getCenterY())));
1573 // add the points after the split
1574 if (outLink.getGeometry().getPoints() != null) {
1575 points.addAll(outLink.getGeometry().getPoints());
1582 * Manage Group to be CellFoldable i.e with a (-) to reduce and a (+) to
1583 * expand them. Labels (mxCell instance with value) should not have a
1584 * visible foldable sign.
1589 * the collapse settings
1590 * @return always <code>false</code>
1591 * @see com.mxgraph.view.mxGraph#isCellFoldable(java.lang.Object, boolean)
1594 public boolean isCellFoldable(final Object cell, final boolean collapse) {
1599 * Not BasicBLock cell have a moveable label.
1603 * @return true if the corresponding label is moveable
1604 * @see com.mxgraph.view.mxGraph#isLabelMovable(java.lang.Object)
1607 public boolean isLabelMovable(Object cell) {
1608 return !(cell instanceof BasicBlock);
1612 * Return true if selectable
1617 * @see com.mxgraph.view.mxGraph#isCellSelectable(java.lang.Object)
1620 public boolean isCellSelectable(final Object cell) {
1621 if (cell instanceof BasicPort) {
1624 return super.isCellSelectable(cell);
1628 * Return true if movable
1633 * @see com.mxgraph.view.mxGraph#isCellMovable(java.lang.Object)
1636 public boolean isCellMovable(final Object cell) {
1637 if (cell instanceof BasicPort) {
1641 boolean movable = false;
1642 final Object[] cells = getSelectionCells();
1644 // don't move if selection is only links
1645 for (Object c : cells) {
1646 if (!(c instanceof BasicLink)) {
1652 return movable && super.isCellMovable(cell);
1656 * Return true if resizable
1661 * @see com.mxgraph.view.mxGraph#isCellResizable(java.lang.Object)
1664 public boolean isCellResizable(final Object cell) {
1665 if (cell instanceof SplitBlock) {
1668 return (cell instanceof BasicBlock) && super.isCellResizable(cell);
1672 * A cell is deletable if it is not a locked block or an identifier cell
1677 * @see com.mxgraph.view.mxGraph#isCellDeletable(java.lang.Object)
1680 public boolean isCellDeletable(final Object cell) {
1681 final boolean isALockedBLock = cell instanceof BasicBlock && ((BasicBlock) cell).isLocked();
1682 final boolean isAnIdentifier = cell.getClass().equals(mxCell.class);
1684 if (isALockedBLock) {
1687 if (isAnIdentifier) {
1691 return super.isCellDeletable(cell);
1695 * Return true if editable
1700 * @see com.mxgraph.view.mxGraph#isCellEditable(java.lang.Object)
1703 public boolean isCellEditable(final Object cell) {
1704 return (cell instanceof TextBlock) && super.isCellDeletable(cell);
1708 * Return or create the identifier for the cell
1712 * @return the identifier cell
1714 public mxCell getOrCreateCellIdentifier(final mxCell cell) {
1715 final mxGraphModel graphModel = (mxGraphModel) getModel();
1717 final mxCell identifier;
1718 final String cellId = cell.getId() + HASH_IDENTIFIER;
1719 if (graphModel.getCell(cellId) == null) {
1720 // create the identifier
1721 identifier = createCellIdentifier(cell);
1723 // add the identifier
1724 graphModel.add(cell, identifier, cell.getChildCount());
1726 identifier = (mxCell) graphModel.getCell(cellId);
1732 * Return the identifier for the cell
1736 * @return the identifier cell
1738 public mxCell getCellIdentifier(final mxCell cell) {
1739 final mxGraphModel graphModel = (mxGraphModel) getModel();
1740 final String cellId = cell.getId() + HASH_IDENTIFIER;
1742 return (mxCell) graphModel.getCell(cellId);
1746 * Create a cell identifier for a specific cell
1750 * @return the cell identifier.
1752 public mxCell createCellIdentifier(final mxCell cell) {
1753 final mxCell identifier;
1754 final String cellId = cell.getId() + HASH_IDENTIFIER;
1756 identifier = new mxCell(null, (mxGeometry) DEFAULT_LABEL_GEOMETRY.clone(), "noLabel=0;opacity=0;");
1757 identifier.getGeometry().setRelative(true);
1758 identifier.setVertex(true);
1759 identifier.setConnectable(false);
1760 identifier.setId(cellId);
1766 * Get the label for the cell according to its style.
1770 * @return a representative the string (block name) or a style specific
1772 * @see com.mxgraph.view.mxGraph#convertValueToString(java.lang.Object)
1775 public String convertValueToString(Object cell) {
1779 final Map<String, Object> style = getCellStyle(cell);
1781 final String displayedLabel = (String) style.get("displayedLabel");
1782 if (displayedLabel != null) {
1783 if (cell instanceof BasicBlock) {
1785 ret = String.format(displayedLabel, ((BasicBlock) cell).getExprsFormat());
1786 } catch (IllegalFormatException e) {
1787 LOG.severe(e.toString());
1788 ret = displayedLabel;
1791 ret = displayedLabel;
1794 final String label = super.convertValueToString(cell);
1795 if (label.isEmpty() && cell instanceof BasicBlock) {
1796 ret = ((BasicBlock) cell).getInterfaceFunctionName();
1807 * Return true if auto sized
1812 * @see com.mxgraph.view.mxGraph#isAutoSizeCell(java.lang.Object)
1815 public boolean isAutoSizeCell(final Object cell) {
1816 boolean status = super.isAutoSizeCell(cell);
1818 if (cell instanceof AfficheBlock) {
1822 if (cell instanceof TextBlock) {
1830 * {@inheritDoc} Do not extends if the port position is north or south.
1833 public boolean isExtendParent(Object cell) {
1834 final boolean extendsParents;
1836 if (cell instanceof BasicPort) {
1837 final BasicPort p = (BasicPort) cell;
1838 extendsParents = !(p.getOrientation() == Orientation.NORTH || p.getOrientation() == Orientation.SOUTH) && super.isExtendParent(p);
1840 extendsParents = super.isExtendParent(cell);
1842 return extendsParents;
1846 * @return the parameters
1848 public ScicosParameters getScicosParameters() {
1849 return scicosParameters;
1853 * @param scicosParameters
1854 * the simulation parameters
1856 public void setScicosParameters(final ScicosParameters scicosParameters) {
1857 this.scicosParameters = scicosParameters;
1859 scicosParameters.addPropertyChangeListener(getEngine());
1863 * @return the engine
1865 public CompilationEngineStatus getEngine() {
1870 * @param finalIntegrationTime
1871 * set integration time
1872 * @throws PropertyVetoException
1873 * when the value is invalid
1874 * @category XMLCompatibility
1876 public void setFinalIntegrationTime(final double finalIntegrationTime) throws PropertyVetoException {
1877 scicosParameters.setFinalIntegrationTime(finalIntegrationTime);
1881 * @param integratorAbsoluteTolerance
1882 * set integrator absolute tolerance
1883 * @throws PropertyVetoException
1884 * when the value is invalid
1885 * @category XMLCompatibility
1887 public void setIntegratorAbsoluteTolerance(final double integratorAbsoluteTolerance) throws PropertyVetoException {
1888 scicosParameters.setIntegratorAbsoluteTolerance(integratorAbsoluteTolerance);
1892 * @param integratorRelativeTolerance
1893 * integrator relative tolerance
1894 * @throws PropertyVetoException
1895 * when the value is invalid
1896 * @category XMLCompatibility
1898 public void setIntegratorRelativeTolerance(final double integratorRelativeTolerance) throws PropertyVetoException {
1899 scicosParameters.setIntegratorRelativeTolerance(integratorRelativeTolerance);
1903 * @param maximumStepSize
1905 * @throws PropertyVetoException
1906 * when the value is invalid
1907 * @category XMLCompatibility
1909 public void setMaximumStepSize(final double maximumStepSize) throws PropertyVetoException {
1910 scicosParameters.setMaximumStepSize(maximumStepSize);
1914 * @param maxIntegrationTimeinterval
1915 * set max integration time
1916 * @throws PropertyVetoException
1917 * when the value is invalid
1918 * @category XMLCompatibility
1920 public void setMaxIntegrationTimeinterval(final double maxIntegrationTimeinterval) throws PropertyVetoException {
1921 scicosParameters.setMaxIntegrationTimeInterval(maxIntegrationTimeinterval);
1925 * @param realTimeScaling
1926 * set real time scaling
1927 * @throws PropertyVetoException
1928 * when the value is invalid
1929 * @category XMLCompatibility
1931 public void setRealTimeScaling(final double realTimeScaling) throws PropertyVetoException {
1932 scicosParameters.setRealTimeScaling(realTimeScaling);
1938 * @throws PropertyVetoException
1939 * when the value is invalid
1940 * @category XMLCompatibility
1942 public void setSolver(final double solver) throws PropertyVetoException {
1943 scicosParameters.setSolver(solver);
1947 * @param toleranceOnTime
1948 * set tolerance time
1949 * @throws PropertyVetoException
1950 * when the value is invalid
1951 * @category XMLCompatibility
1953 public void setToleranceOnTime(final double toleranceOnTime) throws PropertyVetoException {
1954 scicosParameters.setToleranceOnTime(toleranceOnTime);
1958 * Get the current diagram context
1960 * @return the context at the current node
1962 public String[] getContext() {
1963 return scicosParameters.getContext();
1967 * Manage the visibility of the grid and the associated menu
1972 public void setGridVisible(final boolean status) {
1973 setGridEnabled(status);
1974 getAsComponent().setGridVisible(status);
1975 getAsComponent().repaint();
1979 * @return save status
1981 public boolean saveDiagram() {
1982 final boolean isSuccess = saveDiagramAs(getSavedFile());
1986 Xcos.getInstance().addDiagram(getSavedFile(), this);
1995 * @return save status
1997 public boolean saveDiagramAs(final File fileName) {
1998 boolean isSuccess = false;
1999 File writeFile = fileName;
2000 XcosFileType format = XcosOptions.getPreferences().getFileFormat();
2002 info(XcosMessages.SAVING_DIAGRAM);
2003 if (fileName == null) {
2004 final SwingScilabFileChooser fc = SaveAsAction.createFileChooser();
2005 SaveAsAction.configureFileFilters(fc);
2006 ConfigurationManager.configureCurrentDirectory(fc);
2008 if (getSavedFile() != null) {
2009 // using save-as, the file chooser should have a filename
2010 // without extension as default
2011 String filename = getSavedFile().getName();
2012 filename = filename.substring(0, filename.lastIndexOf('.'));
2013 fc.setSelectedFile(new File(filename));
2015 final String title = getTitle();
2016 if (title != null) {
2018 * Escape file to handle not supported character in file
2019 * name (may be Windows only) see
2020 * http://msdn.microsoft.com/en
2021 * -us/library/windows/desktop/aa365247%28v=vs.85%29.aspx
2023 final char[] regex = "<>:\"/\\|?*".toCharArray();
2024 String escaped = title;
2025 for (char c : regex) {
2026 escaped = escaped.replace(c, '-');
2029 fc.setSelectedFile(new File(escaped));
2033 int status = fc.showSaveDialog(this.getAsComponent());
2034 if (status != JFileChooser.APPROVE_OPTION) {
2035 info(XcosMessages.EMPTY_INFO);
2039 // change the format if the user choose a save-able file format
2040 final XcosFileType selectedFilter = XcosFileType.findFileType(fc.getFileFilter());
2041 if (XcosFileType.getAvailableSaveFormats().contains(selectedFilter)) {
2042 format = selectedFilter;
2044 writeFile = fc.getSelectedFile();
2047 /* Extension/format update */
2049 // using a String filename also works on a non-existing file
2050 final String filename = writeFile.getName();
2053 * Look for the user extension if it does not exists, append a default
2056 * if the specified extension is handled, update the save format ; else
2057 * append a default extension and use the default format
2059 XcosFileType userExtension = XcosFileType.findFileType(filename);
2060 if (userExtension == null) {
2061 writeFile = new File(writeFile.getParent(), filename + format.getDottedExtension());
2062 userExtension = format;
2064 if (XcosFileType.getAvailableSaveFormats().contains(userExtension)) {
2065 format = userExtension;
2067 writeFile = new File(writeFile.getParent(), filename + format.getDottedExtension());
2071 * If the file exists, ask for confirmation if this is not the
2072 * previously saved file
2074 if (writeFile.exists() && !writeFile.equals(getSavedFile())) {
2075 final boolean overwrite = ScilabModalDialog.show(XcosTab.get(this), XcosMessages.OVERWRITE_EXISTING_FILE, XcosMessages.XCOS,
2076 IconType.QUESTION_ICON, ButtonType.YES_NO) == AnswerOption.YES_OPTION;
2079 info(XcosMessages.EMPTY_INFO);
2085 * Really save the data
2088 format.save(writeFile.getCanonicalPath(), getRootDiagram());
2089 setSavedFile(writeFile);
2091 setTitle(writeFile.getName().substring(0, writeFile.getName().lastIndexOf('.')));
2092 ConfigurationManager.getInstance().addToRecentFiles(writeFile);
2095 } catch (final Exception e) {
2096 LOG.severe(e.toString());
2098 XcosDialogs.couldNotSaveFile(this);
2101 info(XcosMessages.EMPTY_INFO);
2106 * Perform post loading initialization.
2111 public void postLoad(final File file) {
2112 final String name = file.getName();
2114 if (XcosFileType.getAvailableSaveFormats().contains(XcosFileType.findFileType(file))) {
2116 Xcos.getInstance().addDiagram(file, this);
2118 setTitle(name.substring(0, name.lastIndexOf('.')));
2121 fireEvent(new mxEventObject(mxEvent.ROOT));
2123 info(XcosMessages.EMPTY_INFO);
2127 * Set the title of the diagram
2131 * @see org.scilab.modules.graph.ScilabGraph#setTitle(java.lang.String)
2134 public void setTitle(final String title) {
2135 super.setTitle(title);
2142 public void updateTabTitle() {
2143 // get the modifier string
2144 final String modified;
2151 // get the title string
2152 final String title = getTitle();
2155 CharSequence formattedPath = "";
2156 final File savedFile = getSavedFile();
2157 if (savedFile != null) {
2159 final String path = savedFile.getCanonicalPath();
2160 formattedPath = new StringBuilder().append(" (").append(path).append(')');
2161 } catch (final IOException e) {
2162 LOG.warning(e.toString());
2167 final String product = Xcos.TRADENAME;
2169 final String tabTitle = new StringBuilder().append(modified).append(title).append(formattedPath).append(" - ").append(product).toString();
2171 final SwingScilabTab tab = ScilabTabFactory.getInstance().getFromCache(getGraphTab());
2173 tab.setName(tabTitle);
2180 * @throws PropertyVetoException
2181 * when the new value is invalid
2183 public void setContext(final String[] context) throws PropertyVetoException {
2184 scicosParameters.setContext(context);
2185 fireEvent(new mxEventObject(XcosEvent.DIAGRAM_UPDATED));
2186 updateCellsContext();
2190 * Update context value in diagram children
2192 public void updateCellsContext() {
2193 final Object rootParent = getDefaultParent();
2194 final int childCount = getModel().getChildCount(rootParent);
2195 for (int i = 0; i < childCount; ++i) {
2196 final Object obj = getModel().getChildAt(rootParent, i);
2197 if (obj instanceof ContextUpdate) {
2198 final String[] globalContext = getContext();
2200 /* Determine if the context is not empty */
2201 int nbOfDetectedChar = 0;
2202 for (int j = 0; j < globalContext.length; j++) {
2203 globalContext[j] = globalContext[j].replaceFirst("\\s", "");
2204 nbOfDetectedChar += globalContext[j].length();
2205 if (nbOfDetectedChar != 0) {
2210 if (nbOfDetectedChar != 0) {
2211 ((ContextUpdate) obj).onContextChange(globalContext);
2214 } else if (obj instanceof SuperBlock) {
2215 final SuperBlock superBlock = (SuperBlock) obj;
2216 if (superBlock.getChild() != null) {
2217 superBlock.getChild().updateCellsContext();
2225 * change debug level
2226 * @category XMLCompatibility
2227 * @throws PropertyVetoException
2228 * when the new value is invalid
2230 public void setDebugLevel(final int debugLevel) throws PropertyVetoException {
2231 scicosParameters.setDebugLevel(debugLevel);
2235 * Popup a dialog to ask for a file creation
2238 * the file to create
2239 * @return true if creation is has been performed
2241 public boolean askForFileCreation(final File f) {
2242 AnswerOption answer;
2244 answer = ScilabModalDialog.show(getAsComponent(), new String[] { String.format(XcosMessages.FILE_DOESNT_EXIST, f.getCanonicalFile()) },
2245 XcosMessages.XCOS, IconType.QUESTION_ICON, ButtonType.YES_NO);
2246 } catch (final IOException e) {
2247 LOG.severe(e.toString());
2248 answer = AnswerOption.YES_OPTION;
2251 if (answer == AnswerOption.YES_OPTION) {
2252 return saveDiagramAs(f);
2259 * Load a file with different method depending on it extension
2262 * File to load (can be null)
2264 * the variable to decode (can be null)
2266 public void transformAndLoadFile(final String file, final String variable) {
2268 final XcosFileType filetype;
2271 filetype = XcosFileType.findFileType(f);
2276 new SwingWorker<XcosDiagram, ActionEvent>() {
2278 final Timer t = new Timer(1000, new ActionListener() {
2280 public void actionPerformed(ActionEvent e) {
2281 counter = (counter + 1) % (XcosMessages.DOTS.length() + 1);
2282 String str = XcosMessages.LOADING_DIAGRAM + XcosMessages.DOTS.substring(0, counter);
2284 XcosDiagram.this.info(str);
2289 protected XcosDiagram doInBackground() {
2292 final Xcos instance = Xcos.getInstance();
2293 XcosDiagram diag = XcosDiagram.this;
2295 diag.setReadOnly(true);
2298 * Load, log errors and notify
2300 synchronized (instance) {
2303 if (f != null && filetype != null) {
2304 filetype.load(file, XcosDiagram.this);
2307 final ScilabDirectHandler handler = ScilabDirectHandler.acquire();
2309 if (variable != null && handler != null) {
2310 handler.readDiagram(XcosDiagram.this, variable);
2313 if (handler != null) {
2318 instance.setLastError("");
2319 } catch (Exception e) {
2321 while (ex instanceof RuntimeException) {
2324 instance.setLastError(ex.getMessage());
2333 protected void done() {
2335 XcosDiagram.this.setReadOnly(false);
2336 XcosDiagram.this.getUndoManager().clear();
2337 XcosDiagram.this.refresh();
2342 if (f != null && filetype != null) {
2345 XcosDiagram.this.info(XcosMessages.EMPTY_INFO);
2352 * Update all the children of the current graph.
2354 public void setChildrenParentDiagram() {
2355 getModel().beginUpdate();
2357 for (int i = 0; i < getModel().getChildCount(getDefaultParent()); i++) {
2358 final mxCell cell = (mxCell) getModel().getChildAt(getDefaultParent(), i);
2359 if (cell instanceof BasicBlock) {
2360 final BasicBlock block = (BasicBlock) cell;
2361 block.setParentDiagram(this);
2365 getModel().endUpdate();
2370 * Getting the root diagram of a decomposed diagram
2372 * @return Root parent of the whole parent
2374 public XcosDiagram getRootDiagram() {
2375 XcosDiagram rootGraph = this;
2376 while (rootGraph instanceof SuperBlockDiagram) {
2377 rootGraph = ((SuperBlockDiagram) rootGraph).getContainer().getParentDiagram();
2383 * Returns the tooltip to be used for the given cell.
2387 * @return cell tooltip
2390 public String getToolTipForCell(final Object cell) {
2391 if (cell instanceof BasicBlock) {
2392 return ((BasicBlock) cell).getToolTipText();
2393 } else if (cell instanceof BasicPort) {
2394 return ((BasicPort) cell).getToolTipText();
2400 * Display the message in info bar.
2405 public void info(final String message) {
2406 final XcosTab tab = XcosTab.get(this);
2408 if (tab != null && tab.getInfoBar() != null) {
2409 tab.getInfoBar().setText(message);
2414 * Display the message into an error popup
2417 * Error of the message
2419 public void error(final String message) {
2420 JOptionPane.showMessageDialog(getAsComponent(), message, XcosMessages.XCOS, JOptionPane.ERROR_MESSAGE);
2424 * Find the block corresponding to the given uid and display a warning
2428 * - A String as UID.
2430 * - The message to display.
2432 public void warnCellByUID(final String uid, final String message) {
2433 final Object cell = ((mxGraphModel) getModel()).getCell(uid);
2438 if (GraphicsEnvironment.isHeadless()) {
2439 System.err.printf("%s at %s\n %s: %s\n", "warnCell", getRootDiagram().getTitle(), uid, message);
2444 if (XcosTab.get(this) == null) {
2445 XcosTab.restore(this);
2448 if (message.isEmpty()) {
2449 getAsComponent().clearCellOverlays(cell);
2451 getAsComponent().setCellWarning(cell, message, null, true);
2456 * Set the current diagram in a modified state
2459 * True or False whether the current diagram must be saved or
2463 public void setModified(final boolean modified) {
2464 super.setModified(modified);
2469 * Evaluate the current context
2471 * @return The resulting data. Keys are variable names and Values are
2474 public Map<String, String> evaluateContext() {
2475 Map<String, String> result = Collections.emptyMap();
2477 final ScilabDirectHandler handler = ScilabDirectHandler.acquire();
2478 if (handler == null) {
2483 // first write the context strings
2484 handler.writeContext(getContext());
2486 // evaluate using script2var
2487 ScilabInterpreterManagement.synchronousScilabExec(ScilabDirectHandler.CONTEXT + " = script2var(" + ScilabDirectHandler.CONTEXT + ", struct());");
2489 // read the structure
2490 result = handler.readContext();
2491 } catch (final InterpreterException e) {
2492 info("Unable to evaluate the contexte");
2493 e.printStackTrace();
2506 public BasicBlock getChildById(final String uid) {
2507 BasicBlock returnBlock = null;
2508 for (int i = 0; i < getModel().getChildCount(getDefaultParent()); ++i) {
2509 if (getModel().getChildAt(getDefaultParent(), i) instanceof BasicBlock) {
2510 final BasicBlock block = (BasicBlock) getModel().getChildAt(getDefaultParent(), i);
2511 if (block.getId().compareTo(uid) == 0) { // find it
2512 returnBlock = block;
2514 if (block instanceof SuperBlock) {
2515 ((SuperBlock) block).createChildDiagram();
2518 returnBlock = ((SuperBlock) block).getChild().getChildById(uid);
2520 } else if (block.getRealParameters() instanceof ScilabMList) {
2521 // we have a hidden SuperBlock, create a real one
2522 SuperBlock newSP = (SuperBlock) BlockFactory.createBlock(SuperBlock.INTERFUNCTION_NAME);
2523 newSP.setParentDiagram(block.getParentDiagram());
2525 newSP.setRealParameters(block.getRealParameters());
2526 newSP.createChildDiagram(true);
2529 returnBlock = newSP.getChild().getChildById(uid);
2530 block.setRealParameters(newSP.getRealParameters());
2535 if (returnBlock != null) {
2543 * Returns true if the given cell is a not a block nor a port.
2548 * the cells to be dropped
2549 * @return the drop status
2550 * @see com.mxgraph.view.mxGraph#isValidDropTarget(java.lang.Object,
2551 * java.lang.Object[])
2554 public boolean isValidDropTarget(final Object cell, final Object[] cells) {
2555 return !(cell instanceof BasicBlock) && !(cell instanceof BasicBlock) && !(cell instanceof BasicPort) && super.isValidDropTarget(cell, cells);
2559 * @return child visibility
2562 public boolean isChildVisible() {
2567 // for (int i = 0; i < getModel().getChildCount(getDefaultParent());
2569 // final Object child = getModel().getChildAt(getDefaultParent(), i);
2570 // if (child instanceof SuperBlock) {
2571 // final XcosDiagram diag = ((SuperBlock) child).getChild();
2572 // if (diag != null && (diag.isChildVisible() || diag.isVisible())) {
2573 // // if child or sub child is visible
2582 * @return close capability
2584 public boolean canClose() {
2585 if (!isChildVisible()) {
2592 * Construct a new selection model used on this graph.
2594 * @return a new selection model instance.
2595 * @see com.mxgraph.view.mxGraph#createSelectionModel()
2598 protected mxGraphSelectionModel createSelectionModel() {
2599 return new mxGraphSelectionModel(this) {
2601 * When we only want to select a cell which is a port, select the
2606 * @see com.mxgraph.view.mxGraphSelectionModel#setCell(java.lang.Object)
2609 public void setCell(final Object cell) {
2610 final Object current;
2611 if (cell instanceof BasicPort) {
2612 current = getModel().getParent(cell);
2616 super.setCell(current);