2 * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3 * Copyright (C) 2009-2009 - DIGITEO - Bruno JOFRET
4 * Copyright (C) 2009-2010 - DIGITEO - Clement DAVID
5 * Copyright (C) 2011-2017 - Scilab Enterprises - Clement DAVID
6 * Copyright (C) 2015 - Marcos Cardinot
7 * Copyright (C) 2017-2019 - ESI Group - Clement DAVID
9 * Copyright (C) 2012 - 2016 - Scilab Enterprises
11 * This file is hereby licensed under the terms of the GNU GPL v2.0,
12 * pursuant to article 5.3.4 of the CeCILL v.2.1.
13 * This file was originally licensed under the terms of the CeCILL v2.1,
14 * and continues to be available under such terms.
15 * For more information, see the COPYING file which you should have received
16 * along with this program.
19 package org.scilab.modules.xcos.graph;
21 import org.scilab.modules.xcos.graph.model.XcosGraphModel;
22 import java.awt.GraphicsEnvironment;
23 import java.awt.event.ActionEvent;
24 import java.awt.event.ActionListener;
26 import java.io.IOException;
27 import java.util.ArrayList;
28 import java.util.Arrays;
29 import java.util.Collection;
30 import java.util.Collections;
31 import java.util.Comparator;
32 import java.util.HashSet;
33 import java.util.IllegalFormatException;
34 import java.util.LinkedList;
35 import java.util.List;
37 import java.util.Queue;
39 import java.util.logging.Logger;
41 import javax.swing.JFileChooser;
42 import javax.swing.JOptionPane;
43 import javax.swing.SwingWorker;
44 import javax.swing.Timer;
46 import org.scilab.modules.action_binding.highlevel.ScilabInterpreterManagement;
47 import org.scilab.modules.action_binding.highlevel.ScilabInterpreterManagement.InterpreterException;
48 import org.scilab.modules.graph.ScilabGraph;
49 import org.scilab.modules.graph.utils.ScilabGraphConstants;
50 import org.scilab.modules.gui.bridge.filechooser.SwingScilabFileChooser;
51 import org.scilab.modules.gui.bridge.tab.SwingScilabDockablePanel;
52 import org.scilab.modules.gui.messagebox.ScilabModalDialog;
53 import org.scilab.modules.gui.messagebox.ScilabModalDialog.AnswerOption;
54 import org.scilab.modules.gui.messagebox.ScilabModalDialog.ButtonType;
55 import org.scilab.modules.gui.messagebox.ScilabModalDialog.IconType;
56 import org.scilab.modules.gui.tabfactory.ScilabTabFactory;
57 import org.scilab.modules.xcos.JavaController;
58 import org.scilab.modules.xcos.Kind;
59 import org.scilab.modules.xcos.ObjectProperties;
60 import org.scilab.modules.xcos.VectorOfDouble;
61 import org.scilab.modules.xcos.VectorOfInt;
62 import org.scilab.modules.xcos.VectorOfString;
63 import org.scilab.modules.xcos.Xcos;
64 import org.scilab.modules.xcos.XcosTab;
65 import org.scilab.modules.xcos.actions.SaveAsAction;
66 import org.scilab.modules.xcos.block.AfficheBlock;
67 import org.scilab.modules.xcos.block.BasicBlock;
68 import org.scilab.modules.xcos.block.SplitBlock;
69 import org.scilab.modules.xcos.block.TextBlock;
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.model.BlockInterFunction;
78 import org.scilab.modules.xcos.graph.model.ScicosObjectOwner;
79 import org.scilab.modules.xcos.graph.model.XcosCell;
80 import org.scilab.modules.xcos.graph.model.XcosCellFactory;
81 import org.scilab.modules.xcos.graph.swing.GraphComponent;
82 import org.scilab.modules.xcos.io.XcosFileType;
83 import org.scilab.modules.xcos.io.scicos.ScilabDirectHandler;
84 import org.scilab.modules.xcos.link.BasicLink;
85 import org.scilab.modules.xcos.link.CommandControlLink;
86 import org.scilab.modules.xcos.link.ExplicitLink;
87 import org.scilab.modules.xcos.link.ImplicitLink;
88 import org.scilab.modules.xcos.port.BasicPort;
89 import org.scilab.modules.xcos.port.BasicPort.Type;
90 import org.scilab.modules.xcos.port.Orientation;
91 import org.scilab.modules.xcos.port.PortCheck;
92 import org.scilab.modules.xcos.port.command.CommandPort;
93 import org.scilab.modules.xcos.port.control.ControlPort;
94 import org.scilab.modules.xcos.port.input.ExplicitInputPort;
95 import org.scilab.modules.xcos.port.input.ImplicitInputPort;
96 import org.scilab.modules.xcos.port.output.ExplicitOutputPort;
97 import org.scilab.modules.xcos.port.output.ImplicitOutputPort;
98 import org.scilab.modules.xcos.preferences.XcosOptions;
99 import org.scilab.modules.xcos.utils.BlockPositioning;
100 import org.scilab.modules.xcos.utils.Stack;
101 import org.scilab.modules.xcos.utils.XcosConstants;
102 import org.scilab.modules.xcos.utils.XcosDialogs;
103 import org.scilab.modules.xcos.utils.XcosMessages;
105 import com.mxgraph.model.mxCell;
106 import com.mxgraph.model.mxGeometry;
107 import com.mxgraph.model.mxGraphModel;
108 import com.mxgraph.model.mxICell;
109 import com.mxgraph.util.mxEvent;
110 import com.mxgraph.util.mxEventObject;
111 import com.mxgraph.util.mxPoint;
112 import com.mxgraph.view.mxGraphSelectionModel;
113 import com.mxgraph.view.mxMultiplicity;
114 import java.lang.reflect.Constructor;
115 import java.rmi.server.UID;
116 import java.util.HashMap;
117 import java.util.Hashtable;
118 import java.util.Optional;
119 import java.util.stream.Collectors;
120 import org.scilab.modules.types.ScilabType;
121 import org.scilab.modules.xcos.VectorOfBool;
122 import org.scilab.modules.xcos.VectorOfScicosID;
123 import org.scilab.modules.xcos.block.SuperBlock;
124 import org.scilab.modules.xcos.block.io.ContextUpdate;
125 import org.scilab.modules.xcos.io.ScilabTypeCoder;
126 import org.scilab.modules.xcos.utils.XcosEvent;
129 * The base class for a diagram. This class contains jgraphx + Scicos data.
131 public class XcosDiagram extends ScilabGraph {
133 private static final Logger LOG = Logger.getLogger(XcosDiagram.class.getName());
135 private static final String CELLS = "cells";
136 public static final String IN = "in";
137 public static final String OUT = "out";
138 public static final String EIN = "ein";
139 public static final String EOUT = "eout";
142 * Prefix used to tag text node.
144 public static final String HASH_IDENTIFIER = "#identifier";
149 * @param controller the shared controller
150 * @param diagramId the diagram MVC ID
151 * @param kind DIAGRAM or BLOCK for a root diagram or a super-block
152 * @param uid the string UID that will be used on the default parent
154 public XcosDiagram(final JavaController controller, final long diagramId, final Kind kind, String uid) {
155 super(new XcosGraphModel(controller, diagramId, kind, uid), Xcos.getInstance().getStyleSheet());
157 // set the default parent (the JGraphX layer) defined on the model
158 setDefaultParent(getModel().getChildAt(getModel().getRoot(), 0));
160 setComponent(new GraphComponent(this));
163 // Forbid disconnecting cells once it is connected.
164 setCellsDisconnectable(false);
166 // Forbid pending edges.
167 setAllowDanglingEdges(false);
169 // Cannot connect port to itself.
170 setAllowLoops(false);
172 // Override isCellResizable to filter what the user can resize
173 setCellsResizable(true);
175 /* Labels use HTML if not equal to interface function name */
178 * by default every label is movable, see XcosDiagram##isLabelMovable(java.lang.Object) for restrictions
180 setVertexLabelsMovable(true);
181 setEdgeLabelsMovable(true);
184 setCloneInvalidEdges(true);
186 // Override isCellEditable to filter what the user can edit
187 setCellsEditable(true);
189 setConnectableEdges(true);
191 // Do not clear edge points on connect
192 setResetEdgesOnConnect(false);
196 // auto-position the diagram origin
199 // do not put loop links inside the common block cell but on the defaultParent
200 ((mxGraphModel) getModel()).setMaintainEdgeParent(false);
207 * Only return the instanceof klass
209 * @param selection the selection to filter out
210 * @param klass the class selector
211 * @return the selection with only klass instance.
213 public static Object[] filterByClass(final Object[] selection, final Class<BasicBlock> klass) {
214 return mxGraphModel.filterCells(selection, new mxGraphModel.Filter() {
216 public boolean filter(Object cell) {
217 return klass.isInstance(cell);
223 * Fill the hierarchy from the first element up to the root diagram
226 * Should be used as :
228 * hierarchy = fillHierarchy(new ScicosObjectOwner(getUID(), getKind()))
231 * @param hierarchy the collection to fill
232 * @return the filled collection (the root at the end)
234 public static Stack<ScicosObjectOwner> lookForHierarchy(ScicosObjectOwner current) {
235 ScicosObjectOwner local = current;
236 Stack<ScicosObjectOwner> hierarchy = new Stack<>();
237 JavaController controller = new JavaController();
239 long[] parent = new long[] {local.getUID()};
240 if (local.getKind() == Kind.DIAGRAM) {
241 hierarchy.push(local);
245 while (parent[0] != 0l) {
246 hierarchy.push(new ScicosObjectOwner(parent[0], Kind.BLOCK));
247 controller.getObjectProperty(parent[0], Kind.BLOCK, ObjectProperties.PARENT_BLOCK, parent);
250 controller.getObjectProperty(local.getUID(), local.getKind(), ObjectProperties.PARENT_DIAGRAM, parent);
251 hierarchy.push(new ScicosObjectOwner(parent[0], Kind.DIAGRAM));
257 * Sort the blocks per first integer parameter value
259 * @param blocks the block list
260 * @param the shared controller
261 * @return the sorted block list (same instance)
263 public static List<? extends BasicBlock> iparSort(final List<? extends BasicBlock> blocks, final JavaController controller) {
264 Collections.sort(blocks, new Comparator<BasicBlock>() {
266 public int compare(BasicBlock o1, BasicBlock o2) {
269 final VectorOfInt data1 = new VectorOfInt();
270 final VectorOfInt data2 = new VectorOfInt();
272 controller.getObjectProperty(o1.getUID(), Kind.BLOCK, ObjectProperties.IPAR, data1);
273 controller.getObjectProperty(o2.getUID(), Kind.BLOCK, ObjectProperties.IPAR, data2);
276 if (data1.size() >= 1) {
277 value1 = data1.get(0);
283 if (data2.size() >= 1) {
284 value2 = data2.get(0);
289 return value1 - value2;
296 * @param <T> The type to work on
297 * @param klass the class instance to work on
298 * @return list of typed block
300 @SuppressWarnings("unchecked")
301 private <T extends BasicBlock> List<T> getAllTypedBlock(Class<T> klass) {
302 final List<T> list = new ArrayList<T>();
304 int blockCount = getModel().getChildCount(getDefaultParent());
306 for (int i = 0; i < blockCount; i++) {
307 Object cell = getModel().getChildAt(getDefaultParent(), i);
308 if (klass.isInstance(cell)) {
309 // According to the test we are sure that the cell is an
310 // instance of T. Thus we can safely cast it.
319 * @param <T> The type to work on
320 * @param klasses the class instance list to work on
321 * @return list of typed block
323 private <T extends BasicBlock> List<T> getAllTypedBlock(Class<T>[] klasses) {
324 final List<T> list = new ArrayList<T>();
325 for (Class<T> klass : klasses) {
326 list.addAll(getAllTypedBlock(klass));
332 * Fill the context with I/O port
334 * @param context the context to fill
335 * @param controller the shared controller
336 * @return the context
338 @SuppressWarnings("unchecked")
339 public final Map<Object, Object> fillContext(final Map<Object, Object> context, final JavaController controller) {
340 if (!context.containsKey(IN)) {
341 context.put(IN, iparSort(getAllTypedBlock(new Class[] {ExplicitInBlock.class, ImplicitInBlock.class}), controller));
343 if (!context.containsKey(OUT)) {
344 context.put(OUT, iparSort(getAllTypedBlock(new Class[] {ExplicitOutBlock.class, ImplicitOutBlock.class}), controller));
346 if (!context.containsKey(EIN)) {
347 context.put(EIN, iparSort(getAllTypedBlock(new Class[] {EventInBlock.class}), controller));
349 if (!context.containsKey(EOUT)) {
350 context.put(EOUT, iparSort(getAllTypedBlock(new Class[] {EventOutBlock.class}), controller));
357 public String validateCell(final Object cell, final Hashtable<Object, Object> context) {
358 if (getKind() == Kind.BLOCK) {
359 return validateChildDiagram(cell, context);
361 // does not perform any validation on a root diagram
367 * Validate I/O ports.
369 * /!\ No model modification should be made in this method, this is only a
372 * @param cell Cell that represents the cell to validate.
373 * @param context Hashtable that represents the global validation state.
374 * @return the error message or null
376 public String validateChildDiagram(final Object cell, final Map<Object, Object> context) {
380 * Only validate I/O blocks
384 if (cell instanceof ExplicitInBlock || cell instanceof ImplicitInBlock) {
386 } else if (cell instanceof ExplicitOutBlock || cell instanceof ImplicitOutBlock) {
388 } else if (cell instanceof EventInBlock) {
390 } else if (cell instanceof EventOutBlock) {
395 final BasicBlock block = (BasicBlock) cell;
396 final JavaController controller = new JavaController();
401 // fill the context once
402 fillContext(context, controller);
407 // get the real index
408 final List<? extends BasicBlock> blocks = (List<? extends BasicBlock>) context.get(key);
409 final int realIndex = blocks.indexOf(block) + 1;
411 // get the user index
412 VectorOfInt ipar = new VectorOfInt();
413 controller.getObjectProperty(block.getUID(), Kind.BLOCK, ObjectProperties.IPAR, ipar);
414 if (ipar.size() < 1) {
417 final int userIndex = ipar.get(0);
419 // if the indexes are not equals, alert the user.
420 if (realIndex != userIndex) {
421 final StringBuilder str = new StringBuilder();
422 str.append("<html><body><em>");
423 str.append(XcosMessages.WRONG_PORT_NUMBER);
424 str.append("</em><br/>");
425 str.append(String.format(XcosMessages.EXPECTING_NUMBER, realIndex, userIndex));
426 str.append("</body></html> ");
428 err = str.toString();
435 * Set a model property and track undo/redo operation
436 * @param cell the cell
437 * @param values the value to set
439 public void updateBlock(BasicBlock cell, Map<ObjectProperties, Object> values) {
442 blockUpdated(cell, values);
443 fireEvent(new mxEventObject(XcosEvent.UPDATE_BLOCK, XcosEvent.BLOCK, cell));
449 private void blockUpdated(BasicBlock cell, Map<ObjectProperties, Object> values) {
452 for (Map.Entry<ObjectProperties, Object> e : values.entrySet()) {
453 if (e.getValue() instanceof double[]) {
454 ((XcosGraphModel) model).setProperty(cell, e.getKey(), (double[]) e.getValue());
455 } else if (e.getValue() instanceof int[]) {
456 ((XcosGraphModel) model).setProperty(cell, e.getKey(), (int[]) e.getValue());
457 } else if (e.getValue() instanceof boolean[]) {
458 ((XcosGraphModel) model).setProperty(cell, e.getKey(), (boolean[]) e.getValue());
459 } else if (e.getValue() instanceof String[]) {
460 ((XcosGraphModel) model).setProperty(cell, e.getKey(), (String[]) e.getValue());
461 } else if (e.getValue() instanceof long[]) {
462 ((XcosGraphModel) model).setProperty(cell, e.getKey(), (long[]) e.getValue());
463 } else if (e.getValue() instanceof VectorOfDouble) {
464 ((XcosGraphModel) model).setProperty(cell, e.getKey(), (VectorOfDouble) e.getValue());
465 } else if (e.getValue() instanceof VectorOfInt) {
466 ((XcosGraphModel) model).setProperty(cell, e.getKey(), (VectorOfInt) e.getValue());
467 } else if (e.getValue() instanceof VectorOfBool) {
468 ((XcosGraphModel) model).setProperty(cell, e.getKey(), (VectorOfBool) e.getValue());
469 } else if (e.getValue() instanceof VectorOfString) {
470 ((XcosGraphModel) model).setProperty(cell, e.getKey(), (VectorOfString) e.getValue());
471 } else if (e.getValue() instanceof VectorOfScicosID) {
472 ((XcosGraphModel) model).setProperty(cell, e.getKey(), (VectorOfScicosID) e.getValue());
482 * Static diagram listeners
485 * CellResizedTracker Called when mxEvents.CELLS_RESIZED is fired.
487 private static final class RepositionTracker implements mxIEventListener {
489 private static RepositionTracker instance;
494 private RepositionTracker() {
498 * @return the instance
500 public static RepositionTracker getInstance() {
501 if (instance == null) {
502 instance = new RepositionTracker();
508 * Update the cell view
510 * @param source the source instance
511 * @param evt the event data
513 * com.mxgraph.util.mxEventSource.mxIEventListener#invoke(java.lang.Object,
514 * com.mxgraph.util.mxEventObject)
517 public void invoke(final Object source, final mxEventObject evt) {
518 final XcosDiagram diagram = (XcosDiagram) source;
519 final Object[] cells = (Object[]) evt.getProperty(CELLS);
521 diagram.getModel().beginUpdate();
523 for (int i = 0; i < cells.length; ++i) {
524 if (cells[i] instanceof BasicBlock) {
525 BlockPositioning.updateBlockView(diagram, (BasicBlock) cells[i]);
529 diagram.getModel().endUpdate();
535 * Refresh each block on modification (update port position, etc...)
537 private static final class RefreshBlockTracker implements mxIEventListener {
538 private static RefreshBlockTracker instance = new RefreshBlockTracker();
541 * Default constructor
543 private RefreshBlockTracker() {
547 * @return the instance
549 public static RefreshBlockTracker getInstance() {
554 * Refresh the block on port added
556 * @param sender the diagram
557 * @param evt the event
559 * com.mxgraph.util.mxEventSource.mxIEventListener#invoke(java.lang.Object,
560 * com.mxgraph.util.mxEventObject)
563 public void invoke(Object sender, mxEventObject evt) {
564 final XcosDiagram diagram = (XcosDiagram) sender;
566 if (diagram.isReadonly()) {
570 diagram.getModel().beginUpdate();
572 final BasicBlock updatedBlock = (BasicBlock) evt.getProperty(XcosEvent.BLOCK);
573 BlockPositioning.updateBlockView(diagram, updatedBlock);
575 diagram.getView().clear(updatedBlock, true, true);
577 // validate display errors
578 diagram.getAsComponent().clearCellOverlays();
579 diagram.getAsComponent().validateGraph();
581 diagram.getView().validate();
583 diagram.getModel().endUpdate();
589 * Update the tab title depending on the status
591 private static final class SavedStatusTracker implements mxIEventListener {
592 private static final Map<XcosDiagram, SavedStatusTracker> instances = new HashMap<>();
593 private ScicosObjectOwner owner;
595 private SavedStatusTracker(final ScicosObjectOwner o) {
599 public static SavedStatusTracker getInstance(XcosDiagram d) {
600 return instances.putIfAbsent(d, new SavedStatusTracker(Xcos.findRoot(d)));
604 public void invoke(Object sender, mxEventObject eo) {
605 if (sender instanceof XcosGraphModel) {
606 List<XcosDiagram> diagrams = Xcos.getInstance().openedDiagrams(owner);
607 diagrams.stream().forEach(d -> d.updateTabTitle());
613 * Update the number and position of ports for a Superblock
615 public static final class UpdateSuperblockPortsTracker implements mxIEventListener {
617 private static UpdateSuperblockPortsTracker instance = new UpdateSuperblockPortsTracker();
619 public static UpdateSuperblockPortsTracker getInstance() {
624 public void invoke(Object sender, mxEventObject evt) {
625 final XcosDiagram diagram = (XcosDiagram) sender;
627 if (diagram.isReadonly()) {
631 // common behavior for both block adding/removing and block update
632 List<Object> updatedIOBlocks;
633 if (XcosEvent.UPDATE_BLOCK.equals(evt.getName())) {
634 updatedIOBlocks = new ArrayList<>();
635 Object b = evt.getProperty(XcosEvent.BLOCK);
636 if (b instanceof ContextUpdate) {
637 updatedIOBlocks.add(b);
639 } else { // ADD_CELLS or REMOVE_CELLS
640 Object[] cells = (Object[]) evt.getProperty("cells");
641 updatedIOBlocks = Arrays.stream(cells).filter(b -> b instanceof ContextUpdate).collect(Collectors.toList());
643 if (updatedIOBlocks.isEmpty()) {
647 final JavaController controller = new JavaController();
649 String[] superBlockUID = {""};
650 controller.getObjectProperty(diagram.getUID(), diagram.getKind(), ObjectProperties.UID, superBlockUID);
652 List<XcosDiagram> diagrams = Xcos.getInstance().openedDiagrams(Xcos.findRoot(controller, diagram));
653 for (XcosDiagram parent : diagrams) {
654 final XcosGraphModel model = (XcosGraphModel) parent.getModel();
655 Object cell = model.getCell(superBlockUID[0]);
657 // associated parent superblock when visible
658 SuperBlock superblock = (SuperBlock) cell;
660 // create a full context
661 Map<Object, Object> context = new HashMap<>();
662 diagram.fillContext(context, controller);
664 // only update the superblock ports if all of them are valids
665 diagram.getAsComponent().clearCellOverlays();
666 String status = diagram.getAsComponent().validateGraph();
667 if (status != null) {
673 syncPorts(controller, superblock, ObjectProperties.INPUTS, (List<ContextUpdate>) context.get(IN), parent);
674 syncPorts(controller, superblock, ObjectProperties.OUTPUTS, (List<ContextUpdate>) context.get(OUT), parent);
675 syncPorts(controller, superblock, ObjectProperties.EVENT_INPUTS, (List<ContextUpdate>) context.get(EIN), parent);
676 syncPorts(controller, superblock, ObjectProperties.EVENT_OUTPUTS, (List<ContextUpdate>) context.get(EOUT), parent);
686 * Synchronize ports on the parent diagram accordingly to the updated IOBlocks
687 * @param controller shared controller;
688 * @param superblock the superblock to update
690 * @param updated the updated IOBlocks (ipar sorted)
691 * @param parent diagram containing the superblock
693 public static void syncPorts(final JavaController controller, SuperBlock superblock, ObjectProperties p, List<ContextUpdate> updated, XcosDiagram parent) {
694 VectorOfScicosID ports = new VectorOfScicosID();
695 controller.getObjectProperty(superblock.getUID(), superblock.getKind(), p, ports);
697 int added = updated.size() - ports.size();
699 for (int i = 0; i < added; i++) {
701 BasicPort port = ContextUpdate.IOBlocks.createPort(controller, updated.get(ports.size() + i));
702 parent.addCell(port, superblock);
704 } else if (added < 0) {
705 for (int i = 0; i < -added; i++) {
707 String portUID[] = {""};
708 controller.getObjectProperty(ports.get(updated.size() + i), Kind.PORT, ObjectProperties.UID, portUID);
709 parent.removeCells(new Object[] {((XcosGraphModel) parent.getModel()).getCell(portUID[0])});
715 * Append the modified blocks to an existing context
716 * @param context the validation context
717 * @param updatedIOBlocks the blocks to add
718 * @param controller shared controller
720 public static void updateContext(Map<Object, Object> context, List<XcosCell> updatedIOBlocks, final JavaController controller) {
722 List<BasicBlock> l = (List<BasicBlock>) context.getOrDefault(IN, new ArrayList<>());
723 updatedIOBlocks.stream()
724 .filter(b -> b instanceof ExplicitInBlock || b instanceof ImplicitInBlock)
725 .map(b -> (ContextUpdate) b)
726 .collect(Collectors.toCollection(() -> l));
727 iparSort(l, controller);
728 context.putIfAbsent(IN, l);
731 List<BasicBlock> l = (List<BasicBlock>) context.getOrDefault(OUT, new ArrayList<>());
732 updatedIOBlocks.stream()
733 .filter(b -> b instanceof ExplicitOutBlock || b instanceof ImplicitOutBlock)
734 .map(b -> (ContextUpdate) b)
735 .collect(Collectors.toCollection(() -> l));
736 iparSort(l, controller);
737 context.putIfAbsent(OUT, l);
740 List<BasicBlock> l = (List<BasicBlock>) context.getOrDefault(EIN, new ArrayList<>());
741 updatedIOBlocks.stream()
742 .filter(b -> b instanceof EventInBlock)
743 .map(b -> (ContextUpdate) b)
744 .collect(Collectors.toCollection(() -> l));
745 iparSort(l, controller);
746 context.putIfAbsent(EIN, l);
749 List<BasicBlock> l = (List<BasicBlock>) context.getOrDefault(EOUT, new ArrayList<>());
750 updatedIOBlocks.stream()
751 .filter(b -> b instanceof EventOutBlock)
752 .map(b -> (ContextUpdate) b)
753 .collect(Collectors.toCollection(() -> l));
754 iparSort(l, controller);
755 context.putIfAbsent(EOUT, l);
761 * Hook method that creates the new edge for insertEdge. This implementation
762 * does not set the source and target of the edge, these are set when the
763 * edge is added to the model.
765 * @param parent Cell that specifies the parent of the new edge.
766 * @param id Optional string that defines the Id of the new edge.
767 * @param value Object to be used as the user object.
768 * @param source Cell that defines the source of the edge.
769 * @param target Cell that defines the target of the edge.
770 * @param style Optional string that defines the cell style.
771 * @return Returns the new edge to be inserted.
772 * @see com.mxgraph.view.mxGraph#createEdge(java.lang.Object,
773 * java.lang.String, java.lang.Object, java.lang.Object, java.lang.Object,
777 public Object createEdge(Object parent, String id, Object value, Object source, Object target, String style) {
779 JavaController controller = new JavaController();
781 if (source instanceof BasicPort) {
782 BasicPort src = (BasicPort) source;
783 BasicLink link = null;
785 long uid = controller.createObject(Kind.LINK);
786 if (src.getType() == Type.EXPLICIT) {
787 link = new ExplicitLink(controller, uid, Kind.LINK, value, null, style, id);
788 } else if (src.getType() == Type.IMPLICIT) {
789 link = new ImplicitLink(controller, uid, Kind.LINK, value, null, style, id);
791 link = new CommandControlLink(controller, uid, Kind.LINK, value, null, style, id);
794 // allocate the associated geometry
795 link.setGeometry(new mxGeometry());
797 } else if (source instanceof SplitBlock) {
798 SplitBlock src = (SplitBlock) source;
799 return createEdge(parent, id, value, src.getIn(), target, style);
800 } else if (source instanceof BasicLink) {
801 BasicLink src = (BasicLink) source;
802 BasicLink link = null;
805 Class<? extends BasicLink> klass = src.getClass();
807 // call XXXXLink(JavaController controller, long uid, Kind kind, Object value, mxGeometry geometry, String style, String id)
808 Constructor<? extends BasicLink> cstr = klass.getConstructor(JavaController.class, Long.TYPE, Kind.class, Object.class, mxGeometry.class, String.class, String.class);
809 link = cstr.newInstance(controller, controller.createObject(Kind.LINK), src.getKind(), null, new mxGeometry(), src.getStyle(), new UID().toString());
810 } catch (ReflectiveOperationException e) {
811 LOG.severe(e.toString());
818 LOG.warning("Unable to create an edge");
825 * Add an edge from a source to the target.
827 * @param cell the edge to add (may be null)
828 * @param parent the parent of the source and the target
829 * @param source the source cell
830 * @param target the target cell
831 * @param index the index of the edge
832 * @return the added edge or null.
833 * @see com.mxgraph.view.mxGraph#addEdge(java.lang.Object, java.lang.Object,
834 * java.lang.Object, java.lang.Object, java.lang.Integer)
837 public Object addCell(Object cell, Object parent, Integer index, Object source, Object target) {
839 // already connected edge or normal block
840 if (source == null && target == null) {
841 return super.addCell(cell, parent, index, source, target);
844 // Command -> Control
845 if (source instanceof CommandPort && target instanceof ControlPort && cell instanceof CommandControlLink) {
846 return super.addCell(cell, parent, index, source, target);
849 // Control -> Command
850 // Switch source and target !
851 if (target instanceof CommandPort && source instanceof ControlPort && cell instanceof CommandControlLink) {
852 BasicLink current = (BasicLink) cell;
853 current.invertDirection();
855 return super.addCell(cell, parent, index, target, source);
858 // ExplicitOutput -> ExplicitInput
859 if (source instanceof ExplicitOutputPort && target instanceof ExplicitInputPort && cell instanceof ExplicitLink) {
860 return super.addCell(cell, parent, index, source, target);
862 // ExplicitInput -> ExplicitOutput
863 // Switch source and target !
864 if (target instanceof ExplicitOutputPort && source instanceof ExplicitInputPort && cell instanceof ExplicitLink) {
865 BasicLink current = (BasicLink) cell;
866 current.invertDirection();
868 return super.addCell(cell, parent, index, target, source);
871 // ImplicitOutput -> ImplicitInput
872 if (source instanceof ImplicitOutputPort && target instanceof ImplicitInputPort && cell instanceof ImplicitLink) {
873 return super.addCell(cell, parent, index, source, target);
875 // ImplicitInput -> ImplicitOutput
876 // Switch source and target !
877 if (target instanceof ImplicitOutputPort && source instanceof ImplicitInputPort && cell instanceof ImplicitLink) {
878 BasicLink current = (BasicLink) cell;
879 current.invertDirection();
881 return super.addCell(cell, parent, index, target, source);
884 // ImplicitInput -> ImplicitInput
885 if (source instanceof ImplicitInputPort && target instanceof ImplicitInputPort && cell instanceof ImplicitLink) {
886 return super.addCell(cell, parent, index, source, target);
888 // ImplicitOutputPort -> ImplicitOutput
889 // Switch source and target !
890 if (target instanceof ImplicitOutputPort && source instanceof ImplicitOutputPort && cell instanceof ImplicitLink) {
891 BasicLink current = (BasicLink) cell;
892 current.invertDirection();
894 return super.addCell(cell, parent, index, target, source);
900 // ExplicitLink -> ExplicitInputPort
901 if (source instanceof ExplicitLink && target instanceof ExplicitInputPort && cell instanceof ExplicitLink) {
902 SplitBlock split = addSplitEdge(((BasicLink) cell).getGeometry().getSourcePoint(), (BasicLink) source);
903 return addCell(cell, parent, index, split.getOut2(), target);
905 // ExplicitOutput -> ExpliciLink
906 // Switch source and target !
907 if (target instanceof ExplicitLink && source instanceof ExplicitInputPort && cell instanceof ExplicitLink) {
908 final BasicLink current = (BasicLink) cell;
909 final SplitBlock split = addSplitEdge(current.getGeometry().getTargetPoint(), (BasicLink) target);
911 current.invertDirection();
913 return addCell(cell, parent, index, split.getOut2(), source);
916 // ImplicitLink -> ImplicitInputPort
917 if (source instanceof ImplicitLink && target instanceof ImplicitInputPort && cell instanceof ImplicitLink) {
918 SplitBlock split = addSplitEdge(((BasicLink) cell).getGeometry().getSourcePoint(), (BasicLink) source);
919 return addCell(cell, parent, index, split.getOut2(), target);
921 // ImplicitInputPort -> ImplicitLink
922 // Switch source and target !
923 if (target instanceof ImplicitLink && source instanceof ImplicitInputPort && cell instanceof ImplicitLink) {
924 final BasicLink current = (BasicLink) cell;
925 final SplitBlock split = addSplitEdge(current.getGeometry().getTargetPoint(), (BasicLink) target);
927 current.invertDirection();
929 return addCell(cell, parent, index, split.getOut2(), source);
932 // ImplicitLink -> ImplicitOutputPort
933 if (source instanceof ImplicitLink && target instanceof ImplicitOutputPort && cell instanceof ImplicitLink) {
934 final BasicLink current = (BasicLink) cell;
935 final SplitBlock split = addSplitEdge(current.getGeometry().getTargetPoint(), (BasicLink) source);
936 return addCell(cell, parent, index, split.getOut2(), source);
938 // ImplicitOutputPort -> ImplicitLink
939 // Switch source and target !
940 if (target instanceof ImplicitLink && source instanceof ImplicitOutputPort && cell instanceof ImplicitLink) {
941 final BasicLink current = (BasicLink) cell;
942 final SplitBlock split = addSplitEdge(current.getGeometry().getTargetPoint(), (ImplicitLink) target);
943 return addCell(cell, parent, index, split.getOut2(), source);
946 // CommandControlLink -> ControlPort
947 if (source instanceof CommandControlLink && target instanceof ControlPort && cell instanceof CommandControlLink) {
948 SplitBlock split = addSplitEdge(((BasicLink) cell).getGeometry().getSourcePoint(), (BasicLink) source);
949 return addCell(cell, parent, index, split.getOut2(), target);
951 // ControlPort -> CommandControlLink
952 // Switch source and target !
953 if (target instanceof CommandControlLink && source instanceof ControlPort && cell instanceof CommandControlLink) {
954 final BasicLink current = (BasicLink) cell;
955 final SplitBlock split = addSplitEdge(current.getGeometry().getTargetPoint(), (BasicLink) target);
957 current.invertDirection();
959 return addCell(cell, parent, index, split.getOut2(), source);
962 if (cell instanceof BasicLink && source != null && target != null) {
963 LOG.severe("Unable to add a typed link");
966 LOG.severe("Adding an untyped edge");
967 return super.addCell(cell, parent, index, source, target);
972 * Add a split on a edge.
974 * @param splitPoint the split point (center of the split block)
975 * @param link source link
976 * @return split block
978 public SplitBlock addSplitEdge(final mxPoint splitPoint, final BasicLink link) {
979 final BasicPort linkSource = (BasicPort) link.getSource();
980 final BasicPort linkTarget = (BasicPort) link.getTarget();
983 * Select the right split accordingly to the link klass
985 BlockInterFunction f;
986 if (link instanceof CommandControlLink) {
987 f = BlockInterFunction.CLKSPLIT_f;
988 } else if (link instanceof ImplicitLink) {
989 f = BlockInterFunction.IMPSPLIT_f;
991 f = BlockInterFunction.SPLIT_f;
994 final SplitBlock splitBlock;
996 splitBlock = (SplitBlock) XcosCellFactory.createBlock(f);
997 } catch (InterpreterException ex) {
998 // something goes wrong
999 throw new RuntimeException(ex);
1002 // snap the center of the split block on the grid
1004 mxGeometry geom = splitBlock.getGeometry();
1005 double x = snap(splitPoint.getX()) - (SplitBlock.DEFAULT_SIZE / 2.);
1006 double y = snap(splitPoint.getY()) - (SplitBlock.DEFAULT_SIZE / 2.);
1009 splitBlock.setGeometry(geom);
1010 } catch (NullPointerException e) {
1011 e.printStackTrace();
1014 getModel().beginUpdate();
1016 // Origin of the parent, (0,0) as default may be different in case
1017 mxPoint orig = link.getParent().getGeometry();
1019 orig = new mxPoint();
1022 addCell(splitBlock);
1025 // get breaking segment and related point
1026 mxPoint splitTr = new mxPoint(splitPoint.getX() - orig.getX(), splitPoint.getY() - orig.getY());
1027 final int pos = link.findNearestSegment(splitTr);
1029 // save points after breaking point
1030 final List<mxPoint> saveStartPoints = link.getPoints(pos, true);
1031 final List<mxPoint> saveEndPoints = link.getPoints(pos, false);
1033 // remove the first end point if the position is near the split
1035 if (saveEndPoints.size() > 0) {
1036 final mxPoint p = saveEndPoints.get(0);
1037 final double dx = p.getX() - splitTr.getX();
1038 final double dy = p.getY() - splitTr.getY();
1040 if (!getAsComponent().isSignificant(dx, dy)) {
1041 saveEndPoints.remove(0);
1045 getModel().remove(link);
1046 connect(linkSource, splitBlock.getIn(), saveStartPoints, orig);
1047 connect(splitBlock.getOut1(), linkTarget, saveEndPoints, orig);
1051 getModel().endUpdate();
1058 * Connect two port together with the associated points.
1060 * This method perform the connection in two step in order to generate the
1061 * right UndoableChangeEdits.
1063 * @param src the source port
1064 * @param trg the target port
1065 * @param points the points
1066 * @param orig the origin point (may be (0,0))
1068 public void connect(BasicPort src, BasicPort trg, List<mxPoint> points, mxPoint orig) {
1069 mxGeometry geometry;
1072 * Add the link with a default geometry
1074 final Object newLink1 = createEdge(null, null, null, src, trg, null);
1075 addCell(newLink1, null, null, src, trg);
1076 geometry = getModel().getGeometry(newLink1);
1077 if (getModel().getParent(newLink1) instanceof BasicBlock) {
1078 // on a loop link, translate the points as the cell has been moved to the parent
1079 orig.setX(orig.getX() + geometry.getX());
1080 orig.setY(orig.getY() + geometry.getY());
1082 geometry.setPoints(points);
1083 getModel().setGeometry(newLink1, geometry);
1086 * Update the geometry
1088 // should be cloned to generate an event
1089 geometry = (mxGeometry) getModel().getGeometry(newLink1).clone();
1090 final double dx = orig.getX();
1091 final double dy = orig.getY();
1093 geometry.translate(dx, dy);
1094 getModel().setGeometry(newLink1, geometry);
1098 * Initialize component settings for a graph.
1100 * This method *must* be used to setup the component after any
1103 public final void initComponent() {
1104 getAsComponent().setToolTips(true);
1106 // This enable stop editing cells when pressing Enter.
1107 getAsComponent().setEnterStopsCellEditing(false);
1109 getAsComponent().setTolerance(1);
1111 getAsComponent().getViewport().setOpaque(false);
1113 getAsComponent().setBackground(XcosOptions.getEdition().getGraphBackground());
1115 final boolean gridEnable = XcosOptions.getEdition().isGraphGridEnable();
1116 setGridVisible(gridEnable);
1118 setGridSize(XcosOptions.getEdition().getGraphGrid());
1123 * Install the multiplicities (use for link checking)
1125 private void setMultiplicities() {
1126 final List<mxMultiplicity> multiplicities = new ArrayList<mxMultiplicity>();
1129 multiplicities.add(new PortCheck(ExplicitInputPort.class, Collections.unmodifiableList(new ArrayList<Class<? extends mxCell>>() {
1130 private static final long serialVersionUID = -4987163442006736665L;
1133 add(ExplicitOutputPort.class);
1134 add(ExplicitLink.class);
1136 }), XcosMessages.LINK_ERROR_EXPLICIT_IN));
1137 multiplicities.add(new PortCheck(ImplicitInputPort.class, Collections.unmodifiableList(new ArrayList<Class<? extends mxCell>>() {
1138 private static final long serialVersionUID = 886376532181210926L;
1141 add(ImplicitOutputPort.class);
1142 add(ImplicitInputPort.class);
1143 add(ImplicitLink.class);
1145 }), XcosMessages.LINK_ERROR_IMPLICIT_IN));
1148 multiplicities.add(new PortCheck(ExplicitOutputPort.class, Collections.unmodifiableList(new ArrayList<Class<? extends mxCell>>() {
1149 private static final long serialVersionUID = 4594127972486054821L;
1152 add(ExplicitInputPort.class);
1154 }), XcosMessages.LINK_ERROR_EXPLICIT_OUT));
1155 multiplicities.add(new PortCheck(ImplicitOutputPort.class, Collections.unmodifiableList(new ArrayList<Class<? extends mxCell>>() {
1156 private static final long serialVersionUID = -3719677806532507973L;
1159 add(ImplicitInputPort.class);
1160 add(ImplicitOutputPort.class);
1161 add(ImplicitLink.class);
1163 }), XcosMessages.LINK_ERROR_IMPLICIT_OUT));
1166 multiplicities.add(new PortCheck(ControlPort.class, Collections.unmodifiableList(new ArrayList<Class<? extends mxCell>>() {
1167 private static final long serialVersionUID = 2941077191386058497L;
1170 add(CommandPort.class);
1171 add(CommandControlLink.class);
1173 }), XcosMessages.LINK_ERROR_EVENT_IN));
1176 multiplicities.add(new PortCheck(CommandPort.class, Collections.unmodifiableList(new ArrayList<Class<? extends mxCell>>() {
1177 private static final long serialVersionUID = -3470370027962480362L;
1180 add(ControlPort.class);
1182 }), XcosMessages.LINK_ERROR_EVENT_OUT));
1184 // ExplicitLink connections
1185 multiplicities.add(new PortCheck(ExplicitLink.class, Collections.unmodifiableList(new ArrayList<Class<? extends mxCell>>() {
1186 private static final long serialVersionUID = 7423543162930147373L;
1189 add(ExplicitInputPort.class);
1191 }), XcosMessages.LINK_ERROR_EVENT_OUT));
1193 // ImplicitLink connections
1194 multiplicities.add(new PortCheck(ImplicitLink.class, Collections.unmodifiableList(new ArrayList<Class<? extends mxCell>>() {
1195 private static final long serialVersionUID = 7775100011122283282L;
1198 add(ImplicitInputPort.class);
1199 add(ImplicitOutputPort.class);
1201 }), XcosMessages.LINK_ERROR_EVENT_OUT));
1203 // CommandControlLink connections
1204 multiplicities.add(new PortCheck(CommandControlLink.class, Collections.unmodifiableList(new ArrayList<Class<? extends mxCell>>() {
1205 private static final long serialVersionUID = 3260421433507192386L;
1208 add(ControlPort.class);
1210 }), XcosMessages.LINK_ERROR_EVENT_OUT));
1212 // Already connected port
1213 multiplicities.add(new PortCheck(BasicPort.class, Collections.unmodifiableList(new ArrayList<Class<? extends mxCell>>() {
1214 private static final long serialVersionUID = 6376349598052836660L;
1217 add(BasicPort.class);
1219 }), XcosMessages.LINK_ERROR_ALREADY_CONNECTED));
1221 setMultiplicities(multiplicities.toArray(new mxMultiplicity[multiplicities.size()]));
1225 * Install all needed Listeners.
1227 public void installListeners() {
1229 * First remove all listeners if present
1231 getModel().removeListener(SavedStatusTracker.getInstance(this));
1232 removeListener(UpdateSuperblockPortsTracker.getInstance());
1233 removeListener(RefreshBlockTracker.getInstance());
1234 removeListener(RepositionTracker.getInstance());
1236 // Track when resizing or moving (droping) a cell.
1237 addListener(mxEvent.CELLS_RESIZED, RepositionTracker.getInstance());
1238 addListener(mxEvent.CELLS_MOVED, RepositionTracker.getInstance());
1240 // Refresh block rendering on update
1241 addListener(XcosEvent.UPDATE_BLOCK, RefreshBlockTracker.getInstance());
1243 // on Superblock diagram, refresh port interface on the parent diagram
1244 if (getKind() == Kind.BLOCK) {
1245 addListener(XcosEvent.UPDATE_BLOCK, UpdateSuperblockPortsTracker.getInstance());
1247 addListener(mxEvent.ADD_CELLS, UpdateSuperblockPortsTracker.getInstance());
1248 addListener(mxEvent.REMOVE_CELLS, UpdateSuperblockPortsTracker.getInstance());
1249 addListener(mxEvent.MOVE_CELLS, UpdateSuperblockPortsTracker.getInstance());
1252 // Update the saved status rendering on modification
1253 getModel().addListener(mxEvent.CHANGE, SavedStatusTracker.getInstance(this));
1257 * Translate the cell and align any split block
1259 * @param cell any object
1260 * @param dx the X delta
1261 * @param dy the Y delta
1264 public void translateCell(Object cell, double dx, double dy) {
1265 if (cell instanceof SplitBlock) {
1266 mxGeometry geom = model.getGeometry(cell);
1268 final double posX = snap(geom.getX() + dx) - (SplitBlock.DEFAULT_SIZE / 2.);
1269 final double posY = snap(geom.getY() + dy) - (SplitBlock.DEFAULT_SIZE / 2.);
1271 dx = posX - geom.getX();
1272 dy = posY - geom.getY();
1275 super.translateCell(cell, dx, dy);
1279 * Removes the given cells from the graph including all connected edges if
1280 * includeEdges is true. The change is carried out using cellsRemoved.
1282 * @param cells the cells to be removed
1283 * @param includeEdges true if the edges must be removed, false otherwise.
1284 * @return the deleted cells
1285 * @see com.mxgraph.view.mxGraph#removeCells(java.lang.Object[], boolean)
1288 public Object[] removeCells(final Object[] cells, final boolean includeEdges) {
1289 if (cells == null || cells.length == 0) {
1290 return super.removeCells(cells, includeEdges);
1294 * First remove all links connected to a removed Split if applicable
1296 final Object[] initialCells;
1298 initialCells = addAllEdges(cells);
1300 initialCells = cells;
1303 // stash used on the loop
1304 final Queue<Object> loopCells = new LinkedList<Object>(Arrays.asList(initialCells));
1305 // the cells that need to be really
1306 final Set<Object> removedCells = new HashSet<Object>(loopCells);
1307 // couple of cells to reconnect
1308 final List<BasicPort[]> connectedCells = new ArrayList<BasicPort[]>();
1309 final List<List<mxPoint>> connectedPoints = new ArrayList<List<mxPoint>>();
1312 * Then loop on the algorithm to select the right edges
1314 // /!\ not bounded algorithm
1315 while (loopCells.size() > 0) {
1316 Object cell = loopCells.remove();
1318 if (cell instanceof BasicLink) {
1320 * Continue on non fully connected links
1322 if (((BasicLink) cell).getSource() == null) {
1325 if (((BasicLink) cell).getTarget() == null) {
1330 * Add any split to a link
1332 addTerminalParent(((BasicLink) cell).getSource(), removedCells, loopCells);
1333 addTerminalParent(((BasicLink) cell).getTarget(), removedCells, loopCells);
1335 } else if (cell instanceof SplitBlock) {
1336 final SplitBlock splitBlock = (SplitBlock) cell;
1339 * Remove related connection or not and reconnect.
1341 if (splitBlock.getIn().getEdgeCount() == 0 || splitBlock.getOut1().getEdgeCount() == 0 || splitBlock.getOut2().getEdgeCount() == 0) {
1342 // corner case, all links will be removed
1346 final mxICell inLink = splitBlock.getIn().getEdgeAt(0);
1347 final mxICell out1Link = splitBlock.getOut1().getEdgeAt(0);
1348 final mxICell out2Link = splitBlock.getOut2().getEdgeAt(0);
1350 final boolean inRemoved = removedCells.contains(inLink);
1351 final boolean out1Removed = removedCells.contains(out1Link);
1352 final boolean out2Removed = removedCells.contains(out2Link);
1355 * Explicit case, if the in link is deleted; all the out links also should.
1357 if (inLink instanceof ExplicitLink && inRemoved) {
1358 if (removedCells.add(out1Link)) {
1359 loopCells.add(out1Link);
1362 if (removedCells.add(out2Link)) {
1363 loopCells.add(out2Link);
1368 * Global case reconnect if not removed
1370 final BasicPort[] connection;
1371 List<mxPoint> points = null;
1372 if (!inRemoved && !out1Removed && out2Removed) {
1373 connection = findTerminals(inLink, out1Link, removedCells);
1374 points = getDirectPoints(splitBlock, inLink, out1Link);
1375 } else if (!inRemoved && out1Removed && !out2Removed) {
1376 connection = findTerminals(inLink, out2Link, removedCells);
1377 points = getDirectPoints(splitBlock, inLink, out2Link);
1378 } else if (inRemoved && !out1Removed && !out2Removed) {
1379 // only implicit or event case, log otherwise
1380 if (out1Link instanceof ExplicitLink || out2Link instanceof ExplicitLink) {
1381 LOG.severe("Reconnection failed for explicit links");
1384 connection = findTerminals(out1Link, out2Link, removedCells);
1385 points = getDirectPoints(splitBlock, out1Link, out2Link);
1391 if (connection != null) {
1392 connectedCells.add(connection);
1393 connectedPoints.add(points);
1399 getModel().beginUpdate();
1401 ret = super.removeCells(removedCells.toArray(), includeEdges);
1402 for (int i = 0; i < connectedCells.size(); i++) {
1403 final BasicPort[] connection = connectedCells.get(i);
1404 final List<mxPoint> points = connectedPoints.get(i);
1405 if (!removedCells.contains(connection[0].getParent()) && !removedCells.contains(connection[1].getParent())) {
1406 connect(connection[0], connection[1], points, new mxPoint());
1410 getModel().endUpdate();
1416 * Add any terminal parent to the removed cells
1418 * @param terminal the current terminal (instance of BasicPort)
1419 * @param removedCells the "to be removed" set
1420 * @param loopCells the "while loop" set
1422 private void addTerminalParent(mxICell terminal, Collection<Object> removedCells, Collection<Object> loopCells) {
1423 assert (terminal == null || terminal instanceof BasicPort);
1424 assert (removedCells != null);
1425 assert (loopCells != null);
1427 // getting terminal parent
1428 mxICell target = null;
1429 if (terminal != null) {
1430 target = terminal.getParent();
1435 // add target if applicable
1436 if (target instanceof SplitBlock) {
1437 if (removedCells.add(target)) {
1438 loopCells.add(target);
1444 * Find the terminals when relinking the 2 links
1446 * This method ensure that {source, target} are not child of removed blocks.
1448 * @param linkSource the normal source link
1449 * @param linkTerminal the normal target link
1450 * @param removedCells the set of removed objects
1451 * @return the {source, target} connection
1453 private BasicPort[] findTerminals(final mxICell linkSource, final mxICell linkTerminal, final Set<Object> removedCells) {
1454 BasicPort src = (BasicPort) linkSource.getTerminal(true);
1455 BasicPort tgt = (BasicPort) linkTerminal.getTerminal(false);
1456 if (linkSource instanceof ImplicitLink) {
1457 if (removedCells.contains(src.getParent())) {
1458 src = (BasicPort) linkSource.getTerminal(false);
1460 if (removedCells.contains(tgt.getParent())) {
1461 tgt = (BasicPort) linkTerminal.getTerminal(true);
1465 return new BasicPort[] {src, tgt};
1469 * Get the direct points from inLink.getSource() to outLink.getTarget().
1471 * @param splitBlock the current splitblock (added as a mid-point)
1472 * @param inLink the link before the split
1473 * @param outLink the link after the split
1474 * @return the points
1476 private List<mxPoint> getDirectPoints(final SplitBlock splitBlock, final mxICell inLink, final mxICell outLink) {
1477 List<mxPoint> points;
1478 // add the points before the split
1479 points = new ArrayList<mxPoint>();
1480 if (inLink.getGeometry().getPoints() != null) {
1481 points.addAll(inLink.getGeometry().getPoints());
1484 // add a new point at the split location
1485 points.add(new mxPoint(snap(splitBlock.getGeometry().getCenterX()), snap(splitBlock.getGeometry().getCenterY())));
1487 // add the points after the split
1488 if (outLink.getGeometry().getPoints() != null) {
1489 points.addAll(outLink.getGeometry().getPoints());
1496 * Manage Group to be CellFoldable i.e with a (-) to reduce and a (+) to
1497 * expand them. Labels (mxCell instance with value) should not have a
1498 * visible foldable sign.
1500 * @param cell the selected cell
1501 * @param collapse the collapse settings
1502 * @return always <code>false</code>
1503 * @see com.mxgraph.view.mxGraph#isCellFoldable(java.lang.Object, boolean)
1506 public boolean isCellFoldable(final Object cell, final boolean collapse) {
1511 * Not BasicBLock cell have a moveable label.
1513 * @param cell the cell
1514 * @return true if the corresponding label is moveable
1515 * @see com.mxgraph.view.mxGraph#isLabelMovable(java.lang.Object)
1518 public boolean isLabelMovable(Object cell) {
1519 return !(cell instanceof BasicBlock);
1523 * Return true if selectable
1525 * @param cell the cell
1527 * @see com.mxgraph.view.mxGraph#isCellSelectable(java.lang.Object)
1530 public boolean isCellSelectable(final Object cell) {
1531 if (cell instanceof BasicPort) {
1534 return super.isCellSelectable(cell);
1538 * Return true if movable
1540 * @param cell the cell
1542 * @see com.mxgraph.view.mxGraph#isCellMovable(java.lang.Object)
1545 public boolean isCellMovable(final Object cell) {
1546 if (cell instanceof BasicPort) {
1550 boolean movable = false;
1551 final Object[] cells = getSelectionCells();
1553 // don't move if selection is only links
1554 for (Object c : cells) {
1555 if (!(c instanceof BasicLink)) {
1561 return movable && super.isCellMovable(cell);
1565 * Return true if resizable
1567 * @param cell the cell
1569 * @see com.mxgraph.view.mxGraph#isCellResizable(java.lang.Object)
1572 public boolean isCellResizable(final Object cell) {
1573 if (cell instanceof SplitBlock) {
1576 return (cell instanceof BasicBlock) && super.isCellResizable(cell);
1580 * A cell is deletable if it is not a locked block or an identifier cell
1582 * @param cell the cell
1584 * @see com.mxgraph.view.mxGraph#isCellDeletable(java.lang.Object)
1587 public boolean isCellDeletable(final Object cell) {
1588 final boolean isALockedBLock = cell instanceof BasicBlock && ((BasicBlock) cell).isLocked();
1589 final boolean isAnIdentifier = cell.getClass().equals(mxCell.class);
1591 if (isALockedBLock) {
1594 if (isAnIdentifier) {
1598 return super.isCellDeletable(cell);
1602 * Return true if editable
1604 * @param cell the cell
1606 * @see com.mxgraph.view.mxGraph#isCellEditable(java.lang.Object)
1609 public boolean isCellEditable(final Object cell) {
1610 return (cell instanceof TextBlock) && super.isCellDeletable(cell);
1614 * Return or create the identifier for the cell
1616 * @param cell the cell to check
1617 * @return the identifier cell
1619 public mxCell getOrCreateCellIdentifier(final mxCell cell) {
1620 if (cell.getId().endsWith(HASH_IDENTIFIER)) {
1624 final mxGraphModel graphModel = (mxGraphModel) getModel();
1626 final mxCell identifier;
1627 final String cellId = cell.getId() + HASH_IDENTIFIER;
1628 if (graphModel.getCell(cellId) == null) {
1629 // create the identifier
1630 identifier = createCellIdentifier(cell);
1632 // add the identifier
1633 graphModel.add(cell, identifier, cell.getChildCount());
1635 identifier = (mxCell) graphModel.getCell(cellId);
1641 * Return the identifier for the cell
1643 * @param cell the cell to check
1644 * @return the identifier cell
1646 public mxCell getCellIdentifier(final mxCell cell) {
1647 final mxGraphModel graphModel = (mxGraphModel) getModel();
1648 final String cellId = cell.getId() + HASH_IDENTIFIER;
1650 return (mxCell) graphModel.getCell(cellId);
1654 * Create a cell identifier for a specific cell
1656 * @param cell the cell
1657 * @return the cell identifier.
1659 public mxCell createCellIdentifier(final mxCell cell) {
1660 final XcosCell identifier;
1661 final String cellId = cell.getId() + HASH_IDENTIFIER;
1663 JavaController controller = new JavaController();
1664 long uid = controller.createObject(Kind.ANNOTATION);
1666 final mxGeometry geom;
1667 if (cell.isVertex()) {
1668 // the vertex label position is relative to its parent
1669 geom = new mxGeometry(cell.getGeometry().getWidth() * 0.5, cell.getGeometry().getHeight() * 1.1, 0., 0.);
1671 // the edge label position is absolute, its initial position should be computed
1672 mxPoint src = cell.getGeometry().getSourcePoint();
1673 mxPoint trgt = cell.getGeometry().getTargetPoint();
1675 geom = new mxGeometry(src.getX(), src.getY(), 0., 0.);
1676 geom.translate((trgt.getX() - src.getX()) / 2, (trgt.getY() - src.getY()) / 2);
1679 identifier = new XcosCell(new JavaController(), uid, Kind.ANNOTATION, null, geom, "noLabel=0;opacity=0;", cellId);
1680 identifier.setVertex(true);
1681 identifier.setConnectable(false);
1687 * Get the label for the cell according to its style.
1689 * @param cell the cell object
1690 * @return a representative the string (block name) or a style specific
1692 * @see com.mxgraph.view.mxGraph#convertValueToString(java.lang.Object)
1695 public String convertValueToString(Object cell) {
1699 JavaController controller = new JavaController();
1700 final Map<String, Object> style = getCellStyle(cell);
1702 final String displayedLabel = (String) style.get("displayedLabel");
1703 if (displayedLabel != null) {
1704 if (cell instanceof BasicBlock) {
1705 BasicBlock block = (BasicBlock) cell;
1706 VectorOfDouble v = new VectorOfDouble();
1707 controller.getObjectProperty(block.getUID(), block.getKind(), ObjectProperties.EXPRS, v);
1710 ret = new ScilabTypeCoder().format(displayedLabel, v);
1711 } catch (IllegalFormatException e) {
1712 LOG.severe(e.toString());
1713 ret = displayedLabel;
1716 ret = displayedLabel;
1719 final String label = super.convertValueToString(cell);
1720 if (label.isEmpty() && cell instanceof BasicBlock) {
1721 BasicBlock block = (BasicBlock) cell;
1723 String[] interfaceFunction = new String[1];
1724 controller.getObjectProperty(block.getUID(), block.getKind(), ObjectProperties.INTERFACE_FUNCTION, interfaceFunction);
1725 ret = interfaceFunction[0];
1736 * Return true if auto sized
1738 * @param cell the cell
1740 * @see com.mxgraph.view.mxGraph#isAutoSizeCell(java.lang.Object)
1743 public boolean isAutoSizeCell(final Object cell) {
1744 boolean status = super.isAutoSizeCell(cell);
1746 if (cell instanceof AfficheBlock) {
1750 if (cell instanceof TextBlock) {
1758 * {@inheritDoc} Do not extends if the port position is north or south.
1761 public boolean isExtendParent(Object cell) {
1762 final boolean extendsParents;
1764 if (cell instanceof BasicPort) {
1765 final BasicPort p = (BasicPort) cell;
1766 extendsParents = !(p.getOrientation() == Orientation.NORTH || p.getOrientation() == Orientation.SOUTH) && super.isExtendParent(p);
1768 extendsParents = super.isExtendParent(cell);
1770 return extendsParents;
1774 * @return the model ID
1776 public long getUID() {
1777 return ((XcosCell) getDefaultParent()).getUID();
1781 * @return Kind.DIAGRAM or Kind.BLOCK
1783 public Kind getKind() {
1784 return ((XcosCell) getDefaultParent()).getKind();
1788 * Manage the visibility of the grid and the associated menu
1790 * @param status new status
1792 public void setGridVisible(final boolean status) {
1793 setGridEnabled(status);
1794 getAsComponent().setGridVisible(status);
1795 getAsComponent().repaint();
1799 * @return save status
1801 public boolean saveDiagram() {
1802 return saveDiagramAs(getSavedFile());
1806 * @param fileName diagram filename
1807 * @return save status
1809 public boolean saveDiagramAs(final File fileName) {
1810 boolean isSuccess = false;
1811 File writeFile = fileName;
1812 XcosFileType format = XcosOptions.getPreferences().getFileFormat();
1814 info(XcosMessages.SAVING_DIAGRAM);
1815 if (fileName == null) {
1816 final SwingScilabFileChooser fc = SaveAsAction.createFileChooser();
1817 SaveAsAction.configureFileFilters(fc);
1818 ConfigurationManager.configureCurrentDirectory(fc);
1820 if (getSavedFile() != null) {
1821 // using save-as, the file chooser should have a filename
1822 // without extension as default
1823 String filename = getSavedFile().getName();
1824 filename = filename.substring(0, filename.lastIndexOf('.'));
1825 fc.setSelectedFile(new File(filename));
1827 final String title = getTitle();
1828 if (title != null) {
1830 * Escape file to handle not supported character in file name (may be Windows only) see http://msdn.microsoft.com/en -us/library/windows/desktop/aa365247%28v=vs.85%29.aspx
1832 final char[] regex = "<>:\"/\\|?*".toCharArray();
1833 String escaped = title;
1834 for (char c : regex) {
1835 escaped = escaped.replace(c, '-');
1838 fc.setSelectedFile(new File(escaped));
1842 int status = fc.showSaveDialog(this.getAsComponent());
1843 if (status != JFileChooser.APPROVE_OPTION) {
1844 info(XcosMessages.EMPTY_INFO);
1848 // change the format if the user choose a save-able file format
1849 final XcosFileType selectedFilter = XcosFileType.findFileType(fc.getFileFilter());
1850 if (XcosFileType.getAvailableSaveFormats().contains(selectedFilter)) {
1851 format = selectedFilter;
1853 writeFile = fc.getSelectedFile();
1856 /* Extension/format update */
1857 // using a String filename also works on a non-existing file
1858 final String filename = writeFile.getName();
1861 * Look for the user extension if it does not exist, append a default one
1863 * if the specified extension is handled, update the save format ; else append a default extension and use the default format
1865 XcosFileType userExtension = XcosFileType.findFileType(filename);
1866 if (userExtension == null) {
1867 writeFile = new File(writeFile.getParent(), filename + format.getDottedExtension());
1868 userExtension = format;
1870 if (XcosFileType.getAvailableSaveFormats().contains(userExtension)) {
1871 format = userExtension;
1873 writeFile = new File(writeFile.getParent(), filename + format.getDottedExtension());
1877 * If the file exists, ask for confirmation if this is not the previously saved file
1879 if (writeFile.exists() && !writeFile.equals(getSavedFile())) {
1880 final boolean overwrite = ScilabModalDialog.show(XcosTab.get(this), XcosMessages.OVERWRITE_EXISTING_FILE, XcosMessages.XCOS, IconType.QUESTION_ICON,
1881 ButtonType.YES_NO) == AnswerOption.YES_OPTION;
1884 info(XcosMessages.EMPTY_INFO);
1890 * Really save the data
1893 format.save(writeFile.getCanonicalPath(), getRootDiagram());
1894 setSavedFile(writeFile);
1896 setTitle(writeFile.getName().substring(0, writeFile.getName().lastIndexOf('.')));
1897 ConfigurationManager.getInstance().addToRecentFiles(writeFile);
1899 ScicosObjectOwner root = Xcos.findRoot(this);
1900 Xcos.getInstance().setModified(root, false);
1901 Xcos.getInstance().openedDiagrams(root).stream()
1902 .forEach(d -> d.updateTabTitle());
1905 } catch (final Exception e) {
1906 LOG.severe(e.toString());
1908 XcosDialogs.couldNotSaveFile(this);
1912 info(XcosMessages.EMPTY_INFO);
1917 * Perform post loading initialization.
1919 * @param file the loaded file
1921 public void postLoad(final File file) {
1922 final String name = file.getName();
1924 if (XcosFileType.getAvailableSaveFormats().contains(XcosFileType.findFileType(file))) {
1927 setTitle(name.substring(0, name.lastIndexOf('.')));
1931 fireEvent(new mxEventObject(mxEvent.ROOT));
1933 info(XcosMessages.EMPTY_INFO);
1937 public void setSavedFile(File savedFile) {
1938 super.setSavedFile(savedFile);
1940 if (savedFile != null) {
1941 JavaController controller = new JavaController();
1942 controller.setObjectProperty(getUID(), getKind(), ObjectProperties.PATH, savedFile.getAbsolutePath());
1947 * @return the title of the current diagram (root diagram or super block)
1950 public String getTitle() {
1951 JavaController controller = new JavaController();
1952 String[] property = {""};
1954 if (getKind() == Kind.DIAGRAM) {
1955 controller.getObjectProperty(getUID(), getKind(), ObjectProperties.TITLE, property);
1956 } else { // Kind.BLOCK
1957 // use the one-line description
1958 controller.getObjectProperty(getUID(), getKind(), ObjectProperties.DESCRIPTION, property);
1961 if (property[0].isEmpty()) {
1962 return super.getTitle();
1969 * Set the title of the diagram
1971 * @param title the title
1972 * @see org.scilab.modules.graph.ScilabGraph#setTitle(java.lang.String)
1975 public void setTitle(final String title) {
1976 super.setTitle(title);
1978 JavaController controller = new JavaController();
1979 controller.setObjectProperty(getUID(), getKind(), ObjectProperties.TITLE, title);
1985 public void updateTabTitle() {
1986 // get the modifier string
1987 final String modified;
1988 if (Xcos.getInstance().isModified(Xcos.findRoot(this))) {
1994 // get the title string
1995 final String title = getTitle();
1998 CharSequence formattedPath = "";
1999 if (getKind() == Kind.DIAGRAM) {
2000 final File savedFile = getSavedFile();
2001 if (savedFile != null) {
2003 final String path = savedFile.getCanonicalPath();
2004 formattedPath = new StringBuilder().append(" (").append(path).append(')');
2005 } catch (final IOException e) {
2006 LOG.warning(e.toString());
2012 final String product = Xcos.TRADENAME;
2014 final String tabTitle = new StringBuilder().append(modified).append(title).append(formattedPath).append(" - ").append(product).toString();
2016 final SwingScilabDockablePanel tab = ScilabTabFactory.getInstance().getFromCache(getGraphTab());
2018 tab.setName(tabTitle);
2023 * Load a file with different method depending on it extension
2025 * @param controller the used controller
2026 * @param file File to load (can be null)
2028 public void transformAndLoadFile(final JavaController controller, final String file) {
2030 final XcosFileType filetype;
2033 filetype = XcosFileType.findFileType(f);
2038 new SwingWorker<XcosDiagram, ActionEvent>() {
2040 final Timer t = new Timer(1000, new ActionListener() {
2042 public void actionPerformed(ActionEvent e) {
2043 counter = (counter + 1) % (XcosMessages.DOTS.length() + 1);
2044 String str = XcosMessages.LOADING_DIAGRAM + XcosMessages.DOTS.substring(0, counter);
2046 XcosDiagram.this.info(str);
2051 protected XcosDiagram doInBackground() {
2054 final Xcos instance = Xcos.getInstance();
2055 XcosDiagram diag = XcosDiagram.this;
2057 diag.setReadOnly(true);
2060 * Load, log errors and notify
2062 synchronized (instance) {
2065 if (f != null && filetype != null) {
2066 filetype.load(file, XcosDiagram.this);
2068 XcosCellFactory.insertChildren(controller, XcosDiagram.this);
2071 instance.setLastError("");
2072 } catch (Exception e) {
2073 e.printStackTrace();
2076 while (ex instanceof RuntimeException) {
2080 instance.setLastError(ex.getMessage());
2081 } catch (NullPointerException exp) {
2082 exp.printStackTrace();
2092 protected void done() {
2094 XcosDiagram.this.setReadOnly(false);
2095 XcosDiagram.this.getUndoManager().clear();
2096 XcosDiagram.this.refresh();
2101 if (f != null && filetype != null) {
2104 XcosDiagram.this.info(XcosMessages.EMPTY_INFO);
2111 * Getting the root diagram of a decomposed diagram
2113 * @return Root parent of the whole parent
2115 public XcosDiagram getRootDiagram() {
2116 if (getKind() == Kind.DIAGRAM) {
2120 JavaController controller = new JavaController();
2122 ScicosObjectOwner root = Xcos.findRoot(controller, this);
2123 Collection<XcosDiagram> diagrams = Xcos.getInstance().getDiagrams(root);
2124 Optional<XcosDiagram> found = diagrams.stream().filter(d -> d.getUID() == root.getUID()).findFirst();
2125 if (found.isPresent()) {
2128 // create a temporary hidden root diagram
2129 String[] uid = {""};
2130 controller.getObjectProperty(root.getUID(), Kind.DIAGRAM, ObjectProperties.UID, uid);
2131 return new XcosDiagram(controller, root.getUID(), Kind.DIAGRAM, uid[0]);
2138 * Getting the root diagram UID of a decomposed diagram
2140 * @param controller the current JavaController
2141 * @return Root parent of the whole parent
2143 public long getRootDiagramUID(JavaController controller) {
2144 if (getKind() == Kind.DIAGRAM) {
2148 long[] uid = new long[1];
2149 controller.getObjectProperty(getUID(), getKind(), ObjectProperties.PARENT_DIAGRAM, uid);
2156 * Returns the tooltip to be used for the given cell.
2159 * @return cell tooltip
2162 public String getToolTipForCell(final Object cell) {
2163 if (cell instanceof BasicBlock) {
2164 return getToolTipForCell((BasicBlock) cell);
2165 } else if (cell instanceof BasicPort) {
2166 return getToolTipForCell((BasicPort) cell);
2167 } else if (cell instanceof BasicLink) {
2168 return getToolTipForCell((BasicLink) cell);
2173 private String getToolTipForCell(final BasicBlock o) {
2174 JavaController controller = new JavaController();
2175 String[] strValue = {""};
2176 VectorOfDouble vecValue = new VectorOfDouble();
2177 VectorOfInt vecInteger = new VectorOfInt();
2179 StringBuilder result = new StringBuilder();
2180 result.append(ScilabGraphConstants.HTML_BEGIN);
2182 controller.getObjectProperty(o.getUID(), o.getKind(), ObjectProperties.INTERFACE_FUNCTION, strValue);
2183 result.append(XcosMessages.TOOLTIP_BLOCK).append(ScilabGraphConstants.HTML_BEGIN_CODE)
2184 .append(strValue[0])
2185 .append(ScilabGraphConstants.HTML_END_CODE).append(ScilabGraphConstants.HTML_NEWLINE);
2187 controller.getObjectProperty(o.getUID(), o.getKind(), ObjectProperties.SIM_FUNCTION_NAME, strValue);
2188 result.append(XcosMessages.TOOLTIP_BLOCK_SIMULATION).append(ScilabGraphConstants.HTML_BEGIN_CODE)
2189 .append(strValue[0])
2190 .append(ScilabGraphConstants.HTML_END_CODE).append(ScilabGraphConstants.HTML_NEWLINE);
2192 controller.getObjectProperty(o.getUID(), o.getKind(), ObjectProperties.UID, strValue);
2193 result.append(XcosMessages.TOOLTIP_BLOCK_UID).append(ScilabGraphConstants.HTML_BEGIN_CODE)
2194 .append(strValue[0])
2195 .append(ScilabGraphConstants.HTML_END_CODE).append(ScilabGraphConstants.HTML_NEWLINE);
2197 controller.getObjectProperty(o.getUID(), o.getKind(), ObjectProperties.STYLE, strValue);
2198 result.append(XcosMessages.TOOLTIP_BLOCK_STYLE).append(ScilabGraphConstants.HTML_BEGIN_CODE);
2199 appendReduced(result, strValue[0])
2200 .append(ScilabGraphConstants.HTML_END_CODE).append(ScilabGraphConstants.HTML_NEWLINE);
2202 result.append(ScilabGraphConstants.HTML_NEWLINE);
2204 controller.getObjectProperty(o.getUID(), o.getKind(), ObjectProperties.RPAR, vecValue);
2205 result.append(XcosMessages.TOOLTIP_BLOCK_RPAR).append(ScilabGraphConstants.HTML_BEGIN_CODE);
2206 appendReduced(result, ScilabTypeCoder.toString(vecValue))
2207 .append(ScilabGraphConstants.HTML_END_CODE).append(ScilabGraphConstants.HTML_NEWLINE);
2209 controller.getObjectProperty(o.getUID(), o.getKind(), ObjectProperties.IPAR, vecInteger);
2210 result.append(XcosMessages.TOOLTIP_BLOCK_IPAR).append(ScilabGraphConstants.HTML_BEGIN_CODE);
2211 appendReduced(result, ScilabTypeCoder.toString(vecInteger))
2212 .append(ScilabGraphConstants.HTML_END_CODE).append(ScilabGraphConstants.HTML_NEWLINE);
2214 controller.getObjectProperty(o.getUID(), o.getKind(), ObjectProperties.OPAR, vecValue);
2215 result.append(XcosMessages.TOOLTIP_BLOCK_OPAR).append(ScilabGraphConstants.HTML_BEGIN_CODE);
2216 appendReduced(result, ScilabTypeCoder.toString(vecValue))
2217 .append(ScilabGraphConstants.HTML_END_CODE).append(ScilabGraphConstants.HTML_NEWLINE);
2219 result.append(ScilabGraphConstants.HTML_END);
2220 return result.toString();
2223 private String getToolTipForCell(final BasicPort o) {
2224 JavaController controller = new JavaController();
2225 boolean[] boolValue = {false};
2226 String[] strValue = {""};
2227 VectorOfInt intVecValue = new VectorOfInt();
2229 StringBuilder result = new StringBuilder();
2230 result.append(ScilabGraphConstants.HTML_BEGIN);
2232 controller.getObjectProperty(o.getUID(), o.getKind(), ObjectProperties.DATATYPE, intVecValue);
2233 result.append(XcosMessages.TOOLTIP_PORT_DATATYPE).append(ScilabGraphConstants.HTML_BEGIN_CODE);
2234 formatDatatype(result, intVecValue)
2235 .append(ScilabGraphConstants.HTML_END_CODE).append(ScilabGraphConstants.HTML_NEWLINE);
2237 controller.getObjectProperty(o.getUID(), o.getKind(), ObjectProperties.IMPLICIT, boolValue);
2238 result.append(XcosMessages.TOOLTIP_PORT_IMPLICIT).append(ScilabGraphConstants.HTML_BEGIN_CODE)
2239 .append(boolValue[0])
2240 .append(ScilabGraphConstants.HTML_END_CODE).append(ScilabGraphConstants.HTML_NEWLINE);
2242 controller.getObjectProperty(o.getUID(), o.getKind(), ObjectProperties.STYLE, strValue);
2243 result.append(XcosMessages.TOOLTIP_PORT_STYLE).append(ScilabGraphConstants.HTML_BEGIN_CODE);
2244 appendReduced(result, strValue[0])
2245 .append(ScilabGraphConstants.HTML_END_CODE).append(ScilabGraphConstants.HTML_NEWLINE);
2247 result.append(ScilabGraphConstants.HTML_END);
2248 return result.toString();
2251 private String getToolTipForCell(final BasicLink o) {
2252 JavaController controller = new JavaController();
2253 long[] longValue = {0l};
2254 String[] strValue = {""};
2255 VectorOfInt intVecValue = new VectorOfInt();
2257 StringBuilder result = new StringBuilder();
2258 result.append(ScilabGraphConstants.HTML_BEGIN);
2260 controller.getObjectProperty(o.getUID(), o.getKind(), ObjectProperties.SOURCE_PORT, longValue);
2261 if (longValue[0] != 0l) {
2262 controller.getObjectProperty(longValue[0], Kind.PORT, ObjectProperties.DATATYPE, intVecValue);
2263 result.append(XcosMessages.TOOLTIP_LINK_SRC_DATATYPE).append(ScilabGraphConstants.HTML_BEGIN_CODE);
2264 formatDatatype(result, intVecValue)
2265 .append(ScilabGraphConstants.HTML_END_CODE).append(ScilabGraphConstants.HTML_NEWLINE);
2267 controller.getObjectProperty(o.getUID(), o.getKind(), ObjectProperties.DESTINATION_PORT, longValue);
2268 if (longValue[0] != 0l) {
2269 controller.getObjectProperty(longValue[0], Kind.PORT, ObjectProperties.DATATYPE, intVecValue);
2270 result.append(XcosMessages.TOOLTIP_LINK_TRG_DATATYPE).append(ScilabGraphConstants.HTML_BEGIN_CODE);
2271 formatDatatype(result, intVecValue)
2272 .append(ScilabGraphConstants.HTML_END_CODE).append(ScilabGraphConstants.HTML_NEWLINE);
2275 controller.getObjectProperty(o.getUID(), o.getKind(), ObjectProperties.LABEL, strValue);
2276 result.append(XcosMessages.TOOLTIP_LINK_LABEL).append(ScilabGraphConstants.HTML_BEGIN_CODE)
2277 .append(strValue[0])
2278 .append(ScilabGraphConstants.HTML_END_CODE).append(ScilabGraphConstants.HTML_NEWLINE);
2280 controller.getObjectProperty(o.getUID(), o.getKind(), ObjectProperties.STYLE, strValue);
2281 result.append(XcosMessages.TOOLTIP_LINK_STYLE).append(ScilabGraphConstants.HTML_BEGIN_CODE);
2282 appendReduced(result, strValue[0])
2283 .append(ScilabGraphConstants.HTML_END_CODE).append(ScilabGraphConstants.HTML_NEWLINE);
2285 result.append(ScilabGraphConstants.HTML_END);
2286 return result.toString();
2289 private StringBuilder appendReduced(final StringBuilder result, final String msg) {
2290 if (msg.length() > XcosConstants.MAX_CHAR_IN_STYLE) {
2291 result.append(msg.substring(0, XcosConstants.MAX_CHAR_IN_STYLE));
2292 result.append(XcosMessages.DOTS);
2300 private StringBuilder formatDatatype(final StringBuilder result, final VectorOfInt intVecValue) {
2301 if (intVecValue.size() != 3) {
2302 result.append(ScilabTypeCoder.toString(intVecValue));
2304 // this is a known encoding, output representative strings
2305 int rows = intVecValue.get(0);
2306 int cols = intVecValue.get(1);
2307 int type = intVecValue.get(2);
2310 // should be similar to the naming used on scicos_model doc
2311 String[] typeTable = {"real", "complex", "int32", "int16", "int8", "uint32", "uint16", "uint8"};
2312 if (0 <= type && type < typeTable.length) {
2313 strType = typeTable[type - 1];
2318 result.append(String.format("%s [%d %d]", strType, rows, cols));
2325 * Display the message in info bar.
2327 * @param message Information
2329 public void info(final String message) {
2330 final XcosTab tab = XcosTab.get(this);
2332 if (tab != null && tab.getInfoBar() != null) {
2333 tab.getInfoBar().setText(message);
2338 * Display the message into an error popup
2340 * @param message Error of the message
2342 public void error(final String message) {
2343 JOptionPane.showMessageDialog(getAsComponent(), message, XcosMessages.XCOS, JOptionPane.ERROR_MESSAGE);
2347 * Find the block corresponding to the given uid and display a warning
2350 * @param uid - A String as UID.
2351 * @param message - The message to display.
2353 public void warnCellByUID(final String uid, final String message) {
2354 final Object cell = ((mxGraphModel) getModel()).getCell(uid);
2359 if (GraphicsEnvironment.isHeadless()) {
2360 System.err.printf("%s at %s%n %s: %s%n", "warnCell", getRootDiagram().getTitle(), uid, message);
2365 if (XcosTab.get(this) == null) {
2366 XcosTab.restore(this);
2369 if (message.isEmpty()) {
2370 getAsComponent().clearCellOverlays(cell);
2372 getAsComponent().setCellWarning(cell, message, null, true);
2377 public File getSavedFile() {
2378 if (getKind() == Kind.DIAGRAM) {
2379 return super.getSavedFile();
2381 return getRootDiagram().getSavedFile();
2386 * Read the applicable context on this diagram.
2388 * This function retrieve the current diagram's context and all its parent
2390 * @return the full context
2392 public String[] getContext() {
2393 final ArrayList<String> allContext = new ArrayList<>();
2394 final Stack<ScicosObjectOwner> hierarchy = lookForHierarchy(new ScicosObjectOwner(getUID(), getKind()));
2396 final JavaController controller = new JavaController();
2397 final VectorOfString context = new VectorOfString();
2399 hierarchy.stream().forEach(o -> {
2400 controller.getObjectProperty(o.getUID(), o.getKind(), ObjectProperties.DIAGRAM_CONTEXT, context);
2402 final int length = context.size();
2403 for (int i = 0; i < length; i++) {
2404 allContext.add(context.get(i));
2409 return allContext.toArray(new String[allContext.size()]);
2413 * Evaluate the current context
2415 * @return The resulting data. Keys are variable names and Values are
2418 public Map<String, ScilabType> evaluateContext() {
2419 Map<String, ScilabType> result = Collections.emptyMap();
2420 final ScilabDirectHandler handler = ScilabDirectHandler.acquire();
2421 if (handler == null) {
2426 // first write the context strings
2427 handler.writeContext(getContext());
2429 // evaluate using script2var and convert to string keys and list of values
2430 ScilabInterpreterManagement.synchronousScilabExec(ScilabDirectHandler.CONTEXT + " = script2var(" + ScilabDirectHandler.CONTEXT + ", struct()); "
2431 + ScilabDirectHandler.CONTEXT + "_names = fieldnames("+ScilabDirectHandler.CONTEXT+")'; "
2432 + ScilabDirectHandler.CONTEXT + "_values = list(); "
2433 + "for i=1:size(" + ScilabDirectHandler.CONTEXT + "_names, '*') ;"
2434 + " " + ScilabDirectHandler.CONTEXT + "_values(i) = " + ScilabDirectHandler.CONTEXT + "(" + ScilabDirectHandler.CONTEXT + "_names(i));"
2437 // read the structure
2438 result = handler.readContext();
2439 } catch (final InterpreterException e) {
2440 info("Unable to evaluate the context");
2441 e.printStackTrace();
2450 * Returns true if the given cell is a not a block nor a port.
2452 * @param cell the drop target
2453 * @param cells the cells to be dropped
2454 * @return the drop status
2455 * @see com.mxgraph.view.mxGraph#isValidDropTarget(java.lang.Object,
2456 * java.lang.Object[])
2459 public boolean isValidDropTarget(final Object cell, final Object[] cells) {
2460 return !(cell instanceof BasicBlock) && !(cell instanceof BasicBlock) && !(cell instanceof BasicPort) && super.isValidDropTarget(cell, cells);
2464 * Construct a new selection model used on this graph.
2466 * @return a new selection model instance.
2467 * @see com.mxgraph.view.mxGraph#createSelectionModel()
2470 protected mxGraphSelectionModel createSelectionModel() {
2471 return new mxGraphSelectionModel(this) {
2473 * When we only want to select a cell which is a port, select the
2476 * @param cell the cell
2478 * com.mxgraph.view.mxGraphSelectionModel#setCell(java.lang.Object)
2481 public void setCell(final Object cell) {
2482 final Object current;
2483 if (cell instanceof BasicPort) {
2484 current = getModel().getParent(cell);
2488 super.setCell(current);
2494 * Counts the number of children in the diagram
2496 * @return the number of children in the diagram
2498 public int countChildren() {
2499 return getModel().getChildCount(this.getDefaultParent());