All Downloads are FREE. Search and download functionalities are using the official Maven repository.

eu.mihosoft.vrl.workflow.fx.NodeUtil Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2012-2021 Michael Hoffer . All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification, are
 * permitted provided that the following conditions are met:
 *
 *    1. Redistributions of source code must retain the above copyright notice, this list of
 *       conditions and the following disclaimer.
 *
 *    2. Redistributions in binary form must reproduce the above copyright notice, this list
 *       of conditions and the following disclaimer in the documentation and/or other materials
 *       provided with the distribution.
 *
 * Please cite the following publication(s):
 *
 * M. Hoffer, C.Poliwoda, G.Wittum. Visual Reflection Library -
 * A Framework for Declarative GUI Programming on the Java Platform.
 * Computing and Visualization in Science, 2011, in press.
 *
 * THIS SOFTWARE IS PROVIDED BY Michael Hoffer  "AS IS" AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Michael Hoffer  OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * The views and conclusions contained in the software and documentation are those of the
 * authors and should not be interpreted as representing official policies, either expressed
 * or implied, of Michael Hoffer .
 */
package eu.mihosoft.vrl.workflow.fx;

import eu.mihosoft.vrl.workflow.MouseButton;
import javafx.geometry.Point2D;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Pane;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * Utility class that provides methods to simplify node handling. Possible use
 * cases are searching for nodes at specific locations, adding/removing nodes
 * to/from parents (Parent interface does not give write access to children).
 *
 * @author Michael Hoffer <[email protected]>
 */
public class NodeUtil {

    // no instanciation allowed
    private NodeUtil() {
        throw new AssertionError(); // not in this class either!
    }

    /**
     *
     * @param node
     * @return The X screen coordinate of the node.
     */
    static public double screenX(Node node) {
        return node.localToScene(node.getBoundsInLocal()).getMinX() + node.getScene().getX() + node.getScene().getWindow().getX();

    }

    /**
     *
     * @param node
     * @return The Y screen coordinate of the node.
     */
    static public double screenY(Node node) {
        return node.localToScene(node.getBoundsInLocal()).getMinY() + node.getScene().getY() + node.getScene().getWindow().getY();
    }

    static public Point2D transformCoordinates(double x, double y, Node from, Node to) {

        if (from == to || from == null || to == null) {
            return new Point2D(x, y);
        }

        // from -> scene
        Point2D fromInSceneCoordinates = new Point2D(
                (x) * from.localToSceneTransformProperty().get().getMxx(),
                (y) * from.localToSceneTransformProperty().get().getMyy());

        // scene -> to
        return new Point2D(
                (fromInSceneCoordinates.getX() + from.localToSceneTransformProperty().get().getTx())
                / to.getLocalToSceneTransform().getMxx(),
                (fromInSceneCoordinates.getY() + from.localToSceneTransformProperty().get().getTy())
                / to.getLocalToSceneTransform().getMyy());

    }

    /**
     * Returns all common ancestors of the specified nodes.
     *
     * @param n1 first scene graph node
     * @param n2 second scene graph node
     * @return a list that contains all common ancestors of the specified nodes
     */
    static public List getCommonAncestors(Node n1, Node n2) {
        Parent p = null;

        List n1Parents = getAncestors(n1);
        List n2Parents = getAncestors(n2);

        n1Parents.retainAll(n2Parents);

        return n1Parents;
    }

    /**
     * Returns all ancestors of the specified node.
     *
     * @param n scene graph node
     * @return a list that contains all ancestors of the specified node
     */
    public static List getAncestors(Node n) {

        return getAncestors(n, null);
    }

    /**
     * Returns the stylesheets of the node and all ancestors of the specified
     * node.
     *
     * @param n scene graph node
     * @return a list that contains the stylesheets of the node and all
     * ancestors of the specified node
     */
    public static List getStylesheetsOfAncestors(Node n) {

//        System.out.println(">> searching stylesheets of ancestors of: " + n);
        List result = new ArrayList<>();

        if (n instanceof Parent) {

//            for (String css : ((Parent) n).getStylesheets()) {
//                System.out.println(" --> n-css: " + css);
//            }
            result.addAll(((Parent) n).getStylesheets());
        }

        Scene scene = null;

        for (Parent p : getAncestors(n)) {

//            System.out.println(" --> parent: " + p);
//
//            for (String css : p.getStylesheets()) {
//                System.out.println("  --> p-css: " + p + ": " + css);
//            }
            result.addAll(p.getStylesheets());

            if (scene == null) {
                scene = p.getScene();
            }
        }

        if (scene != null) {

//            System.out.println(" --> scene: " + scene);
            result.addAll(scene.getStylesheets());

//            for (String css : scene.getStylesheets()) {
//                System.out.println("  --> scene-css: " + css);
//            }
//
//            System.out.println(" --> done");
        }

        return result;
    }

    /**
     * Returns all ancestors of the specified node till the specified one is
     * reached.
     *
     * @param n scene graph node
     * @param parent scene graph parent
     * @return a list that contains all ancestors of the specified node
     */
    public static List getAncestors(Node n, Parent parent) {
        List nParents = new ArrayList<>();

        Parent p = n.getParent();

        while (p != null && p != parent) {
            nParents.add(p);
            p = p.getParent();
        }

        return nParents;
    }

    /**
     * Removes the specified node from its parent.
     *
     * @param n the node to remove
     *
     * @throws IllegalArgumentException if an unsupported parent class has been
     * specified or the parent is null
     */
    public static void removeFromParent(Node n) {
        if (n.getParent() instanceof Group) {
            ((Group) n.getParent()).getChildren().remove(n);
        } else if (n.getParent() instanceof Pane) {
            ((Pane) n.getParent()).getChildren().remove(n);
        } else {
            throw new IllegalArgumentException("Unsupported parent: " + n.getParent());
        }
    }

    /**
     * Adds the given node to the specified parent.
     *
     * @param p parent
     * @param n node
     *
     * @throws IllegalArgumentException if an unsupported parent class has been
     * specified or the parent is null
     */
    public static void addToParent(Parent p, Node n) {
        if (p instanceof Group) {
            ((Group) p).getChildren().add(n);
        } else if (p instanceof Pane) {
            ((Pane) p).getChildren().add(n);
        } else if (p instanceof ScalableContentPane) {
            ((Pane)((ScalableContentPane) p).getContent()).getChildren().add(n);
        } else {
            throw new IllegalArgumentException("Unsupported parent: " + p);
        }
    }

    /**
     * Adds the given node to the specified parent.
     *
     * @param p parent
     * @param n node
     * @param index child index (z buffer)
     * @throws IllegalArgumentException if an unsupported parent class has been
     * specified or the parent is null
     */
    public static void addToParent(Parent p, Node n, int index) {
        if (p instanceof Group) {
            ((Group) p).getChildren().add(index, n);
        } else if (p instanceof Pane) {
            ((Pane) p).getChildren().add(index, n);
        } else {
            throw new IllegalArgumentException("Unsupported parent: " + p);
        }
    }

    /**
     * Returns the first node at the given location that is an instance of the
     * specified class object. The search is performed recursively until either
     * a node has been found or a leaf node is reached.
     *
     * @param p parent node
     * @param sceneX x coordinate
     * @param sceneY y coordinate
     * @param nodeClass node class to search for
     * @return a node that contains the specified screen coordinates and is an
     * instance of the specified class or null if no such node
     * exist
     */
    public static Node getNode(Parent p, double sceneX, double sceneY, Class nodeClass) {

        // dammit! javafx uses "wrong" children order.
        List rightOrder = new ArrayList<>();
        rightOrder.addAll(p.getChildrenUnmodifiable());
        Collections.reverse(rightOrder);

        for (Node n : rightOrder) {
            boolean contains = n.contains(n.sceneToLocal(sceneX, sceneY));

            if (contains) {

                if (nodeClass.isAssignableFrom(n.getClass())) {
                    return n;
                }

                if (n instanceof Parent) {
                    return getNode((Parent) n, sceneX, sceneY, nodeClass);
                }
            }
        }

        return null;
    }

    /**
     * Returns the deepest node at the given location that is an instance of the
     * specified class object. The search is performed recursively until either
     * a node has been found or a leaf node is reached.
     *
     * @param p parent node
     * @param sceneX x coordinate
     * @param sceneY y coordinate
     * @param nodeClasses node classes to search for
     * @return a node that contains the specified screen coordinates and is an
     * instance of the specified class or null if no such node
     * exist
     */
    /*
    public static Node getDeepestNode(Parent p, double sceneX, double sceneY, Class... nodeClasses) {

        // dammit! javafx uses "wrong" children order.
        List rightOrder = new ArrayList<>();
        rightOrder.addAll(p.getChildrenUnmodifiable());
        Collections.reverse(rightOrder);

        for (Node n : rightOrder) {
            boolean contains = n.contains(n.sceneToLocal(sceneX, sceneY));

            if (contains) {

                Node result = null;

                if (n instanceof Parent) {
                    result = getDeepestNode((Parent) n, sceneX, sceneY, nodeClasses);
                }

                if (result == null) {
                    result = n;
                }

                for (Class nodeClass : nodeClasses) {

                    if (nodeClass.isAssignableFrom(result.getClass())) {

                        return result;

                    }
                }
            }
        }

        return null;
    }
    */

    /**
     * Returns the first node at the given location that is an instance of the
     * specified class object. The search is performed recursively until either
     * a node has been found or a leaf node is reached.
     *
     * @param p parent node
     * @param sceneX x coordinate
     * @param sceneY y coordinate
     * @param nodeClasses node classes to search for
     * @return a node that contains the specified screen coordinates and is an
     * instance of the specified class or null if no such node
     * exist
     */
    public static Node getNode(Parent p, double sceneX, double sceneY, Class... nodeClasses) {

        // dammit! javafx uses "wrong" children order.
        List rightOrder = new ArrayList<>();
        rightOrder.addAll(p.getChildrenUnmodifiable());
        Collections.reverse(rightOrder);

        for (Node n : rightOrder) {
            boolean contains = n.contains(n.sceneToLocal(sceneX, sceneY));

            if (contains) {

                Node result = null;

                if (n instanceof Parent) {
                    result = getNode((Parent) n, sceneX, sceneY, nodeClasses);
                }

                if (result == null) {
                    result = n;
                }

                for (Class nodeClass : nodeClasses) {
                    // may not implement the interface. We check if the Node has a property marking the link
                    // a custom node may have established a link to the ConnectorShape type but the Node itself
                    Class connectorShapeClass = (Class) n.getProperties().get(ConnectorShape.CONNECTOR_SHAPE_CLASS);
                    if (nodeClass.isAssignableFrom(result.getClass())) {
                        return result;
                    } else if (connectorShapeClass != null && connectorShapeClass.isAssignableFrom(result.getClass())) {
                        return result;
                    }
                }
            }
        }

        return null;
    }

    public static List nodesWithParent(Parent p, List nodes) {
        List result = new ArrayList<>();

        for (Node n : nodes) {
            if (p.equals(n.getParent())) {
                result.add(n);
            }
        }

        return result;
    }

    public static List nodesThatImplement(List nodes, Class cls) {
        List result = new ArrayList<>();

        for (Node n : nodes) {
            if (cls.isAssignableFrom(n.getClass())) {
                result.add(n);
            }
        }

        return result;
    }

    public static MouseButton mouseBtnFromEvent(MouseEvent t) {
        MouseButton btn = MouseButton.NONE;

        switch (t.getButton()) {
            case PRIMARY:
                btn = MouseButton.PRIMARY;
                break;
            case MIDDLE:
                btn = MouseButton.MIDDLE;
                break;
            case SECONDARY:
                btn = MouseButton.SECONDARY;
                break;
        }

        return btn;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy