Xcos: de-duplicate ports on loading and DnD 13/17513/6
Clément DAVID [Mon, 30 Nov 2015 10:27:05 +0000 (11:27 +0100)]
To avoid duplication of ports we have to implement JGraphX `removeFromParent`
method. This method will be used to remove before add any XcosCell.

Change-Id: I12cd37971f7c67112d705aff59415a19a5328767

scilab/modules/xcos/src/java/org/scilab/modules/xcos/graph/model/XcosCell.java
scilab/modules/xcos/src/java/org/scilab/modules/xcos/graph/model/XcosCellFactory.java
scilab/modules/xcos/src/java/org/scilab/modules/xcos/io/sax/CustomHandler.java
scilab/modules/xcos/src/java/org/scilab/modules/xcos/io/sax/XcosSAXHandler.java

index 179ba37..ea8aba1 100644 (file)
@@ -49,6 +49,11 @@ public class XcosCell extends ScilabGraphUniqueObject {
         if (uid == 0l) {
             throw new IllegalArgumentException();
         }
+
+        /* Store default values to the C++ side */
+        JavaController controller = new JavaController();
+        setValue(controller, value);
+        setId(controller, id);
     }
 
     /**
@@ -81,7 +86,10 @@ public class XcosCell extends ScilabGraphUniqueObject {
             return;
         }
 
-        JavaController controller = new JavaController();
+        setValue(new JavaController(), value);
+    }
+
+    private void setValue(JavaController controller, Object value) {
         switch (getKind()) {
             case ANNOTATION:
                 controller.setObjectProperty(getUID(), getKind(), ObjectProperties.DESCRIPTION, String.valueOf(value));
@@ -105,7 +113,10 @@ public class XcosCell extends ScilabGraphUniqueObject {
     public void setId(String id) {
         super.setId(id);
 
-        JavaController controller = new JavaController();
+        setId(new JavaController(), id);
+    }
+
+    private void setId(JavaController controller, String id) {
         switch (getKind()) {
             case ANNOTATION:
             case BLOCK:
@@ -130,7 +141,10 @@ public class XcosCell extends ScilabGraphUniqueObject {
             return;
         }
 
-        JavaController controller = new JavaController();
+        setGeometry(new JavaController(), geometry);
+    }
+
+    private void setGeometry(JavaController controller, mxGeometry geometry) {
         switch (getKind()) {
             case ANNOTATION:
             case BLOCK: {
@@ -148,7 +162,6 @@ public class XcosCell extends ScilabGraphUniqueObject {
                  */
                 mxPoint sourcePoint = null;
                 mxPoint targetPoint = null;
-
                 mxICell sourceCell = getSource();
                 mxICell targetCell = getTarget();
                 if (sourceCell != null && sourceCell.getGeometry() != null) {
@@ -157,49 +170,42 @@ public class XcosCell extends ScilabGraphUniqueObject {
                 if (targetCell != null && targetCell.getGeometry() != null) {
                     targetPoint = new mxPoint(targetCell.getGeometry().getCenterX(), targetCell.getGeometry().getCenterY());
                 }
-
                 if (sourcePoint == null) {
                     sourcePoint = geometry.getSourcePoint();
                 }
                 if (targetPoint == null) {
                     targetPoint = geometry.getTargetPoint();
                 }
-
                 if (sourcePoint == null) {
                     sourcePoint = new mxPoint();
                 }
                 if (targetPoint == null) {
                     targetPoint = new mxPoint();
                 }
-
                 List<mxPoint> points = geometry.getPoints();
                 if (points == null) {
                     points = Collections.emptyList();
                 }
 
                 /*
-                 * At that point, the sourcePoint, targetPoint and points are valid values (but may be unknown) encode them to the the CONTROL_POINTS
-                 */
+                * At that point, the sourcePoint, targetPoint and points are valid values (but may be unknown) encode them to the the CONTROL_POINTS
+                */
 
                 // Allocate some space to contains them all
                 int nbOfPoints = 2 + points.size();
                 VectorOfDouble v = new VectorOfDouble(2 * nbOfPoints);
                 int i = 0;
-
                 // then fill the allocation space
                 v.set(2 * i, sourcePoint.getX());
                 v.set(2 * i + 1, sourcePoint.getY());
                 i++;
-
                 for (; i < nbOfPoints - 1; i++) {
                     v.set(2 * i, points.get(i - 1).getX());
                     v.set(2 * i + 1, points.get(i - 1).getY());
                 }
-
                 v.set(2 * i, targetPoint.getX());
                 v.set(2 * i + 1, targetPoint.getY());
                 i++;
-
                 /*
                  * Finally push the values to the model
                  */
@@ -223,7 +229,10 @@ public class XcosCell extends ScilabGraphUniqueObject {
             return;
         }
 
-        JavaController controller = new JavaController();
+        setStyle(new JavaController(), style);
+    }
+
+    private void setStyle(JavaController controller, String style) {
         switch (getKind()) {
             case ANNOTATION:
             case BLOCK:
@@ -233,7 +242,88 @@ public class XcosCell extends ScilabGraphUniqueObject {
             default:
                 break;
         }
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see com.mxgraph.model.mxCell#removeFromParent()
+     */
+    @Override
+    public void removeFromParent() {
+        JavaController controller = new JavaController();
+        switch (getKind()) {
+            case ANNOTATION:
+            case BLOCK:
+            case LINK: {
+                /*
+                 * Retrieve the parent
+                 */
+                long[] parent = new long[1];
+                Kind parentKind = Kind.BLOCK;
+                ObjectProperties prop = ObjectProperties.PARENT_BLOCK;
+                controller.getObjectProperty(getUID(), getKind(), prop, parent);
+                if (parent[0] == 0l) {
+                    parentKind = Kind.DIAGRAM;
+                    prop = ObjectProperties.PARENT_DIAGRAM;
+                    controller.getObjectProperty(getUID(), getKind(), prop, parent);
+                }
+
+                /*
+                 * If there is a parent, clear it
+                 */
+                if (parent[0] == 0l) {
+                    VectorOfScicosID children = new VectorOfScicosID();
+                    controller.getObjectProperty(parent[0], parentKind, ObjectProperties.CHILDREN, children);
+                    children.remove(getUID());
+                    controller.setObjectProperty(parent[0], parentKind, ObjectProperties.CHILDREN, children);
+
+                    controller.setObjectProperty(getUID(), getKind(), prop, 0l);
+                }
+                break;
+            }
+            case PORT: {
+                long[] parent = new long[1];
+                Kind parentKind = Kind.BLOCK;
+                controller.getObjectProperty(getUID(), getKind(), ObjectProperties.SOURCE_BLOCK, parent);
+
+                int[] portKind = new int[1];
+                controller.getObjectProperty(getUID(), getKind(), ObjectProperties.PORT_KIND, portKind);
+                ObjectProperties property = relatedPortKindProperty(portKind[0]);
 
+                VectorOfScicosID ports = new VectorOfScicosID();
+                controller.getObjectProperty(parent[0], parentKind, property, ports);
+                ports.remove(getUID());
+                controller.setObjectProperty(parent[0], parentKind, property, ports);
+
+                controller.setObjectProperty(getUID(), getKind(), ObjectProperties.SOURCE_BLOCK, 0l);
+                break;
+            }
+            default:
+                break;
+        }
+    }
+
+    private ObjectProperties relatedPortKindProperty(int portKind) {
+        ObjectProperties property;
+        switch (PortKind.values()[portKind]) {
+            case PORT_IN:
+                property = ObjectProperties.INPUTS;
+                break;
+            case PORT_OUT:
+                property = ObjectProperties.OUTPUTS;
+                break;
+            case PORT_EIN:
+                property = ObjectProperties.EVENT_INPUTS;
+                break;
+            case PORT_EOUT:
+                property = ObjectProperties.EVENT_OUTPUTS;
+                break;
+            default:
+                property = null;
+                break;
+        }
+        return property;
     }
 
     /*
@@ -342,24 +432,8 @@ public class XcosCell extends ScilabGraphUniqueObject {
         controller.getObjectProperty(c.getUID(), c.getKind(), ObjectProperties.PORT_KIND, v);
 
         VectorOfScicosID children = new VectorOfScicosID();
-        final ObjectProperties property;
-        switch (PortKind.values()[v[0]]) {
-            case PORT_IN:
-                property = ObjectProperties.INPUTS;
-                break;
-            case PORT_OUT:
-                property = ObjectProperties.OUTPUTS;
-                break;
-            case PORT_EIN:
-                property = ObjectProperties.EVENT_INPUTS;
-                break;
-            case PORT_EOUT:
-                property = ObjectProperties.EVENT_OUTPUTS;
-                break;
-            default:
-                property = null;
-                break;
-        }
+        final ObjectProperties property = relatedPortKindProperty(v[0]);
+
 
         // FIXME manage the index argument, possibly by counting the JGraphX children by kind
         if (property != null) {
@@ -459,7 +533,11 @@ public class XcosCell extends ScilabGraphUniqueObject {
     public Object clone() throws CloneNotSupportedException {
         JavaController controller = new JavaController();
         XcosCell c = (XcosCell) super.clone();
+
         c.owner = new ScicosObjectOwner(controller.cloneObject(getUID(), false, false), getKind());
+        c.setValue(controller, c.getValue());
+        c.setId(controller, c.getId());
+
         return c;
     }
 }
index 222d2c9..6c26ee2 100644 (file)
@@ -50,6 +50,10 @@ import org.scilab.modules.xcos.utils.BlockPositioning;
 import com.mxgraph.model.mxCell;
 import com.mxgraph.model.mxGeometry;
 import com.mxgraph.util.mxPoint;
+import java.util.EnumMap;
+import org.scilab.modules.xcos.block.SplitBlock;
+import org.scilab.modules.xcos.block.positionning.RoundBlock;
+;
 
 /**
  * Ease the creation of any {@link Kind} of graphical object
@@ -145,7 +149,6 @@ public final class XcosCellFactory {
          */
         VectorOfScicosID children = new VectorOfScicosID();
         controller.getObjectProperty(diagram.getUID(), diagram.getKind(), ObjectProperties.CHILDREN, children);
-        controller.setObjectProperty(diagram.getUID(), diagram.getKind(), ObjectProperties.CHILDREN, new VectorOfScicosID());
         final int childrenLen = children.size();
 
         /*
@@ -209,6 +212,7 @@ public final class XcosCellFactory {
             }
         }
 
+        // re-add the children cells
         diagram.addCells(cells);
     }
 
@@ -340,20 +344,17 @@ public final class XcosCellFactory {
          *
          * Annotations have no inputs/outputs
          */
+        EnumMap<ObjectProperties, Integer> properties = new EnumMap<>(ObjectProperties.class);
+        properties.put(ObjectProperties.INPUTS, 0);
+        properties.put(ObjectProperties.OUTPUTS, 0);
+        properties.put(ObjectProperties.EVENT_INPUTS, 0);
+        properties.put(ObjectProperties.EVENT_OUTPUTS, 0);
         if (block.getKind() == Kind.BLOCK) {
-            insertPortChildren(controller, block);
+            insertPortChildren(controller, properties, block);
         }
-        final boolean convertGeometry;
 
         String[] strUID = new String[1];
         controller.getObjectProperty(block.getUID(), block.getKind(), ObjectProperties.UID, strUID);
-        if (strUID[0].isEmpty()) {
-            // this is a new block, convert the geom and positions
-            convertGeometry = true;
-        } else {
-            block.setId(strUID[0]);
-            convertGeometry = false;
-        }
 
         String[] style = new String[1];
         controller.getObjectProperty(block.getUID(), block.getKind(), ObjectProperties.STYLE, style);
@@ -366,13 +367,33 @@ public final class XcosCellFactory {
         VectorOfDouble geom = new VectorOfDouble(4);
         controller.getObjectProperty(block.getUID(), block.getKind(), ObjectProperties.GEOMETRY, geom);
 
-        /*
-         * Compatibility to ease user definition
-         */
         double x = geom.get(0);
         double y = geom.get(1);
         double w = geom.get(2);
         double h = geom.get(3);
+
+        /*
+         * Compatibility to ease user definition :
+         *   * split are not updated as they have an hard-coded fixed-size
+         *   * round blocks : ports globally layout around the block
+         *   * generic case : layout the ports per kind per block-side
+         */
+        boolean convertGeometry;
+        if ((block instanceof SplitBlock)) {
+            convertGeometry = false;
+        } else if (block instanceof RoundBlock) {
+            int numberOfPorts = properties.get(ObjectProperties.INPUTS) +
+                                properties.get(ObjectProperties.OUTPUTS) +
+                                properties.get(ObjectProperties.EVENT_INPUTS) +
+                                properties.get(ObjectProperties.EVENT_OUTPUTS);
+            convertGeometry = (2 * w + 2 * h) < (numberOfPorts * BasicPort.DEFAULT_PORTSIZE);
+        } else {
+            convertGeometry = w < (properties.get(ObjectProperties.INPUTS) * BasicPort.DEFAULT_PORTSIZE) |
+                              w < (properties.get(ObjectProperties.OUTPUTS) * BasicPort.DEFAULT_PORTSIZE) |
+                              h < (properties.get(ObjectProperties.EVENT_INPUTS) * BasicPort.DEFAULT_PORTSIZE) |
+                              h < (properties.get(ObjectProperties.EVENT_OUTPUTS) * BasicPort.DEFAULT_PORTSIZE);
+        }
+
         if (convertGeometry) {
             w = w * DEFAULT_SIZE_FACTOR;
             h = h * DEFAULT_SIZE_FACTOR;
@@ -418,22 +439,6 @@ public final class XcosCellFactory {
      *
      * @param controller
      *            is the shared controller instance
-     * @param parent
-     *            is the parent {@link mxCell} to modify
-     */
-    private static void insertPortChildren(final JavaController controller, final XcosCell parent) {
-        final EnumSet<ObjectProperties> properties = EnumSet.of(ObjectProperties.INPUTS, ObjectProperties.OUTPUTS, ObjectProperties.EVENT_INPUTS,
-                ObjectProperties.EVENT_OUTPUTS);
-        insertPortChildren(controller, properties, parent);
-    }
-
-    /**
-     * Helper used to create port children on a parent block.
-     *
-     * This method does not manage the model transaction and should be used to preset the children of a block out of an {@link XcosDiagram}.
-     *
-     * @param controller
-     *            is the shared controller instance
      * @param properties
      *            specify the kind of port to insert and should be some of :
      *            <UL>
@@ -441,12 +446,14 @@ public final class XcosCellFactory {
      *            <LI>{@link ObjectProperties#OUTPUTS}
      *            <LI>{@link ObjectProperties#EVENT_INPUTS}
      *            <LI>{@link ObjectProperties#EVENT_OUTPUTS}
+     *            </UL>
+     *            This method will fill the value with the number of added ports
      * @param parent
      *            is the parent {@link mxCell} to modify
      */
-    private static void insertPortChildren(final JavaController controller, final EnumSet<ObjectProperties> properties, final XcosCell parent) {
-        for (ObjectProperties property : properties) {
-            insertPortChildren(controller, property, parent);
+    private static void insertPortChildren(final JavaController controller, final EnumMap<ObjectProperties, Integer> properties, final XcosCell parent) {
+        for (ObjectProperties property : properties.keySet()) {
+            properties.put(property, insertPortChildren(controller, property, parent));
         }
     }
 
@@ -466,8 +473,9 @@ public final class XcosCellFactory {
      *            <LI>{@link ObjectProperties#EVENT_OUTPUTS}
      * @param parent
      *            is the parent {@link mxCell} to modify
+     * @return the number of inserted children
      */
-    private static void insertPortChildren(final JavaController controller, final ObjectProperties property, final XcosCell parent) {
+    private static int insertPortChildren(final JavaController controller, final ObjectProperties property, final XcosCell parent) {
         VectorOfScicosID modelChildren = new VectorOfScicosID();
         controller.getObjectProperty(parent.getUID(), parent.getKind(), property, modelChildren);
 
@@ -476,7 +484,10 @@ public final class XcosCellFactory {
             XcosCell child = createPort(controller, modelChildren.get(i), property);
             children[i] = child;
         }
+
         Arrays.stream(children).forEach(c -> parent.insert(c));
+
+        return children.length;
     }
 
     /**
index f263d78..ab620ad 100644 (file)
@@ -168,7 +168,7 @@ class CustomHandler implements ScilabHandler {
                  * Update some states
                  */
                 saxHandler.allChildren.push(new HashMap<>());
-                return new ScicosObjectOwner(uid, kind);
+                return new XcosCell(uid, kind);
             default:
                 throw new IllegalArgumentException();
         }
index 82655c2..634be7b 100644 (file)
@@ -76,9 +76,6 @@ public class XcosSAXHandler extends DefaultHandler {
     }
 
     XcosCell lookupForParentXcosCellElement() {
-        // TODO in fact we can use the depth (eg. size) of the stack to retrieve
-        // this value
-        // is it necessary to improve performance (over safety) there ?
         Optional<XcosCell> parentBlock = parents.stream()
                                          .filter(o -> o instanceof XcosCell)
                                          .map(o -> (XcosCell) o)