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.xcos.VectorOfBool;
121 import org.scilab.modules.xcos.VectorOfScicosID;
122 import org.scilab.modules.xcos.block.SuperBlock;
123 import org.scilab.modules.xcos.block.io.ContextUpdate;
124 import org.scilab.modules.xcos.io.ScilabTypeCoder;
125 import org.scilab.modules.xcos.utils.XcosEvent;
128 * The base class for a diagram. This class contains jgraphx + Scicos data.
130 public class XcosDiagram extends ScilabGraph {
132 private static final Logger LOG = Logger.getLogger(XcosDiagram.class.getName());
134 private static final String CELLS = "cells";
135 public static final String IN = "in";
136 public static final String OUT = "out";
137 public static final String EIN = "ein";
138 public static final String EOUT = "eout";
141 * Prefix used to tag text node.
143 public static final String HASH_IDENTIFIER = "#identifier";
148 * @param controller the shared controller
149 * @param diagramId the diagram MVC ID
150 * @param kind DIAGRAM or BLOCK for a root diagram or a super-block
151 * @param uid the string UID that will be used on the default parent
153 public XcosDiagram(final JavaController controller, final long diagramId, final Kind kind, String uid) {
154 super(new XcosGraphModel(controller, diagramId, kind, uid), Xcos.getInstance().getStyleSheet());
156 // set the default parent (the JGraphX layer) defined on the model
157 setDefaultParent(getModel().getChildAt(getModel().getRoot(), 0));
159 setComponent(new GraphComponent(this));
162 // Forbid disconnecting cells once it is connected.
163 setCellsDisconnectable(false);
165 // Forbid pending edges.
166 setAllowDanglingEdges(false);
168 // Cannot connect port to itself.
169 setAllowLoops(false);
171 // Override isCellResizable to filter what the user can resize
172 setCellsResizable(true);
174 /* Labels use HTML if not equal to interface function name */
177 * by default every label is movable, see XcosDiagram##isLabelMovable(java.lang.Object) for restrictions
179 setVertexLabelsMovable(true);
180 setEdgeLabelsMovable(true);
183 setCloneInvalidEdges(true);
185 // Override isCellEditable to filter what the user can edit
186 setCellsEditable(true);
188 setConnectableEdges(true);
190 // Do not clear edge points on connect
191 setResetEdgesOnConnect(false);
195 // auto-position the diagram origin
198 // do not put loop links inside the common block cell but on the defaultParent
199 ((mxGraphModel) getModel()).setMaintainEdgeParent(false);
206 * Only return the instanceof klass
208 * @param selection the selection to filter out
209 * @param klass the class selector
210 * @return the selection with only klass instance.
212 public static Object[] filterByClass(final Object[] selection, final Class<BasicBlock> klass) {
213 return mxGraphModel.filterCells(selection, new mxGraphModel.Filter() {
215 public boolean filter(Object cell) {
216 return klass.isInstance(cell);
222 * Fill the hierarchy from the first element up to the root diagram
225 * Should be used as :
227 * hierarchy = fillHierarchy(new ScicosObjectOwner(getUID(), getKind()))
230 * @param hierarchy the collection to fill
231 * @return the filled collection (the root at the end)
233 public static Stack<ScicosObjectOwner> lookForHierarchy(ScicosObjectOwner current) {
234 ScicosObjectOwner local = current;
235 Stack<ScicosObjectOwner> hierarchy = new Stack<>();
236 JavaController controller = new JavaController();
238 long[] parent = new long[] {local.getUID()};
239 if (local.getKind() == Kind.DIAGRAM) {
240 hierarchy.push(local);
244 while (parent[0] != 0l) {
245 hierarchy.push(new ScicosObjectOwner(parent[0], Kind.BLOCK));
246 controller.getObjectProperty(parent[0], Kind.BLOCK, ObjectProperties.PARENT_BLOCK, parent);
249 controller.getObjectProperty(local.getUID(), local.getKind(), ObjectProperties.PARENT_DIAGRAM, parent);
250 hierarchy.push(new ScicosObjectOwner(parent[0], Kind.DIAGRAM));
256 * Sort the blocks per first integer parameter value
258 * @param blocks the block list
259 * @param the shared controller
260 * @return the sorted block list (same instance)
262 public static List<? extends BasicBlock> iparSort(final List<? extends BasicBlock> blocks, final JavaController controller) {
263 Collections.sort(blocks, new Comparator<BasicBlock>() {
265 public int compare(BasicBlock o1, BasicBlock o2) {
268 final VectorOfInt data1 = new VectorOfInt();
269 final VectorOfInt data2 = new VectorOfInt();
271 controller.getObjectProperty(o1.getUID(), Kind.BLOCK, ObjectProperties.IPAR, data1);
272 controller.getObjectProperty(o2.getUID(), Kind.BLOCK, ObjectProperties.IPAR, data2);
275 if (data1.size() >= 1) {
276 value1 = data1.get(0);
282 if (data2.size() >= 1) {
283 value2 = data2.get(0);
288 return value1 - value2;
295 * @param <T> The type to work on
296 * @param klass the class instance to work on
297 * @return list of typed block
299 @SuppressWarnings("unchecked")
300 private <T extends BasicBlock> List<T> getAllTypedBlock(Class<T> klass) {
301 final List<T> list = new ArrayList<T>();
303 int blockCount = getModel().getChildCount(getDefaultParent());
305 for (int i = 0; i < blockCount; i++) {
306 Object cell = getModel().getChildAt(getDefaultParent(), i);
307 if (klass.isInstance(cell)) {
308 // According to the test we are sure that the cell is an
309 // instance of T. Thus we can safely cast it.
318 * @param <T> The type to work on
319 * @param klasses the class instance list to work on
320 * @return list of typed block
322 private <T extends BasicBlock> List<T> getAllTypedBlock(Class<T>[] klasses) {
323 final List<T> list = new ArrayList<T>();
324 for (Class<T> klass : klasses) {
325 list.addAll(getAllTypedBlock(klass));
331 * Fill the context with I/O port
333 * @param context the context to fill
334 * @param controller the shared controller
335 * @return the context
337 @SuppressWarnings("unchecked")
338 public final Map<Object, Object> fillContext(final Map<Object, Object> context, final JavaController controller) {
339 if (!context.containsKey(IN)) {
340 context.put(IN, iparSort(getAllTypedBlock(new Class[] {ExplicitInBlock.class, ImplicitInBlock.class}), controller));
342 if (!context.containsKey(OUT)) {
343 context.put(OUT, iparSort(getAllTypedBlock(new Class[] {ExplicitOutBlock.class, ImplicitOutBlock.class}), controller));
345 if (!context.containsKey(EIN)) {
346 context.put(EIN, iparSort(getAllTypedBlock(new Class[] {EventInBlock.class}), controller));
348 if (!context.containsKey(EOUT)) {
349 context.put(EOUT, iparSort(getAllTypedBlock(new Class[] {EventOutBlock.class}), controller));
356 public String validateCell(final Object cell, final Hashtable<Object, Object> context) {
357 if (getKind() == Kind.BLOCK) {
358 return validateChildDiagram(cell, context);
360 // does not perform any validation on a root diagram
366 * Validate I/O ports.
368 * /!\ No model modification should be made in this method, this is only a
371 * @param cell Cell that represents the cell to validate.
372 * @param context Hashtable that represents the global validation state.
373 * @return the error message or null
375 public String validateChildDiagram(final Object cell, final Map<Object, Object> context) {
379 * Only validate I/O blocks
383 if (cell instanceof ExplicitInBlock || cell instanceof ImplicitInBlock) {
385 } else if (cell instanceof ExplicitOutBlock || cell instanceof ImplicitOutBlock) {
387 } else if (cell instanceof EventInBlock) {
389 } else if (cell instanceof EventOutBlock) {
394 final BasicBlock block = (BasicBlock) cell;
395 final JavaController controller = new JavaController();
400 // fill the context once
401 fillContext(context, controller);
406 // get the real index
407 final List<? extends BasicBlock> blocks = (List<? extends BasicBlock>) context.get(key);
408 final int realIndex = blocks.indexOf(block) + 1;
410 // get the user index
411 VectorOfInt ipar = new VectorOfInt();
412 controller.getObjectProperty(block.getUID(), Kind.BLOCK, ObjectProperties.IPAR, ipar);
413 if (ipar.size() < 1) {
416 final int userIndex = ipar.get(0);
418 // if the indexes are not equals, alert the user.
419 if (realIndex != userIndex) {
420 final StringBuilder str = new StringBuilder();
421 str.append("<html><body><em>");
422 str.append(XcosMessages.WRONG_PORT_NUMBER);
423 str.append("</em><br/>");
424 str.append(String.format(XcosMessages.EXPECTING_NUMBER, realIndex, userIndex));
425 str.append("</body></html> ");
427 err = str.toString();
434 * Set a model property and track undo/redo operation
435 * @param cell the cell
436 * @param values the value to set
438 public void updateBlock(BasicBlock cell, Map<ObjectProperties, Object> values) {
441 blockUpdated(cell, values);
442 fireEvent(new mxEventObject(XcosEvent.UPDATE_BLOCK, XcosEvent.BLOCK, cell));
448 private void blockUpdated(BasicBlock cell, Map<ObjectProperties, Object> values) {
451 for (Map.Entry<ObjectProperties, Object> e : values.entrySet()) {
452 if (e.getValue() instanceof double[]) {
453 ((XcosGraphModel) model).setProperty(cell, e.getKey(), (double[]) e.getValue());
454 } else if (e.getValue() instanceof int[]) {
455 ((XcosGraphModel) model).setProperty(cell, e.getKey(), (int[]) e.getValue());
456 } else if (e.getValue() instanceof boolean[]) {
457 ((XcosGraphModel) model).setProperty(cell, e.getKey(), (boolean[]) e.getValue());
458 } else if (e.getValue() instanceof String[]) {
459 ((XcosGraphModel) model).setProperty(cell, e.getKey(), (String[]) e.getValue());
460 } else if (e.getValue() instanceof long[]) {
461 ((XcosGraphModel) model).setProperty(cell, e.getKey(), (long[]) e.getValue());
462 } else if (e.getValue() instanceof VectorOfDouble) {
463 ((XcosGraphModel) model).setProperty(cell, e.getKey(), (VectorOfDouble) e.getValue());
464 } else if (e.getValue() instanceof VectorOfInt) {
465 ((XcosGraphModel) model).setProperty(cell, e.getKey(), (VectorOfInt) e.getValue());
466 } else if (e.getValue() instanceof VectorOfBool) {
467 ((XcosGraphModel) model).setProperty(cell, e.getKey(), (VectorOfBool) e.getValue());
468 } else if (e.getValue() instanceof VectorOfString) {
469 ((XcosGraphModel) model).setProperty(cell, e.getKey(), (VectorOfString) e.getValue());
470 } else if (e.getValue() instanceof VectorOfScicosID) {
471 ((XcosGraphModel) model).setProperty(cell, e.getKey(), (VectorOfScicosID) e.getValue());
481 * Static diagram listeners
484 * CellResizedTracker Called when mxEvents.CELLS_RESIZED is fired.
486 private static final class RepositionTracker implements mxIEventListener {
488 private static RepositionTracker instance;
493 private RepositionTracker() {
497 * @return the instance
499 public static RepositionTracker getInstance() {
500 if (instance == null) {
501 instance = new RepositionTracker();
507 * Update the cell view
509 * @param source the source instance
510 * @param evt the event data
512 * com.mxgraph.util.mxEventSource.mxIEventListener#invoke(java.lang.Object,
513 * com.mxgraph.util.mxEventObject)
516 public void invoke(final Object source, final mxEventObject evt) {
517 final XcosDiagram diagram = (XcosDiagram) source;
518 final Object[] cells = (Object[]) evt.getProperty(CELLS);
520 diagram.getModel().beginUpdate();
522 for (int i = 0; i < cells.length; ++i) {
523 if (cells[i] instanceof BasicBlock) {
524 BlockPositioning.updateBlockView(diagram, (BasicBlock) cells[i]);
528 diagram.getModel().endUpdate();
534 * Refresh each block on modification (update port position, etc...)
536 private static final class RefreshBlockTracker implements mxIEventListener {
537 private static RefreshBlockTracker instance = new RefreshBlockTracker();
540 * Default constructor
542 private RefreshBlockTracker() {
546 * @return the instance
548 public static RefreshBlockTracker getInstance() {
553 * Refresh the block on port added
555 * @param sender the diagram
556 * @param evt the event
558 * com.mxgraph.util.mxEventSource.mxIEventListener#invoke(java.lang.Object,
559 * com.mxgraph.util.mxEventObject)
562 public void invoke(Object sender, mxEventObject evt) {
563 final XcosDiagram diagram = (XcosDiagram) sender;
565 if (diagram.isReadonly()) {
569 diagram.getModel().beginUpdate();
571 final BasicBlock updatedBlock = (BasicBlock) evt.getProperty(XcosEvent.BLOCK);
572 BlockPositioning.updateBlockView(diagram, updatedBlock);
574 diagram.getView().clear(updatedBlock, true, true);
576 // validate display errors
577 diagram.getAsComponent().clearCellOverlays();
578 diagram.getAsComponent().validateGraph();
580 diagram.getView().validate();
582 diagram.getModel().endUpdate();
588 * Update the tab title depending on the status
590 private static final class SavedStatusTracker implements mxIEventListener {
591 private static final Map<XcosDiagram, SavedStatusTracker> instances = new HashMap<>();
592 private ScicosObjectOwner owner;
594 private SavedStatusTracker(final ScicosObjectOwner o) {
598 public static SavedStatusTracker getInstance(XcosDiagram d) {
599 return instances.putIfAbsent(d, new SavedStatusTracker(Xcos.findRoot(d)));
603 public void invoke(Object sender, mxEventObject eo) {
604 if (sender instanceof XcosGraphModel) {
605 List<XcosDiagram> diagrams = Xcos.getInstance().openedDiagrams(owner);
606 diagrams.stream().forEach(d -> d.updateTabTitle());
612 * Update the number and position of ports for a Superblock
614 public static final class UpdateSuperblockPortsTracker implements mxIEventListener {
616 private static UpdateSuperblockPortsTracker instance = new UpdateSuperblockPortsTracker();
618 public static UpdateSuperblockPortsTracker getInstance() {
623 public void invoke(Object sender, mxEventObject evt) {
624 final XcosDiagram diagram = (XcosDiagram) sender;
626 if (diagram.isReadonly()) {
630 // common behavior for both block adding/removing and block update
631 List<Object> updatedIOBlocks;
632 if (XcosEvent.UPDATE_BLOCK.equals(evt.getName())) {
633 updatedIOBlocks = new ArrayList<>();
634 Object b = evt.getProperty(XcosEvent.BLOCK);
635 if (b instanceof ContextUpdate) {
636 updatedIOBlocks.add(b);
638 } else { // ADD_CELLS or REMOVE_CELLS
639 Object[] cells = (Object[]) evt.getProperty("cells");
640 updatedIOBlocks = Arrays.stream(cells).filter(b -> b instanceof ContextUpdate).collect(Collectors.toList());
642 if (updatedIOBlocks.isEmpty()) {
646 final JavaController controller = new JavaController();
648 String[] superBlockUID = {""};
649 controller.getObjectProperty(diagram.getUID(), diagram.getKind(), ObjectProperties.UID, superBlockUID);
651 List<XcosDiagram> diagrams = Xcos.getInstance().openedDiagrams(Xcos.findRoot(controller, diagram));
652 for (XcosDiagram parent : diagrams) {
653 final XcosGraphModel model = (XcosGraphModel) parent.getModel();
654 Object cell = model.getCell(superBlockUID[0]);
656 // associated parent superblock when visible
657 SuperBlock superblock = (SuperBlock) cell;
659 // create a full context
660 Map<Object, Object> context = new HashMap<>();
661 diagram.fillContext(context, controller);
663 // only update the superblock ports if all of them are valids
664 diagram.getAsComponent().clearCellOverlays();
665 String status = diagram.getAsComponent().validateGraph();
666 if (status != null) {
672 syncPorts(controller, superblock, ObjectProperties.INPUTS, (List<ContextUpdate>) context.get(IN), parent);
673 syncPorts(controller, superblock, ObjectProperties.OUTPUTS, (List<ContextUpdate>) context.get(OUT), parent);
674 syncPorts(controller, superblock, ObjectProperties.EVENT_INPUTS, (List<ContextUpdate>) context.get(EIN), parent);
675 syncPorts(controller, superblock, ObjectProperties.EVENT_OUTPUTS, (List<ContextUpdate>) context.get(EOUT), parent);
685 * Synchronize ports on the parent diagram accordingly to the updated IOBlocks
686 * @param controller shared controller;
687 * @param superblock the superblock to update
689 * @param updated the updated IOBlocks (ipar sorted)
690 * @param parent diagram containing the superblock
692 public static void syncPorts(final JavaController controller, SuperBlock superblock, ObjectProperties p, List<ContextUpdate> updated, XcosDiagram parent) {
693 VectorOfScicosID ports = new VectorOfScicosID();
694 controller.getObjectProperty(superblock.getUID(), superblock.getKind(), p, ports);
696 int added = updated.size() - ports.size();
698 for (int i = 0; i < added; i++) {
700 BasicPort port = ContextUpdate.IOBlocks.createPort(controller, updated.get(ports.size() + i));
701 parent.addCell(port, superblock);
703 } else if (added < 0) {
704 for (int i = 0; i < -added; i++) {
706 String portUID[] = {""};
707 controller.getObjectProperty(ports.get(updated.size() + i), Kind.PORT, ObjectProperties.UID, portUID);
708 parent.removeCells(new Object[] {((XcosGraphModel) parent.getModel()).getCell(portUID[0])});
714 * Append the modified blocks to an existing context
715 * @param context the validation context
716 * @param updatedIOBlocks the blocks to add
717 * @param controller shared controller
719 public static void updateContext(Map<Object, Object> context, List<XcosCell> updatedIOBlocks, final JavaController controller) {
721 List<BasicBlock> l = (List<BasicBlock>) context.getOrDefault(IN, new ArrayList<>());
722 updatedIOBlocks.stream()
723 .filter(b -> b instanceof ExplicitInBlock || b instanceof ImplicitInBlock)
724 .map(b -> (ContextUpdate) b)
725 .collect(Collectors.toCollection(() -> l));
726 iparSort(l, controller);
727 context.putIfAbsent(IN, l);
730 List<BasicBlock> l = (List<BasicBlock>) context.getOrDefault(OUT, new ArrayList<>());
731 updatedIOBlocks.stream()
732 .filter(b -> b instanceof ExplicitOutBlock || b instanceof ImplicitOutBlock)
733 .map(b -> (ContextUpdate) b)
734 .collect(Collectors.toCollection(() -> l));
735 iparSort(l, controller);
736 context.putIfAbsent(OUT, l);
739 List<BasicBlock> l = (List<BasicBlock>) context.getOrDefault(EIN, new ArrayList<>());
740 updatedIOBlocks.stream()
741 .filter(b -> b instanceof EventInBlock)
742 .map(b -> (ContextUpdate) b)
743 .collect(Collectors.toCollection(() -> l));
744 iparSort(l, controller);
745 context.putIfAbsent(EIN, l);
748 List<BasicBlock> l = (List<BasicBlock>) context.getOrDefault(EOUT, new ArrayList<>());
749 updatedIOBlocks.stream()
750 .filter(b -> b instanceof EventOutBlock)
751 .map(b -> (ContextUpdate) b)
752 .collect(Collectors.toCollection(() -> l));
753 iparSort(l, controller);
754 context.putIfAbsent(EOUT, l);
760 * Hook method that creates the new edge for insertEdge. This implementation
761 * does not set the source and target of the edge, these are set when the
762 * edge is added to the model.
764 * @param parent Cell that specifies the parent of the new edge.
765 * @param id Optional string that defines the Id of the new edge.
766 * @param value Object to be used as the user object.
767 * @param source Cell that defines the source of the edge.
768 * @param target Cell that defines the target of the edge.
769 * @param style Optional string that defines the cell style.
770 * @return Returns the new edge to be inserted.
771 * @see com.mxgraph.view.mxGraph#createEdge(java.lang.Object,
772 * java.lang.String, java.lang.Object, java.lang.Object, java.lang.Object,
776 public Object createEdge(Object parent, String id, Object value, Object source, Object target, String style) {
778 JavaController controller = new JavaController();
780 if (source instanceof BasicPort) {
781 BasicPort src = (BasicPort) source;
782 BasicLink link = null;
784 long uid = controller.createObject(Kind.LINK);
785 if (src.getType() == Type.EXPLICIT) {
786 link = new ExplicitLink(controller, uid, Kind.LINK, value, null, style, id);
787 } else if (src.getType() == Type.IMPLICIT) {
788 link = new ImplicitLink(controller, uid, Kind.LINK, value, null, style, id);
790 link = new CommandControlLink(controller, uid, Kind.LINK, value, null, style, id);
793 // allocate the associated geometry
794 link.setGeometry(new mxGeometry());
796 } else if (source instanceof SplitBlock) {
797 SplitBlock src = (SplitBlock) source;
798 return createEdge(parent, id, value, src.getIn(), target, style);
799 } else if (source instanceof BasicLink) {
800 BasicLink src = (BasicLink) source;
801 BasicLink link = null;
804 Class<? extends BasicLink> klass = src.getClass();
806 // call XXXXLink(JavaController controller, long uid, Kind kind, Object value, mxGeometry geometry, String style, String id)
807 Constructor<? extends BasicLink> cstr = klass.getConstructor(JavaController.class, Long.TYPE, Kind.class, Object.class, mxGeometry.class, String.class, String.class);
808 link = cstr.newInstance(controller, controller.createObject(Kind.LINK), src.getKind(), null, new mxGeometry(), src.getStyle(), new UID().toString());
809 } catch (ReflectiveOperationException e) {
810 LOG.severe(e.toString());
817 LOG.warning("Unable to create an edge");
824 * Add an edge from a source to the target.
826 * @param cell the edge to add (may be null)
827 * @param parent the parent of the source and the target
828 * @param source the source cell
829 * @param target the target cell
830 * @param index the index of the edge
831 * @return the added edge or null.
832 * @see com.mxgraph.view.mxGraph#addEdge(java.lang.Object, java.lang.Object,
833 * java.lang.Object, java.lang.Object, java.lang.Integer)
836 public Object addCell(Object cell, Object parent, Integer index, Object source, Object target) {
838 // already connected edge or normal block
839 if (source == null && target == null) {
840 return super.addCell(cell, parent, index, source, target);
843 // Command -> Control
844 if (source instanceof CommandPort && target instanceof ControlPort && cell instanceof CommandControlLink) {
845 return super.addCell(cell, parent, index, source, target);
848 // Control -> Command
849 // Switch source and target !
850 if (target instanceof CommandPort && source instanceof ControlPort && cell instanceof CommandControlLink) {
851 BasicLink current = (BasicLink) cell;
852 current.invertDirection();
854 return super.addCell(cell, parent, index, target, source);
857 // ExplicitOutput -> ExplicitInput
858 if (source instanceof ExplicitOutputPort && target instanceof ExplicitInputPort && cell instanceof ExplicitLink) {
859 return super.addCell(cell, parent, index, source, target);
861 // ExplicitInput -> ExplicitOutput
862 // Switch source and target !
863 if (target instanceof ExplicitOutputPort && source instanceof ExplicitInputPort && cell instanceof ExplicitLink) {
864 BasicLink current = (BasicLink) cell;
865 current.invertDirection();
867 return super.addCell(cell, parent, index, target, source);
870 // ImplicitOutput -> ImplicitInput
871 if (source instanceof ImplicitOutputPort && target instanceof ImplicitInputPort && cell instanceof ImplicitLink) {
872 return super.addCell(cell, parent, index, source, target);
874 // ImplicitInput -> ImplicitOutput
875 // Switch source and target !
876 if (target instanceof ImplicitOutputPort && source instanceof ImplicitInputPort && cell instanceof ImplicitLink) {
877 BasicLink current = (BasicLink) cell;
878 current.invertDirection();
880 return super.addCell(cell, parent, index, target, source);
883 // ImplicitInput -> ImplicitInput
884 if (source instanceof ImplicitInputPort && target instanceof ImplicitInputPort && cell instanceof ImplicitLink) {
885 return super.addCell(cell, parent, index, source, target);
887 // ImplicitOutputPort -> ImplicitOutput
888 // Switch source and target !
889 if (target instanceof ImplicitOutputPort && source instanceof ImplicitOutputPort && cell instanceof ImplicitLink) {
890 BasicLink current = (BasicLink) cell;
891 current.invertDirection();
893 return super.addCell(cell, parent, index, target, source);
899 // ExplicitLink -> ExplicitInputPort
900 if (source instanceof ExplicitLink && target instanceof ExplicitInputPort && cell instanceof ExplicitLink) {
901 SplitBlock split = addSplitEdge(((BasicLink) cell).getGeometry().getSourcePoint(), (BasicLink) source);
902 return addCell(cell, parent, index, split.getOut2(), target);
904 // ExplicitOutput -> ExpliciLink
905 // Switch source and target !
906 if (target instanceof ExplicitLink && source instanceof ExplicitInputPort && cell instanceof ExplicitLink) {
907 final BasicLink current = (BasicLink) cell;
908 final SplitBlock split = addSplitEdge(current.getGeometry().getTargetPoint(), (BasicLink) target);
910 current.invertDirection();
912 return addCell(cell, parent, index, split.getOut2(), source);
915 // ImplicitLink -> ImplicitInputPort
916 if (source instanceof ImplicitLink && target instanceof ImplicitInputPort && cell instanceof ImplicitLink) {
917 SplitBlock split = addSplitEdge(((BasicLink) cell).getGeometry().getSourcePoint(), (BasicLink) source);
918 return addCell(cell, parent, index, split.getOut2(), target);
920 // ImplicitInputPort -> ImplicitLink
921 // Switch source and target !
922 if (target instanceof ImplicitLink && source instanceof ImplicitInputPort && cell instanceof ImplicitLink) {
923 final BasicLink current = (BasicLink) cell;
924 final SplitBlock split = addSplitEdge(current.getGeometry().getTargetPoint(), (BasicLink) target);
926 current.invertDirection();
928 return addCell(cell, parent, index, split.getOut2(), source);
931 // ImplicitLink -> ImplicitOutputPort
932 if (source instanceof ImplicitLink && target instanceof ImplicitOutputPort && cell instanceof ImplicitLink) {
933 final BasicLink current = (BasicLink) cell;
934 final SplitBlock split = addSplitEdge(current.getGeometry().getTargetPoint(), (BasicLink) source);
935 return addCell(cell, parent, index, split.getOut2(), source);
937 // ImplicitOutputPort -> ImplicitLink
938 // Switch source and target !
939 if (target instanceof ImplicitLink && source instanceof ImplicitOutputPort && cell instanceof ImplicitLink) {
940 final BasicLink current = (BasicLink) cell;
941 final SplitBlock split = addSplitEdge(current.getGeometry().getTargetPoint(), (ImplicitLink) target);
942 return addCell(cell, parent, index, split.getOut2(), source);
945 // CommandControlLink -> ControlPort
946 if (source instanceof CommandControlLink && target instanceof ControlPort && cell instanceof CommandControlLink) {
947 SplitBlock split = addSplitEdge(((BasicLink) cell).getGeometry().getSourcePoint(), (BasicLink) source);
948 return addCell(cell, parent, index, split.getOut2(), target);
950 // ControlPort -> CommandControlLink
951 // Switch source and target !
952 if (target instanceof CommandControlLink && source instanceof ControlPort && cell instanceof CommandControlLink) {
953 final BasicLink current = (BasicLink) cell;
954 final SplitBlock split = addSplitEdge(current.getGeometry().getTargetPoint(), (BasicLink) target);
956 current.invertDirection();
958 return addCell(cell, parent, index, split.getOut2(), source);
961 if (cell instanceof BasicLink && source != null && target != null) {
962 LOG.severe("Unable to add a typed link");
965 LOG.severe("Adding an untyped edge");
966 return super.addCell(cell, parent, index, source, target);
971 * Add a split on a edge.
973 * @param splitPoint the split point (center of the split block)
974 * @param link source link
975 * @return split block
977 public SplitBlock addSplitEdge(final mxPoint splitPoint, final BasicLink link) {
978 final BasicPort linkSource = (BasicPort) link.getSource();
979 final BasicPort linkTarget = (BasicPort) link.getTarget();
982 * Select the right split accordingly to the link klass
984 BlockInterFunction f;
985 if (link instanceof CommandControlLink) {
986 f = BlockInterFunction.CLKSPLIT_f;
987 } else if (link instanceof ImplicitLink) {
988 f = BlockInterFunction.IMPSPLIT_f;
990 f = BlockInterFunction.SPLIT_f;
993 final SplitBlock splitBlock;
995 splitBlock = (SplitBlock) XcosCellFactory.createBlock(f);
996 } catch (InterpreterException ex) {
997 // something goes wrong
998 throw new RuntimeException(ex);
1001 // snap the center of the split block on the grid
1003 mxGeometry geom = splitBlock.getGeometry();
1004 double x = snap(splitPoint.getX()) - (SplitBlock.DEFAULT_SIZE / 2.);
1005 double y = snap(splitPoint.getY()) - (SplitBlock.DEFAULT_SIZE / 2.);
1008 splitBlock.setGeometry(geom);
1009 } catch (NullPointerException e) {
1010 e.printStackTrace();
1013 getModel().beginUpdate();
1015 // Origin of the parent, (0,0) as default may be different in case
1016 mxPoint orig = link.getParent().getGeometry();
1018 orig = new mxPoint();
1021 addCell(splitBlock);
1024 // get breaking segment and related point
1025 mxPoint splitTr = new mxPoint(splitPoint.getX() - orig.getX(), splitPoint.getY() - orig.getY());
1026 final int pos = link.findNearestSegment(splitTr);
1028 // save points after breaking point
1029 final List<mxPoint> saveStartPoints = link.getPoints(pos, true);
1030 final List<mxPoint> saveEndPoints = link.getPoints(pos, false);
1032 // remove the first end point if the position is near the split
1034 if (saveEndPoints.size() > 0) {
1035 final mxPoint p = saveEndPoints.get(0);
1036 final double dx = p.getX() - splitTr.getX();
1037 final double dy = p.getY() - splitTr.getY();
1039 if (!getAsComponent().isSignificant(dx, dy)) {
1040 saveEndPoints.remove(0);
1044 getModel().remove(link);
1045 connect(linkSource, splitBlock.getIn(), saveStartPoints, orig);
1046 connect(splitBlock.getOut1(), linkTarget, saveEndPoints, orig);
1050 getModel().endUpdate();
1057 * Connect two port together with the associated points.
1059 * This method perform the connection in two step in order to generate the
1060 * right UndoableChangeEdits.
1062 * @param src the source port
1063 * @param trg the target port
1064 * @param points the points
1065 * @param orig the origin point (may be (0,0))
1067 public void connect(BasicPort src, BasicPort trg, List<mxPoint> points, mxPoint orig) {
1068 mxGeometry geometry;
1071 * Add the link with a default geometry
1073 final Object newLink1 = createEdge(null, null, null, src, trg, null);
1074 addCell(newLink1, null, null, src, trg);
1075 geometry = getModel().getGeometry(newLink1);
1076 if (getModel().getParent(newLink1) instanceof BasicBlock) {
1077 // on a loop link, translate the points as the cell has been moved to the parent
1078 orig.setX(orig.getX() + geometry.getX());
1079 orig.setY(orig.getY() + geometry.getY());
1081 geometry.setPoints(points);
1082 getModel().setGeometry(newLink1, geometry);
1085 * Update the geometry
1087 // should be cloned to generate an event
1088 geometry = (mxGeometry) getModel().getGeometry(newLink1).clone();
1089 final double dx = orig.getX();
1090 final double dy = orig.getY();
1092 geometry.translate(dx, dy);
1093 getModel().setGeometry(newLink1, geometry);
1097 * Initialize component settings for a graph.
1099 * This method *must* be used to setup the component after any
1102 public final void initComponent() {
1103 getAsComponent().setToolTips(true);
1105 // This enable stop editing cells when pressing Enter.
1106 getAsComponent().setEnterStopsCellEditing(false);
1108 getAsComponent().setTolerance(1);
1110 getAsComponent().getViewport().setOpaque(false);
1112 getAsComponent().setBackground(XcosOptions.getEdition().getGraphBackground());
1114 final boolean gridEnable = XcosOptions.getEdition().isGraphGridEnable();
1115 setGridVisible(gridEnable);
1117 setGridSize(XcosOptions.getEdition().getGraphGrid());
1122 * Install the multiplicities (use for link checking)
1124 private void setMultiplicities() {
1125 final List<mxMultiplicity> multiplicities = new ArrayList<mxMultiplicity>();
1128 multiplicities.add(new PortCheck(ExplicitInputPort.class, Collections.unmodifiableList(new ArrayList<Class<? extends mxCell>>() {
1129 private static final long serialVersionUID = -4987163442006736665L;
1132 add(ExplicitOutputPort.class);
1133 add(ExplicitLink.class);
1135 }), XcosMessages.LINK_ERROR_EXPLICIT_IN));
1136 multiplicities.add(new PortCheck(ImplicitInputPort.class, Collections.unmodifiableList(new ArrayList<Class<? extends mxCell>>() {
1137 private static final long serialVersionUID = 886376532181210926L;
1140 add(ImplicitOutputPort.class);
1141 add(ImplicitInputPort.class);
1142 add(ImplicitLink.class);
1144 }), XcosMessages.LINK_ERROR_IMPLICIT_IN));
1147 multiplicities.add(new PortCheck(ExplicitOutputPort.class, Collections.unmodifiableList(new ArrayList<Class<? extends mxCell>>() {
1148 private static final long serialVersionUID = 4594127972486054821L;
1151 add(ExplicitInputPort.class);
1153 }), XcosMessages.LINK_ERROR_EXPLICIT_OUT));
1154 multiplicities.add(new PortCheck(ImplicitOutputPort.class, Collections.unmodifiableList(new ArrayList<Class<? extends mxCell>>() {
1155 private static final long serialVersionUID = -3719677806532507973L;
1158 add(ImplicitInputPort.class);
1159 add(ImplicitOutputPort.class);
1160 add(ImplicitLink.class);
1162 }), XcosMessages.LINK_ERROR_IMPLICIT_OUT));
1165 multiplicities.add(new PortCheck(ControlPort.class, Collections.unmodifiableList(new ArrayList<Class<? extends mxCell>>() {
1166 private static final long serialVersionUID = 2941077191386058497L;
1169 add(CommandPort.class);
1170 add(CommandControlLink.class);
1172 }), XcosMessages.LINK_ERROR_EVENT_IN));
1175 multiplicities.add(new PortCheck(CommandPort.class, Collections.unmodifiableList(new ArrayList<Class<? extends mxCell>>() {
1176 private static final long serialVersionUID = -3470370027962480362L;
1179 add(ControlPort.class);
1181 }), XcosMessages.LINK_ERROR_EVENT_OUT));
1183 // ExplicitLink connections
1184 multiplicities.add(new PortCheck(ExplicitLink.class, Collections.unmodifiableList(new ArrayList<Class<? extends mxCell>>() {
1185 private static final long serialVersionUID = 7423543162930147373L;
1188 add(ExplicitInputPort.class);
1190 }), XcosMessages.LINK_ERROR_EVENT_OUT));
1192 // ImplicitLink connections
1193 multiplicities.add(new PortCheck(ImplicitLink.class, Collections.unmodifiableList(new ArrayList<Class<? extends mxCell>>() {
1194 private static final long serialVersionUID = 7775100011122283282L;
1197 add(ImplicitInputPort.class);
1198 add(ImplicitOutputPort.class);
1200 }), XcosMessages.LINK_ERROR_EVENT_OUT));
1202 // CommandControlLink connections
1203 multiplicities.add(new PortCheck(CommandControlLink.class, Collections.unmodifiableList(new ArrayList<Class<? extends mxCell>>() {
1204 private static final long serialVersionUID = 3260421433507192386L;
1207 add(ControlPort.class);
1209 }), XcosMessages.LINK_ERROR_EVENT_OUT));
1211 // Already connected port
1212 multiplicities.add(new PortCheck(BasicPort.class, Collections.unmodifiableList(new ArrayList<Class<? extends mxCell>>() {
1213 private static final long serialVersionUID = 6376349598052836660L;
1216 add(BasicPort.class);
1218 }), XcosMessages.LINK_ERROR_ALREADY_CONNECTED));
1220 setMultiplicities(multiplicities.toArray(new mxMultiplicity[multiplicities.size()]));
1224 * Install all needed Listeners.
1226 public void installListeners() {
1228 * First remove all listeners if present
1230 getModel().removeListener(SavedStatusTracker.getInstance(this));
1231 removeListener(UpdateSuperblockPortsTracker.getInstance());
1232 removeListener(RefreshBlockTracker.getInstance());
1233 removeListener(RepositionTracker.getInstance());
1235 // Track when resizing or moving (droping) a cell.
1236 addListener(mxEvent.CELLS_RESIZED, RepositionTracker.getInstance());
1237 addListener(mxEvent.CELLS_MOVED, RepositionTracker.getInstance());
1239 // Refresh block rendering on update
1240 addListener(XcosEvent.UPDATE_BLOCK, RefreshBlockTracker.getInstance());
1242 // on Superblock diagram, refresh port interface on the parent diagram
1243 if (getKind() == Kind.BLOCK) {
1244 addListener(XcosEvent.UPDATE_BLOCK, UpdateSuperblockPortsTracker.getInstance());
1246 addListener(mxEvent.ADD_CELLS, UpdateSuperblockPortsTracker.getInstance());
1247 addListener(mxEvent.REMOVE_CELLS, UpdateSuperblockPortsTracker.getInstance());
1248 addListener(mxEvent.MOVE_CELLS, UpdateSuperblockPortsTracker.getInstance());
1251 // Update the saved status rendering on modification
1252 getModel().addListener(mxEvent.CHANGE, SavedStatusTracker.getInstance(this));
1256 * Translate the cell and align any split block
1258 * @param cell any object
1259 * @param dx the X delta
1260 * @param dy the Y delta
1263 public void translateCell(Object cell, double dx, double dy) {
1264 if (cell instanceof SplitBlock) {
1265 mxGeometry geom = model.getGeometry(cell);
1267 final double posX = snap(geom.getX() + dx) - (SplitBlock.DEFAULT_SIZE / 2.);
1268 final double posY = snap(geom.getY() + dy) - (SplitBlock.DEFAULT_SIZE / 2.);
1270 dx = posX - geom.getX();
1271 dy = posY - geom.getY();
1274 super.translateCell(cell, dx, dy);
1278 * Removes the given cells from the graph including all connected edges if
1279 * includeEdges is true. The change is carried out using cellsRemoved.
1281 * @param cells the cells to be removed
1282 * @param includeEdges true if the edges must be removed, false otherwise.
1283 * @return the deleted cells
1284 * @see com.mxgraph.view.mxGraph#removeCells(java.lang.Object[], boolean)
1287 public Object[] removeCells(final Object[] cells, final boolean includeEdges) {
1288 if (cells == null || cells.length == 0) {
1289 return super.removeCells(cells, includeEdges);
1293 * First remove all links connected to a removed Split if applicable
1295 final Object[] initialCells;
1297 initialCells = addAllEdges(cells);
1299 initialCells = cells;
1302 // stash used on the loop
1303 final Queue<Object> loopCells = new LinkedList<Object>(Arrays.asList(initialCells));
1304 // the cells that need to be really
1305 final Set<Object> removedCells = new HashSet<Object>(loopCells);
1306 // couple of cells to reconnect
1307 final List<BasicPort[]> connectedCells = new ArrayList<BasicPort[]>();
1308 final List<List<mxPoint>> connectedPoints = new ArrayList<List<mxPoint>>();
1311 * Then loop on the algorithm to select the right edges
1313 // /!\ not bounded algorithm
1314 while (loopCells.size() > 0) {
1315 Object cell = loopCells.remove();
1317 if (cell instanceof BasicLink) {
1319 * Continue on non fully connected links
1321 if (((BasicLink) cell).getSource() == null) {
1324 if (((BasicLink) cell).getTarget() == null) {
1329 * Add any split to a link
1331 addTerminalParent(((BasicLink) cell).getSource(), removedCells, loopCells);
1332 addTerminalParent(((BasicLink) cell).getTarget(), removedCells, loopCells);
1334 } else if (cell instanceof SplitBlock) {
1335 final SplitBlock splitBlock = (SplitBlock) cell;
1338 * Remove related connection or not and reconnect.
1340 if (splitBlock.getIn().getEdgeCount() == 0 || splitBlock.getOut1().getEdgeCount() == 0 || splitBlock.getOut2().getEdgeCount() == 0) {
1341 // corner case, all links will be removed
1345 final mxICell inLink = splitBlock.getIn().getEdgeAt(0);
1346 final mxICell out1Link = splitBlock.getOut1().getEdgeAt(0);
1347 final mxICell out2Link = splitBlock.getOut2().getEdgeAt(0);
1349 final boolean inRemoved = removedCells.contains(inLink);
1350 final boolean out1Removed = removedCells.contains(out1Link);
1351 final boolean out2Removed = removedCells.contains(out2Link);
1354 * Explicit case, if the in link is deleted; all the out links also should.
1356 if (inLink instanceof ExplicitLink && inRemoved) {
1357 if (removedCells.add(out1Link)) {
1358 loopCells.add(out1Link);
1361 if (removedCells.add(out2Link)) {
1362 loopCells.add(out2Link);
1367 * Global case reconnect if not removed
1369 final BasicPort[] connection;
1370 List<mxPoint> points = null;
1371 if (!inRemoved && !out1Removed && out2Removed) {
1372 connection = findTerminals(inLink, out1Link, removedCells);
1373 points = getDirectPoints(splitBlock, inLink, out1Link);
1374 } else if (!inRemoved && out1Removed && !out2Removed) {
1375 connection = findTerminals(inLink, out2Link, removedCells);
1376 points = getDirectPoints(splitBlock, inLink, out2Link);
1377 } else if (inRemoved && !out1Removed && !out2Removed) {
1378 // only implicit or event case, log otherwise
1379 if (out1Link instanceof ExplicitLink || out2Link instanceof ExplicitLink) {
1380 LOG.severe("Reconnection failed for explicit links");
1383 connection = findTerminals(out1Link, out2Link, removedCells);
1384 points = getDirectPoints(splitBlock, out1Link, out2Link);
1390 if (connection != null) {
1391 connectedCells.add(connection);
1392 connectedPoints.add(points);
1398 getModel().beginUpdate();
1400 ret = super.removeCells(removedCells.toArray(), includeEdges);
1401 for (int i = 0; i < connectedCells.size(); i++) {
1402 final BasicPort[] connection = connectedCells.get(i);
1403 final List<mxPoint> points = connectedPoints.get(i);
1404 if (!removedCells.contains(connection[0].getParent()) && !removedCells.contains(connection[1].getParent())) {
1405 connect(connection[0], connection[1], points, new mxPoint());
1409 getModel().endUpdate();
1415 * Add any terminal parent to the removed cells
1417 * @param terminal the current terminal (instance of BasicPort)
1418 * @param removedCells the "to be removed" set
1419 * @param loopCells the "while loop" set
1421 private void addTerminalParent(mxICell terminal, Collection<Object> removedCells, Collection<Object> loopCells) {
1422 assert (terminal == null || terminal instanceof BasicPort);
1423 assert (removedCells != null);
1424 assert (loopCells != null);
1426 // getting terminal parent
1427 mxICell target = null;
1428 if (terminal != null) {
1429 target = terminal.getParent();
1434 // add target if applicable
1435 if (target instanceof SplitBlock) {
1436 if (removedCells.add(target)) {
1437 loopCells.add(target);
1443 * Find the terminals when relinking the 2 links
1445 * This method ensure that {source, target} are not child of removed blocks.
1447 * @param linkSource the normal source link
1448 * @param linkTerminal the normal target link
1449 * @param removedCells the set of removed objects
1450 * @return the {source, target} connection
1452 private BasicPort[] findTerminals(final mxICell linkSource, final mxICell linkTerminal, final Set<Object> removedCells) {
1453 BasicPort src = (BasicPort) linkSource.getTerminal(true);
1454 BasicPort tgt = (BasicPort) linkTerminal.getTerminal(false);
1455 if (linkSource instanceof ImplicitLink) {
1456 if (removedCells.contains(src.getParent())) {
1457 src = (BasicPort) linkSource.getTerminal(false);
1459 if (removedCells.contains(tgt.getParent())) {
1460 tgt = (BasicPort) linkTerminal.getTerminal(true);
1464 return new BasicPort[] {src, tgt};
1468 * Get the direct points from inLink.getSource() to outLink.getTarget().
1470 * @param splitBlock the current splitblock (added as a mid-point)
1471 * @param inLink the link before the split
1472 * @param outLink the link after the split
1473 * @return the points
1475 private List<mxPoint> getDirectPoints(final SplitBlock splitBlock, final mxICell inLink, final mxICell outLink) {
1476 List<mxPoint> points;
1477 // add the points before the split
1478 points = new ArrayList<mxPoint>();
1479 if (inLink.getGeometry().getPoints() != null) {
1480 points.addAll(inLink.getGeometry().getPoints());
1483 // add a new point at the split location
1484 points.add(new mxPoint(snap(splitBlock.getGeometry().getCenterX()), snap(splitBlock.getGeometry().getCenterY())));
1486 // add the points after the split
1487 if (outLink.getGeometry().getPoints() != null) {
1488 points.addAll(outLink.getGeometry().getPoints());
1495 * Manage Group to be CellFoldable i.e with a (-) to reduce and a (+) to
1496 * expand them. Labels (mxCell instance with value) should not have a
1497 * visible foldable sign.
1499 * @param cell the selected cell
1500 * @param collapse the collapse settings
1501 * @return always <code>false</code>
1502 * @see com.mxgraph.view.mxGraph#isCellFoldable(java.lang.Object, boolean)
1505 public boolean isCellFoldable(final Object cell, final boolean collapse) {
1510 * Not BasicBLock cell have a moveable label.
1512 * @param cell the cell
1513 * @return true if the corresponding label is moveable
1514 * @see com.mxgraph.view.mxGraph#isLabelMovable(java.lang.Object)
1517 public boolean isLabelMovable(Object cell) {
1518 return !(cell instanceof BasicBlock);
1522 * Return true if selectable
1524 * @param cell the cell
1526 * @see com.mxgraph.view.mxGraph#isCellSelectable(java.lang.Object)
1529 public boolean isCellSelectable(final Object cell) {
1530 if (cell instanceof BasicPort) {
1533 return super.isCellSelectable(cell);
1537 * Return true if movable
1539 * @param cell the cell
1541 * @see com.mxgraph.view.mxGraph#isCellMovable(java.lang.Object)
1544 public boolean isCellMovable(final Object cell) {
1545 if (cell instanceof BasicPort) {
1549 boolean movable = false;
1550 final Object[] cells = getSelectionCells();
1552 // don't move if selection is only links
1553 for (Object c : cells) {
1554 if (!(c instanceof BasicLink)) {
1560 return movable && super.isCellMovable(cell);
1564 * Return true if resizable
1566 * @param cell the cell
1568 * @see com.mxgraph.view.mxGraph#isCellResizable(java.lang.Object)
1571 public boolean isCellResizable(final Object cell) {
1572 if (cell instanceof SplitBlock) {
1575 return (cell instanceof BasicBlock) && super.isCellResizable(cell);
1579 * A cell is deletable if it is not a locked block or an identifier cell
1581 * @param cell the cell
1583 * @see com.mxgraph.view.mxGraph#isCellDeletable(java.lang.Object)
1586 public boolean isCellDeletable(final Object cell) {
1587 final boolean isALockedBLock = cell instanceof BasicBlock && ((BasicBlock) cell).isLocked();
1588 final boolean isAnIdentifier = cell.getClass().equals(mxCell.class);
1590 if (isALockedBLock) {
1593 if (isAnIdentifier) {
1597 return super.isCellDeletable(cell);
1601 * Return true if editable
1603 * @param cell the cell
1605 * @see com.mxgraph.view.mxGraph#isCellEditable(java.lang.Object)
1608 public boolean isCellEditable(final Object cell) {
1609 return (cell instanceof TextBlock) && super.isCellDeletable(cell);
1613 * Return or create the identifier for the cell
1615 * @param cell the cell to check
1616 * @return the identifier cell
1618 public mxCell getOrCreateCellIdentifier(final mxCell cell) {
1619 if (cell.getId().endsWith(HASH_IDENTIFIER)) {
1623 final mxGraphModel graphModel = (mxGraphModel) getModel();
1625 final mxCell identifier;
1626 final String cellId = cell.getId() + HASH_IDENTIFIER;
1627 if (graphModel.getCell(cellId) == null) {
1628 // create the identifier
1629 identifier = createCellIdentifier(cell);
1631 // add the identifier
1632 graphModel.add(cell, identifier, cell.getChildCount());
1634 identifier = (mxCell) graphModel.getCell(cellId);
1640 * Return the identifier for the cell
1642 * @param cell the cell to check
1643 * @return the identifier cell
1645 public mxCell getCellIdentifier(final mxCell cell) {
1646 final mxGraphModel graphModel = (mxGraphModel) getModel();
1647 final String cellId = cell.getId() + HASH_IDENTIFIER;
1649 return (mxCell) graphModel.getCell(cellId);
1653 * Create a cell identifier for a specific cell
1655 * @param cell the cell
1656 * @return the cell identifier.
1658 public mxCell createCellIdentifier(final mxCell cell) {
1659 final XcosCell identifier;
1660 final String cellId = cell.getId() + HASH_IDENTIFIER;
1662 JavaController controller = new JavaController();
1663 long uid = controller.createObject(Kind.ANNOTATION);
1665 final mxGeometry geom;
1666 if (cell.isVertex()) {
1667 // the vertex label position is relative to its parent
1668 geom = new mxGeometry(cell.getGeometry().getWidth() * 0.5, cell.getGeometry().getHeight() * 1.1, 0., 0.);
1670 // the edge label position is absolute, its initial position should be computed
1671 mxPoint src = cell.getGeometry().getSourcePoint();
1672 mxPoint trgt = cell.getGeometry().getTargetPoint();
1674 geom = new mxGeometry(src.getX(), src.getY(), 0., 0.);
1675 geom.translate((trgt.getX() - src.getX()) / 2, (trgt.getY() - src.getY()) / 2);
1678 identifier = new XcosCell(new JavaController(), uid, Kind.ANNOTATION, null, geom, "noLabel=0;opacity=0;", cellId);
1679 identifier.setVertex(true);
1680 identifier.setConnectable(false);
1686 * Get the label for the cell according to its style.
1688 * @param cell the cell object
1689 * @return a representative the string (block name) or a style specific
1691 * @see com.mxgraph.view.mxGraph#convertValueToString(java.lang.Object)
1694 public String convertValueToString(Object cell) {
1698 JavaController controller = new JavaController();
1699 final Map<String, Object> style = getCellStyle(cell);
1701 final String displayedLabel = (String) style.get("displayedLabel");
1702 if (displayedLabel != null) {
1703 if (cell instanceof BasicBlock) {
1704 BasicBlock block = (BasicBlock) cell;
1705 VectorOfDouble v = new VectorOfDouble();
1706 controller.getObjectProperty(block.getUID(), block.getKind(), ObjectProperties.EXPRS, v);
1709 ret = new ScilabTypeCoder().format(displayedLabel, v);
1710 } catch (IllegalFormatException e) {
1711 LOG.severe(e.toString());
1712 ret = displayedLabel;
1715 ret = displayedLabel;
1718 final String label = super.convertValueToString(cell);
1719 if (label.isEmpty() && cell instanceof BasicBlock) {
1720 BasicBlock block = (BasicBlock) cell;
1722 String[] interfaceFunction = new String[1];
1723 controller.getObjectProperty(block.getUID(), block.getKind(), ObjectProperties.INTERFACE_FUNCTION, interfaceFunction);
1724 ret = interfaceFunction[0];
1735 * Return true if auto sized
1737 * @param cell the cell
1739 * @see com.mxgraph.view.mxGraph#isAutoSizeCell(java.lang.Object)
1742 public boolean isAutoSizeCell(final Object cell) {
1743 boolean status = super.isAutoSizeCell(cell);
1745 if (cell instanceof AfficheBlock) {
1749 if (cell instanceof TextBlock) {
1757 * {@inheritDoc} Do not extends if the port position is north or south.
1760 public boolean isExtendParent(Object cell) {
1761 final boolean extendsParents;
1763 if (cell instanceof BasicPort) {
1764 final BasicPort p = (BasicPort) cell;
1765 extendsParents = !(p.getOrientation() == Orientation.NORTH || p.getOrientation() == Orientation.SOUTH) && super.isExtendParent(p);
1767 extendsParents = super.isExtendParent(cell);
1769 return extendsParents;
1773 * @return the model ID
1775 public long getUID() {
1776 return ((XcosCell) getDefaultParent()).getUID();
1780 * @return Kind.DIAGRAM or Kind.BLOCK
1782 public Kind getKind() {
1783 return ((XcosCell) getDefaultParent()).getKind();
1787 * Manage the visibility of the grid and the associated menu
1789 * @param status new status
1791 public void setGridVisible(final boolean status) {
1792 setGridEnabled(status);
1793 getAsComponent().setGridVisible(status);
1794 getAsComponent().repaint();
1798 * @return save status
1800 public boolean saveDiagram() {
1801 return saveDiagramAs(getSavedFile());
1805 * @param fileName diagram filename
1806 * @return save status
1808 public boolean saveDiagramAs(final File fileName) {
1809 boolean isSuccess = false;
1810 File writeFile = fileName;
1811 XcosFileType format = XcosOptions.getPreferences().getFileFormat();
1813 info(XcosMessages.SAVING_DIAGRAM);
1814 if (fileName == null) {
1815 final SwingScilabFileChooser fc = SaveAsAction.createFileChooser();
1816 SaveAsAction.configureFileFilters(fc);
1817 ConfigurationManager.configureCurrentDirectory(fc);
1819 if (getSavedFile() != null) {
1820 // using save-as, the file chooser should have a filename
1821 // without extension as default
1822 String filename = getSavedFile().getName();
1823 filename = filename.substring(0, filename.lastIndexOf('.'));
1824 fc.setSelectedFile(new File(filename));
1826 final String title = getTitle();
1827 if (title != null) {
1829 * 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
1831 final char[] regex = "<>:\"/\\|?*".toCharArray();
1832 String escaped = title;
1833 for (char c : regex) {
1834 escaped = escaped.replace(c, '-');
1837 fc.setSelectedFile(new File(escaped));
1841 int status = fc.showSaveDialog(this.getAsComponent());
1842 if (status != JFileChooser.APPROVE_OPTION) {
1843 info(XcosMessages.EMPTY_INFO);
1847 // change the format if the user choose a save-able file format
1848 final XcosFileType selectedFilter = XcosFileType.findFileType(fc.getFileFilter());
1849 if (XcosFileType.getAvailableSaveFormats().contains(selectedFilter)) {
1850 format = selectedFilter;
1852 writeFile = fc.getSelectedFile();
1855 /* Extension/format update */
1856 // using a String filename also works on a non-existing file
1857 final String filename = writeFile.getName();
1860 * Look for the user extension if it does not exist, append a default one
1862 * if the specified extension is handled, update the save format ; else append a default extension and use the default format
1864 XcosFileType userExtension = XcosFileType.findFileType(filename);
1865 if (userExtension == null) {
1866 writeFile = new File(writeFile.getParent(), filename + format.getDottedExtension());
1867 userExtension = format;
1869 if (XcosFileType.getAvailableSaveFormats().contains(userExtension)) {
1870 format = userExtension;
1872 writeFile = new File(writeFile.getParent(), filename + format.getDottedExtension());
1876 * If the file exists, ask for confirmation if this is not the previously saved file
1878 if (writeFile.exists() && !writeFile.equals(getSavedFile())) {
1879 final boolean overwrite = ScilabModalDialog.show(XcosTab.get(this), XcosMessages.OVERWRITE_EXISTING_FILE, XcosMessages.XCOS, IconType.QUESTION_ICON,
1880 ButtonType.YES_NO) == AnswerOption.YES_OPTION;
1883 info(XcosMessages.EMPTY_INFO);
1889 * Really save the data
1892 format.save(writeFile.getCanonicalPath(), getRootDiagram());
1893 setSavedFile(writeFile);
1895 setTitle(writeFile.getName().substring(0, writeFile.getName().lastIndexOf('.')));
1896 ConfigurationManager.getInstance().addToRecentFiles(writeFile);
1898 ScicosObjectOwner root = Xcos.findRoot(this);
1899 Xcos.getInstance().setModified(root, false);
1900 Xcos.getInstance().openedDiagrams(root).stream()
1901 .forEach(d -> d.updateTabTitle());
1904 } catch (final Exception e) {
1905 LOG.severe(e.toString());
1907 XcosDialogs.couldNotSaveFile(this);
1911 info(XcosMessages.EMPTY_INFO);
1916 * Perform post loading initialization.
1918 * @param file the loaded file
1920 public void postLoad(final File file) {
1921 final String name = file.getName();
1923 if (XcosFileType.getAvailableSaveFormats().contains(XcosFileType.findFileType(file))) {
1926 setTitle(name.substring(0, name.lastIndexOf('.')));
1930 fireEvent(new mxEventObject(mxEvent.ROOT));
1932 info(XcosMessages.EMPTY_INFO);
1936 public void setSavedFile(File savedFile) {
1937 super.setSavedFile(savedFile);
1939 if (savedFile != null) {
1940 JavaController controller = new JavaController();
1941 controller.setObjectProperty(getUID(), getKind(), ObjectProperties.PATH, savedFile.getAbsolutePath());
1946 * @return the title of the current diagram (root diagram or super block)
1949 public String getTitle() {
1950 JavaController controller = new JavaController();
1951 String[] property = {""};
1953 if (getKind() == Kind.DIAGRAM) {
1954 controller.getObjectProperty(getUID(), getKind(), ObjectProperties.TITLE, property);
1955 } else { // Kind.BLOCK
1956 // use the one-line description
1957 controller.getObjectProperty(getUID(), getKind(), ObjectProperties.DESCRIPTION, property);
1960 if (property[0].isEmpty()) {
1961 return super.getTitle();
1968 * Set the title of the diagram
1970 * @param title the title
1971 * @see org.scilab.modules.graph.ScilabGraph#setTitle(java.lang.String)
1974 public void setTitle(final String title) {
1975 super.setTitle(title);
1977 JavaController controller = new JavaController();
1978 controller.setObjectProperty(getUID(), getKind(), ObjectProperties.TITLE, title);
1984 public void updateTabTitle() {
1985 // get the modifier string
1986 final String modified;
1987 if (Xcos.getInstance().isModified(Xcos.findRoot(this))) {
1993 // get the title string
1994 final String title = getTitle();
1997 CharSequence formattedPath = "";
1998 if (getKind() == Kind.DIAGRAM) {
1999 final File savedFile = getSavedFile();
2000 if (savedFile != null) {
2002 final String path = savedFile.getCanonicalPath();
2003 formattedPath = new StringBuilder().append(" (").append(path).append(')');
2004 } catch (final IOException e) {
2005 LOG.warning(e.toString());
2011 final String product = Xcos.TRADENAME;
2013 final String tabTitle = new StringBuilder().append(modified).append(title).append(formattedPath).append(" - ").append(product).toString();
2015 final SwingScilabDockablePanel tab = ScilabTabFactory.getInstance().getFromCache(getGraphTab());
2017 tab.setName(tabTitle);
2022 * Load a file with different method depending on it extension
2024 * @param controller the used controller
2025 * @param file File to load (can be null)
2027 public void transformAndLoadFile(final JavaController controller, final String file) {
2029 final XcosFileType filetype;
2032 filetype = XcosFileType.findFileType(f);
2037 new SwingWorker<XcosDiagram, ActionEvent>() {
2039 final Timer t = new Timer(1000, new ActionListener() {
2041 public void actionPerformed(ActionEvent e) {
2042 counter = (counter + 1) % (XcosMessages.DOTS.length() + 1);
2043 String str = XcosMessages.LOADING_DIAGRAM + XcosMessages.DOTS.substring(0, counter);
2045 XcosDiagram.this.info(str);
2050 protected XcosDiagram doInBackground() {
2053 final Xcos instance = Xcos.getInstance();
2054 XcosDiagram diag = XcosDiagram.this;
2056 diag.setReadOnly(true);
2059 * Load, log errors and notify
2061 synchronized (instance) {
2064 if (f != null && filetype != null) {
2065 filetype.load(file, XcosDiagram.this);
2067 XcosCellFactory.insertChildren(controller, XcosDiagram.this);
2070 instance.setLastError("");
2071 } catch (Exception e) {
2072 e.printStackTrace();
2075 while (ex instanceof RuntimeException) {
2079 instance.setLastError(ex.getMessage());
2080 } catch (NullPointerException exp) {
2081 exp.printStackTrace();
2091 protected void done() {
2093 XcosDiagram.this.setReadOnly(false);
2094 XcosDiagram.this.getUndoManager().clear();
2095 XcosDiagram.this.refresh();
2100 if (f != null && filetype != null) {
2103 XcosDiagram.this.info(XcosMessages.EMPTY_INFO);
2110 * Getting the root diagram of a decomposed diagram
2112 * @return Root parent of the whole parent
2114 public XcosDiagram getRootDiagram() {
2115 if (getKind() == Kind.DIAGRAM) {
2119 JavaController controller = new JavaController();
2121 ScicosObjectOwner root = Xcos.findRoot(controller, this);
2122 Collection<XcosDiagram> diagrams = Xcos.getInstance().getDiagrams(root);
2123 Optional<XcosDiagram> found = diagrams.stream().filter(d -> d.getUID() == root.getUID()).findFirst();
2124 if (found.isPresent()) {
2127 // create a temporary hidden root diagram
2128 String[] uid = {""};
2129 controller.getObjectProperty(root.getUID(), Kind.DIAGRAM, ObjectProperties.UID, uid);
2130 return new XcosDiagram(controller, root.getUID(), Kind.DIAGRAM, uid[0]);
2137 * Getting the root diagram UID of a decomposed diagram
2139 * @param controller the current JavaController
2140 * @return Root parent of the whole parent
2142 public long getRootDiagramUID(JavaController controller) {
2143 if (getKind() == Kind.DIAGRAM) {
2147 long[] uid = new long[1];
2148 controller.getObjectProperty(getUID(), getKind(), ObjectProperties.PARENT_DIAGRAM, uid);
2155 * Returns the tooltip to be used for the given cell.
2158 * @return cell tooltip
2161 public String getToolTipForCell(final Object cell) {
2162 if (cell instanceof BasicBlock) {
2163 return getToolTipForCell((BasicBlock) cell);
2164 } else if (cell instanceof BasicPort) {
2165 return getToolTipForCell((BasicPort) cell);
2166 } else if (cell instanceof BasicLink) {
2167 return getToolTipForCell((BasicLink) cell);
2172 private String getToolTipForCell(final BasicBlock o) {
2173 JavaController controller = new JavaController();
2174 String[] strValue = {""};
2175 VectorOfDouble vecValue = new VectorOfDouble();
2176 VectorOfInt vecInteger = new VectorOfInt();
2178 StringBuilder result = new StringBuilder();
2179 result.append(ScilabGraphConstants.HTML_BEGIN);
2181 controller.getObjectProperty(o.getUID(), o.getKind(), ObjectProperties.INTERFACE_FUNCTION, strValue);
2182 result.append(XcosMessages.TOOLTIP_BLOCK).append(ScilabGraphConstants.HTML_BEGIN_CODE)
2183 .append(strValue[0])
2184 .append(ScilabGraphConstants.HTML_END_CODE).append(ScilabGraphConstants.HTML_NEWLINE);
2186 controller.getObjectProperty(o.getUID(), o.getKind(), ObjectProperties.SIM_FUNCTION_NAME, strValue);
2187 result.append(XcosMessages.TOOLTIP_BLOCK_SIMULATION).append(ScilabGraphConstants.HTML_BEGIN_CODE)
2188 .append(strValue[0])
2189 .append(ScilabGraphConstants.HTML_END_CODE).append(ScilabGraphConstants.HTML_NEWLINE);
2191 controller.getObjectProperty(o.getUID(), o.getKind(), ObjectProperties.UID, strValue);
2192 result.append(XcosMessages.TOOLTIP_BLOCK_UID).append(ScilabGraphConstants.HTML_BEGIN_CODE)
2193 .append(strValue[0])
2194 .append(ScilabGraphConstants.HTML_END_CODE).append(ScilabGraphConstants.HTML_NEWLINE);
2196 controller.getObjectProperty(o.getUID(), o.getKind(), ObjectProperties.STYLE, strValue);
2197 result.append(XcosMessages.TOOLTIP_BLOCK_STYLE).append(ScilabGraphConstants.HTML_BEGIN_CODE);
2198 appendReduced(result, strValue[0])
2199 .append(ScilabGraphConstants.HTML_END_CODE).append(ScilabGraphConstants.HTML_NEWLINE);
2201 result.append(ScilabGraphConstants.HTML_NEWLINE);
2203 controller.getObjectProperty(o.getUID(), o.getKind(), ObjectProperties.RPAR, vecValue);
2204 result.append(XcosMessages.TOOLTIP_BLOCK_RPAR).append(ScilabGraphConstants.HTML_BEGIN_CODE);
2205 appendReduced(result, ScilabTypeCoder.toString(vecValue))
2206 .append(ScilabGraphConstants.HTML_END_CODE).append(ScilabGraphConstants.HTML_NEWLINE);
2208 controller.getObjectProperty(o.getUID(), o.getKind(), ObjectProperties.IPAR, vecInteger);
2209 result.append(XcosMessages.TOOLTIP_BLOCK_IPAR).append(ScilabGraphConstants.HTML_BEGIN_CODE);
2210 appendReduced(result, ScilabTypeCoder.toString(vecInteger))
2211 .append(ScilabGraphConstants.HTML_END_CODE).append(ScilabGraphConstants.HTML_NEWLINE);
2213 controller.getObjectProperty(o.getUID(), o.getKind(), ObjectProperties.OPAR, vecValue);
2214 result.append(XcosMessages.TOOLTIP_BLOCK_OPAR).append(ScilabGraphConstants.HTML_BEGIN_CODE);
2215 appendReduced(result, ScilabTypeCoder.toString(vecValue))
2216 .append(ScilabGraphConstants.HTML_END_CODE).append(ScilabGraphConstants.HTML_NEWLINE);
2218 result.append(ScilabGraphConstants.HTML_END);
2219 return result.toString();
2222 private String getToolTipForCell(final BasicPort o) {
2223 JavaController controller = new JavaController();
2224 boolean[] boolValue = {false};
2225 String[] strValue = {""};
2226 VectorOfInt intVecValue = new VectorOfInt();
2228 StringBuilder result = new StringBuilder();
2229 result.append(ScilabGraphConstants.HTML_BEGIN);
2231 controller.getObjectProperty(o.getUID(), o.getKind(), ObjectProperties.DATATYPE, intVecValue);
2232 result.append(XcosMessages.TOOLTIP_PORT_DATATYPE).append(ScilabGraphConstants.HTML_BEGIN_CODE);
2233 formatDatatype(result, intVecValue)
2234 .append(ScilabGraphConstants.HTML_END_CODE).append(ScilabGraphConstants.HTML_NEWLINE);
2236 controller.getObjectProperty(o.getUID(), o.getKind(), ObjectProperties.IMPLICIT, boolValue);
2237 result.append(XcosMessages.TOOLTIP_PORT_IMPLICIT).append(ScilabGraphConstants.HTML_BEGIN_CODE)
2238 .append(boolValue[0])
2239 .append(ScilabGraphConstants.HTML_END_CODE).append(ScilabGraphConstants.HTML_NEWLINE);
2241 controller.getObjectProperty(o.getUID(), o.getKind(), ObjectProperties.STYLE, strValue);
2242 result.append(XcosMessages.TOOLTIP_PORT_STYLE).append(ScilabGraphConstants.HTML_BEGIN_CODE);
2243 appendReduced(result, strValue[0])
2244 .append(ScilabGraphConstants.HTML_END_CODE).append(ScilabGraphConstants.HTML_NEWLINE);
2246 result.append(ScilabGraphConstants.HTML_END);
2247 return result.toString();
2250 private String getToolTipForCell(final BasicLink o) {
2251 JavaController controller = new JavaController();
2252 long[] longValue = {0l};
2253 String[] strValue = {""};
2254 VectorOfInt intVecValue = new VectorOfInt();
2256 StringBuilder result = new StringBuilder();
2257 result.append(ScilabGraphConstants.HTML_BEGIN);
2259 controller.getObjectProperty(o.getUID(), o.getKind(), ObjectProperties.SOURCE_PORT, longValue);
2260 if (longValue[0] != 0l) {
2261 controller.getObjectProperty(longValue[0], Kind.PORT, ObjectProperties.DATATYPE, intVecValue);
2262 result.append(XcosMessages.TOOLTIP_LINK_SRC_DATATYPE).append(ScilabGraphConstants.HTML_BEGIN_CODE);
2263 formatDatatype(result, intVecValue)
2264 .append(ScilabGraphConstants.HTML_END_CODE).append(ScilabGraphConstants.HTML_NEWLINE);
2266 controller.getObjectProperty(o.getUID(), o.getKind(), ObjectProperties.DESTINATION_PORT, longValue);
2267 if (longValue[0] != 0l) {
2268 controller.getObjectProperty(longValue[0], Kind.PORT, ObjectProperties.DATATYPE, intVecValue);
2269 result.append(XcosMessages.TOOLTIP_LINK_TRG_DATATYPE).append(ScilabGraphConstants.HTML_BEGIN_CODE);
2270 formatDatatype(result, intVecValue)
2271 .append(ScilabGraphConstants.HTML_END_CODE).append(ScilabGraphConstants.HTML_NEWLINE);
2274 controller.getObjectProperty(o.getUID(), o.getKind(), ObjectProperties.LABEL, strValue);
2275 result.append(XcosMessages.TOOLTIP_LINK_LABEL).append(ScilabGraphConstants.HTML_BEGIN_CODE)
2276 .append(strValue[0])
2277 .append(ScilabGraphConstants.HTML_END_CODE).append(ScilabGraphConstants.HTML_NEWLINE);
2279 controller.getObjectProperty(o.getUID(), o.getKind(), ObjectProperties.STYLE, strValue);
2280 result.append(XcosMessages.TOOLTIP_LINK_STYLE).append(ScilabGraphConstants.HTML_BEGIN_CODE);
2281 appendReduced(result, strValue[0])
2282 .append(ScilabGraphConstants.HTML_END_CODE).append(ScilabGraphConstants.HTML_NEWLINE);
2284 result.append(ScilabGraphConstants.HTML_END);
2285 return result.toString();
2288 private StringBuilder appendReduced(final StringBuilder result, final String msg) {
2289 if (msg.length() > XcosConstants.MAX_CHAR_IN_STYLE) {
2290 result.append(msg.substring(0, XcosConstants.MAX_CHAR_IN_STYLE));
2291 result.append(XcosMessages.DOTS);
2299 private StringBuilder formatDatatype(final StringBuilder result, final VectorOfInt intVecValue) {
2300 if (intVecValue.size() != 3) {
2301 result.append(ScilabTypeCoder.toString(intVecValue));
2303 // this is a known encoding, output representative strings
2304 int rows = intVecValue.get(0);
2305 int cols = intVecValue.get(1);
2306 int type = intVecValue.get(2);
2309 // should be similar to the naming used on scicos_model doc
2310 String[] typeTable = {"real", "complex", "int32", "int16", "int8", "uint32", "uint16", "uint8"};
2311 if (0 <= type && type < typeTable.length) {
2312 strType = typeTable[type - 1];
2317 result.append(String.format("%s [%d %d]", strType, rows, cols));
2324 * Display the message in info bar.
2326 * @param message Information
2328 public void info(final String message) {
2329 final XcosTab tab = XcosTab.get(this);
2331 if (tab != null && tab.getInfoBar() != null) {
2332 tab.getInfoBar().setText(message);
2337 * Display the message into an error popup
2339 * @param message Error of the message
2341 public void error(final String message) {
2342 JOptionPane.showMessageDialog(getAsComponent(), message, XcosMessages.XCOS, JOptionPane.ERROR_MESSAGE);
2346 * Find the block corresponding to the given uid and display a warning
2349 * @param uid - A String as UID.
2350 * @param message - The message to display.
2352 public void warnCellByUID(final String uid, final String message) {
2353 final Object cell = ((mxGraphModel) getModel()).getCell(uid);
2358 if (GraphicsEnvironment.isHeadless()) {
2359 System.err.printf("%s at %s%n %s: %s%n", "warnCell", getRootDiagram().getTitle(), uid, message);
2364 if (XcosTab.get(this) == null) {
2365 XcosTab.restore(this);
2368 if (message.isEmpty()) {
2369 getAsComponent().clearCellOverlays(cell);
2371 getAsComponent().setCellWarning(cell, message, null, true);
2376 public File getSavedFile() {
2377 if (getKind() == Kind.DIAGRAM) {
2378 return super.getSavedFile();
2380 return getRootDiagram().getSavedFile();
2385 * Read the applicable context on this diagram.
2387 * This function retrieve the current diagram's context and all its parent
2389 * @return the full context
2391 public String[] getContext() {
2392 final ArrayList<String> allContext = new ArrayList<>();
2393 final Stack<ScicosObjectOwner> hierarchy = lookForHierarchy(new ScicosObjectOwner(getUID(), getKind()));
2395 final JavaController controller = new JavaController();
2396 final VectorOfString context = new VectorOfString();
2398 hierarchy.stream().forEach(o -> {
2399 controller.getObjectProperty(o.getUID(), o.getKind(), ObjectProperties.DIAGRAM_CONTEXT, context);
2401 final int length = context.size();
2402 for (int i = 0; i < length; i++) {
2403 allContext.add(context.get(i));
2408 return allContext.toArray(new String[allContext.size()]);
2412 * Evaluate the current context
2414 * @return The resulting data. Keys are variable names and Values are
2417 public Map<String, String> evaluateContext() {
2418 Map<String, String> result = Collections.emptyMap();
2419 final ScilabDirectHandler handler = ScilabDirectHandler.acquire();
2420 if (handler == null) {
2425 // first write the context strings
2426 handler.writeContext(getContext());
2428 // evaluate using script2var
2429 ScilabInterpreterManagement.synchronousScilabExec(ScilabDirectHandler.CONTEXT + " = script2var(" + ScilabDirectHandler.CONTEXT + ", struct());");
2431 // read the structure
2432 result = handler.readContext();
2433 } catch (final InterpreterException e) {
2434 info("Unable to evaluate the context");
2435 e.printStackTrace();
2444 * Returns true if the given cell is a not a block nor a port.
2446 * @param cell the drop target
2447 * @param cells the cells to be dropped
2448 * @return the drop status
2449 * @see com.mxgraph.view.mxGraph#isValidDropTarget(java.lang.Object,
2450 * java.lang.Object[])
2453 public boolean isValidDropTarget(final Object cell, final Object[] cells) {
2454 return !(cell instanceof BasicBlock) && !(cell instanceof BasicBlock) && !(cell instanceof BasicPort) && super.isValidDropTarget(cell, cells);
2458 * Construct a new selection model used on this graph.
2460 * @return a new selection model instance.
2461 * @see com.mxgraph.view.mxGraph#createSelectionModel()
2464 protected mxGraphSelectionModel createSelectionModel() {
2465 return new mxGraphSelectionModel(this) {
2467 * When we only want to select a cell which is a port, select the
2470 * @param cell the cell
2472 * com.mxgraph.view.mxGraphSelectionModel#setCell(java.lang.Object)
2475 public void setCell(final Object cell) {
2476 final Object current;
2477 if (cell instanceof BasicPort) {
2478 current = getModel().getParent(cell);
2482 super.setCell(current);
2488 * Counts the number of children in the diagram
2490 * @return the number of children in the diagram
2492 public int countChildren() {
2493 return getModel().getChildCount(this.getDefaultParent());