<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/metadata/property/value/EnumerationPropertyMetadata.java<|end_filename|>
/*
 * Copyright (c) 2016, Gluon and/or its affiliates.
 * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.kit.metadata.property.value;

import com.oracle.javafx.scenebuilder.kit.fxom.FXOMProperty;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMInstance;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMPropertyT;
import com.oracle.javafx.scenebuilder.kit.metadata.property.ValuePropertyMetadata;
import com.oracle.javafx.scenebuilder.kit.metadata.util.InspectorPath;
import com.oracle.javafx.scenebuilder.kit.metadata.util.PrefixedValue;
import com.oracle.javafx.scenebuilder.kit.metadata.util.PropertyName;
import java.util.ArrayList;
import java.util.List;

/**
 *
 * 
 */
public class EnumerationPropertyMetadata extends ValuePropertyMetadata {
    
    public static final String EQUIV_NONE = "NONE"; //NOI18N
    public static final String EQUIV_AUTOMATIC = "AUTOMATIC"; //NOI18N
    public static final String EQUIV_INHERITED = "INHERIT"; //NOI18N
    
    private final Class<?> enumClass;
    private final Enum<?> defaultValue;
    private final String nullEquivalent;
    private List<String> validValues;

    public EnumerationPropertyMetadata(PropertyName name, Class<?> enumClass,
            boolean readWrite, Enum<?> defaultValue, InspectorPath inspectorPath) {
        super(name, readWrite, inspectorPath);
        assert enumClass.isEnum();
        assert (readWrite == false) || (defaultValue != null);
        this.enumClass = enumClass;
        this.defaultValue = defaultValue;
        this.nullEquivalent = null;
    }
    
    public EnumerationPropertyMetadata(PropertyName name, Class<?> enumClass,
            String nullEquivalent, boolean readWrite, InspectorPath inspectorPath) {
        super(name, readWrite, inspectorPath);
        assert enumClass.isEnum();
        assert nullEquivalent != null;
        this.enumClass = enumClass;
        this.defaultValue = null;
        this.nullEquivalent = nullEquivalent;
    }
    
    public String getValue(FXOMInstance fxomInstance) {
        final String result;
        
        if (isReadWrite()) {
            final FXOMProperty fxomProperty = fxomInstance.getProperties().get(getName());
            if (fxomProperty == null) {
                // propertyName is not specified in the fxom instance.
                // We return the default value specified in the metadata of the
                // property
                result = getDefaultValue();
            } else {
                assert fxomProperty instanceof FXOMPropertyT;
                final FXOMPropertyT fxomPropertyT = (FXOMPropertyT) fxomProperty;
                final PrefixedValue pv = new PrefixedValue(fxomPropertyT.getValue());
                if (pv.isBindingExpression()) {
                    result = getDefaultValue();
                } else {
                    result = fxomPropertyT.getValue();
                }
            }
        } else {
            final Object o = getName().getValue(fxomInstance.getSceneGraphObject());
            if (o == null) {
                result = getDefaultValue();
            } else {
                assert o.getClass() == enumClass;
                result = o.toString();
            }
        }
        
        return result;
    }

    public void setValue(FXOMInstance fxomInstance, String value) {
        assert isReadWrite();
        assert value != null;
        
        final FXOMProperty fxomProperty = fxomInstance.getProperties().get(getName());
        if (fxomProperty == null) {
            // propertyName is not specified in the fxom instance.
            if (value.equals(getDefaultValue()) == false) {
                // We insert a new fxom property
                final FXOMPropertyT newProperty 
                        = new FXOMPropertyT(fxomInstance.getFxomDocument(),
                        getName(), value);
                newProperty.addToParentInstance(-1, fxomInstance);
            }
        } else {
            assert fxomProperty instanceof FXOMPropertyT;
            final FXOMPropertyT fxomPropertyT = (FXOMPropertyT) fxomProperty;
            if (value.equals(getDefaultValue())) {
                fxomPropertyT.removeFromParentInstance();
            } else {
                fxomPropertyT.setValue(value);
            }
        }
    }
    
    public String getDefaultValue() {
        final String result;
        if (isReadWrite()) {
            assert (defaultValue == null) == (nullEquivalent != null);
            result = (defaultValue == null) ? nullEquivalent : defaultValue.toString();
        } else {
            result = null;
        }
        return result;
    }
    
    public List<String> getValidValues() {
        if (validValues == null) {
            validValues = new ArrayList<>();

            for (Object e : enumClass.getEnumConstants()) {
                validValues.add(e.toString());
            }
            if (nullEquivalent != null) {
                assert defaultValue == null;
                if (validValues.contains(nullEquivalent) == false) {
                    validValues.add(0, nullEquivalent);
                }
            }
        }
        return validValues;
    }

    public int getValidValuesNumber() {
        return getValidValues().size();
    }
    
    /*
     * ValuePropertyMetadata
     */
    
    @Override
    public Class<?> getValueClass() {
        return enumClass;
    }

    @Override
    public Object getDefaultValueObject() {
        return getDefaultValue();
    }

    @Override
    public Object getValueObject(FXOMInstance fxomInstance) {
        return getValue(fxomInstance);
    }

    @Override
    public void setValueObject(FXOMInstance fxomInstance, Object valueObject) {
        assert valueObject instanceof String;
        setValue(fxomInstance, (String) valueObject);
    }
}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/content/WorkspaceController.java<|end_filename|>
/*
 * Copyright (c) 2017, Gluon and/or its affiliates.
 * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.kit.editor.panel.content;

import com.oracle.javafx.scenebuilder.kit.editor.EditorController;
import com.oracle.javafx.scenebuilder.kit.editor.EditorPlatform;
import com.oracle.javafx.scenebuilder.kit.i18n.I18N;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMDocument;

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

import javafx.animation.FadeTransition;
import javafx.application.ConditionalFeature;
import javafx.application.Platform;
import javafx.beans.value.ChangeListener;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.BoundingBox;
import javafx.geometry.Bounds;
import javafx.geometry.Insets;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.SubScene;
import javafx.scene.control.Label;
import javafx.scene.control.ScrollPane;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.layout.CornerRadii;
import javafx.scene.layout.Region;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.paint.Paint;
import javafx.scene.shape.Rectangle;
import javafx.stage.Window;
import javafx.util.Duration;

/**
 *
 */
class WorkspaceController {
    
    private static final double AUTORESIZE_SIZE = 500.0;
    
    private ScrollPane scrollPane;
    private Group scalingGroup;
    private SubScene contentSubScene;
    private Group contentGroup;
    private Label backgroundPane;
    private Rectangle extensionRect;
    private boolean autoResize3DContent = true;
    private double scaling = 1.0;
    private RuntimeException layoutException;
    private EditorController editorController;
    private ArrayList<String> themeStylesheets = new ArrayList<String>();

    private FXOMDocument fxomDocument;

    public WorkspaceController(EditorController editorController) {
        this.editorController = editorController;
    }

    public void panelControllerDidLoadFxml(ScrollPane scrollPane, 
            Group scalingGroup, SubScene contentSubScene, Group contentGroup, Label backgroundPane, 
            Rectangle extensionRect) {
        assert scrollPane != null;
        assert backgroundPane != null;
        assert scalingGroup != null;
        assert contentSubScene != null;
        assert contentGroup != null;
        assert extensionRect != null;
        
        this.scrollPane = scrollPane;
        this.scalingGroup = scalingGroup;
        this.contentSubScene = contentSubScene;
        this.contentGroup = contentGroup;
        this.backgroundPane = backgroundPane;
        this.extensionRect = extensionRect;
        
        // Add scene listener to panelRoot.sceneProperty()
        this.scrollPane.sceneProperty().addListener((ChangeListener<Scene>) (ov, t, t1) -> sceneDidChange());
        
        // Make scalingGroup invisible.
        // We'll turn it visible once content panel is displayed in a Scene
        this.scalingGroup.setVisible(false);
        
        // Remove sample content from contentGroup
        this.contentGroup.getChildren().clear();
        
        updateContentGroup();
        updateScalingGroup();
    }
    
    public void setFxomDocument(FXOMDocument fxomDocument) {
        if (this.fxomDocument != fxomDocument) {
            this.fxomDocument = fxomDocument;
            sceneGraphDidChange();
        }
    }
    
    public void sceneGraphDidChange() {
        if (this.scrollPane != null) {
            updateContentGroup();
            updateScalingGroup();
        }
    }
    
    public boolean isAutoResize3DContent() {
        return autoResize3DContent;
    }

    public void setAutoResize3DContent(boolean autoResize3DContent) {
        
        this.autoResize3DContent = autoResize3DContent;
        if ((scrollPane != null) && (scrollPane.getScene() != null)) {
            adjustWorkspace();
        }
    }

    public double getScaling() {
        return scaling;
    }

    public void setScaling(double scaling) {
        this.scaling = scaling;
        updateScalingGroup();
    }
    
    public List<String> getThemeStyleSheets() {
        return Collections.unmodifiableList(themeStylesheets);
    }
    
    public void setThemeStyleSheet(String themeStyleSheet, EditorPlatform.Theme theme, EditorPlatform.GluonSwatch gluonSwatch, EditorPlatform.GluonTheme gluonTheme) {
        assert themeStyleSheet != null;
        assert theme != null;
        assert gluonSwatch != null;
        assert gluonTheme != null;
        String gluonDocumentStylesheet = EditorPlatform.getGluonDocumentStylesheetURL();
        String gluonSwatchStylesheet = gluonSwatch.getStylesheetURL();
        String gluonThemeStylesheet = gluonTheme.getStylesheetURL();
        String previousGluonSwatchStylesheet = editorController.getGluonSwatch().getStylesheetURL();
        String previousGluonThemeStylesheet = editorController.getGluonTheme().getStylesheetURL();
        if (theme == EditorPlatform.Theme.GLUON_MOBILE_LIGHT || theme == EditorPlatform.Theme.GLUON_MOBILE_DARK) {
            contentSubScene.setUserAgentStylesheet(EditorPlatform.Theme.MODENA.getStylesheetURL());
            ObservableList<String> currentStyleSheets = FXCollections.observableArrayList(contentGroup.getStylesheets());
            currentStyleSheets.remove(previousGluonSwatchStylesheet);
            currentStyleSheets.remove(previousGluonThemeStylesheet);
            if (!currentStyleSheets.contains(themeStyleSheet)) {
                currentStyleSheets.add(themeStyleSheet);
            }
            if (!currentStyleSheets.contains(gluonDocumentStylesheet)) {
                currentStyleSheets.add(gluonDocumentStylesheet);
            }
            if (!currentStyleSheets.contains(gluonSwatchStylesheet)) {
                currentStyleSheets.add(gluonSwatchStylesheet);
            }
            if (!currentStyleSheets.contains(gluonThemeStylesheet)) {
                currentStyleSheets.add(gluonThemeStylesheet);
            }
            themeStylesheets.clear();
            themeStylesheets.addAll(currentStyleSheets);
            contentGroupApplyCss();
//            setPreviewStyleSheets(Arrays.asList(themeStyleSheet));
        } else {
            contentSubScene.setUserAgentStylesheet(themeStyleSheet);

            String gluonMobileStyleSheet = EditorPlatform.Theme.GLUON_MOBILE_LIGHT.getStylesheetURL(); // We can call this with GLUON_MOBILE_LIGHT or GLUON_MOBILE_DARK
            themeStylesheets.remove(gluonMobileStyleSheet);
            themeStylesheets.remove(gluonDocumentStylesheet);
            themeStylesheets.remove(previousGluonSwatchStylesheet);
            themeStylesheets.remove(previousGluonThemeStylesheet);
        }

        // Update scenegraph layout, etc
        FXOMDocument fxomDocument = editorController.getFxomDocument();
        if (fxomDocument != null) {
            fxomDocument.refreshSceneGraph();
        }
    }
    
    public void setPreviewStyleSheets(List<String> previewStyleSheets) {
        EditorPlatform.Theme currentTheme = editorController.getTheme();
        themeStylesheets.clear();
        themeStylesheets.addAll(previewStyleSheets);
        if (currentTheme == EditorPlatform.Theme.GLUON_MOBILE_LIGHT || currentTheme == EditorPlatform.Theme.GLUON_MOBILE_DARK) {
            themeStylesheets.add(EditorPlatform.Theme.GLUON_MOBILE_LIGHT.getStylesheetURL()); // We can call this with GLUON_MOBILE_LIGHT or GLUON_MOBILE_DARK
            themeStylesheets.add(editorController.getGluonSwatch().getStylesheetURL());
            themeStylesheets.add(editorController.getGluonTheme().getStylesheetURL());
            themeStylesheets.add(EditorPlatform.getGluonDocumentStylesheetURL());
        }
        contentGroupApplyCss();
    }
    
    public void layoutContent(boolean applyCSS) {
        if (scrollPane != null) {
            try {
                if (applyCSS) {
                    contentGroupApplyCss();
                }
                scrollPane.layout();
                layoutException = null;
            } catch(RuntimeException x) {
                layoutException = x;
            }
        }
    }

    public RuntimeException getLayoutException() {
        return layoutException;
    }
    
    public void beginInteraction() {
        assert scalingGroup.getParent().isManaged();
        assert scrollPane.getContent() instanceof StackPane;
        
        // Makes the user design and enclosing group unmanaged so
        // that they no longer influence the scroll pane viewport.
        scalingGroup.getParent().setManaged(false);

        // Renders the top stack pane fully rigid
        final StackPane contentPane = (StackPane) scrollPane.getContent();
        assert contentPane.getMinWidth() == Region.USE_PREF_SIZE;
        assert contentPane.getMinHeight() == Region.USE_PREF_SIZE;
        assert contentPane.getPrefWidth() == Region.USE_COMPUTED_SIZE;
        assert contentPane.getPrefHeight() == Region.USE_COMPUTED_SIZE;
        assert contentPane.getMaxWidth() == Double.MAX_VALUE;
        assert contentPane.getMaxHeight() == Double.MAX_VALUE;
        contentPane.setPrefWidth(contentPane.getWidth());
        contentPane.setPrefHeight(contentPane.getHeight());
        contentPane.setMaxWidth(Region.USE_PREF_SIZE);
        contentPane.setMaxHeight(Region.USE_PREF_SIZE);
    }
    
    public void endInteraction() {
        assert scalingGroup.getParent().isManaged() == false;
        
        // Reverts the top stack pane : it now adjusts to the size of its children
        final StackPane contentPane = (StackPane) scrollPane.getContent();
        assert contentPane.getMinWidth() == Region.USE_PREF_SIZE;
        assert contentPane.getMinHeight() == Region.USE_PREF_SIZE;
        assert contentPane.getPrefWidth() != Region.USE_COMPUTED_SIZE;
        assert contentPane.getPrefHeight() != Region.USE_COMPUTED_SIZE;
        assert contentPane.getMaxWidth() == Region.USE_PREF_SIZE;
        assert contentPane.getMaxHeight() == Region.USE_PREF_SIZE;
        contentPane.setPrefWidth(Region.USE_COMPUTED_SIZE);
        contentPane.setPrefHeight(Region.USE_COMPUTED_SIZE);
        contentPane.setMaxWidth(Double.MAX_VALUE);
        contentPane.setMaxHeight(Double.MAX_VALUE);
        
        // Reverts scalingGroup setup
        scalingGroup.getParent().setManaged(true);
    }
    
    /*
     * Private
     */
    
    private void sceneDidChange() {
        assert this.scrollPane != null;
        
        if (scrollPane.getScene() != null) {
            assert scalingGroup.isVisible() == false;
            
            // Here we'd like to layout the user scene graph immediately
            // i.e. invoke:
            //      1) layoutContent()      // to relayout user scene graph
            //      2) adjustWorkspace()    // to size the content workspace
            //
            // However invoking layoutContent() from here (scene change listener)
            // does not work very well (see RT-32326).
            //
            // So we do these two steps in runLater().
            // Until they are done, scalingGroup is kept invisible to avoid
            // visual artifacts. After the two steps are done, we turn the 
            // visible by calling revealScalingGroup().
            
            Platform.runLater(() -> {
                layoutContent(true /* applyCSS */);
                adjustWorkspace();
                revealScalingGroup();
            });
        } else {
            assert scalingGroup.isVisible();
            scalingGroup.setVisible(false);
        }
    }
    
    
    private void updateContentGroup() {
        
        
        /*
         * fxomRoot 
         */
        
        final String statusMessageText, statusStyleClass;
        contentGroup.getChildren().clear();

        boolean canDisplayDocument = false;
        if (fxomDocument == null) {
            statusMessageText = "FXOMDocument is null"; //NOI18N
            statusStyleClass = "stage-prompt"; //NOI18N
        } else if (fxomDocument.getFxomRoot() == null) {
            statusMessageText = I18N.getString("content.label.status.invitation");
            statusStyleClass = "stage-prompt"; //NOI18N
        } else {
            final Object userSceneGraph = fxomDocument.getDisplayNodeOrSceneGraphRoot();
            if (userSceneGraph instanceof Node) {
                final Node rootNode = (Node) userSceneGraph;
                assert rootNode.getParent() == null;
                contentGroup.getChildren().add(rootNode);
                layoutContent(true /* applyCSS */);
                if (layoutException == null) {
                    statusMessageText = ""; //NOI18N
                    statusStyleClass = "stage-prompt-default"; //NOI18N
                    canDisplayDocument = true;
                } else {
                    contentGroup.getChildren().clear();
                    statusMessageText = I18N.getString("content.label.status.cannot.display");
                    statusStyleClass = "stage-prompt"; //NOI18N
                }
            } else {
                statusMessageText = I18N.getString("content.label.status.cannot.display");
                statusStyleClass = "stage-prompt"; //NOI18N
            }
        }
        
        backgroundPane.setText(statusMessageText);
        backgroundPane.getStyleClass().clear();
        backgroundPane.getStyleClass().add(statusStyleClass);

        // Display background fill of the Window/Scene
        if (canDisplayDocument) {
            assert fxomDocument != null;
            assert fxomDocument.getFxomRoot() != null;

            Paint backgroundPaneFillPaint = Color.WHITE;

            if (fxomDocument.getFxomRoot().getSceneGraphObject() instanceof Window) {
                Window window = (Window) fxomDocument.getFxomRoot().getSceneGraphObject();
                Scene scene = window.getScene();
                if (scene != null && scene.getFill() != null) {
                    backgroundPaneFillPaint = scene.getFill();
                }
            } else if (fxomDocument.getFxomRoot().getSceneGraphObject() instanceof Scene) {
                Scene scene = (Scene) fxomDocument.getFxomRoot().getSceneGraphObject();
                if (scene.getFill() != null) {
                    backgroundPaneFillPaint = scene.getFill();
                }
            }

            BackgroundFill backgroundPaneFill = new BackgroundFill(backgroundPaneFillPaint,
                    CornerRadii.EMPTY, Insets.EMPTY);
            backgroundPane.setBackground(new Background(backgroundPaneFill));
        }

        // If layoutException != null, then this layout call is required
        // so that backgroundPane updates its message... Strange...
        backgroundPane.layout();
        
        adjustWorkspace();
    }
    
    private void updateScalingGroup() {
        if (scalingGroup != null) {
            final double actualScaling;
            if (fxomDocument == null) {
                actualScaling = 1.0;
            } else if (fxomDocument.getSceneGraphRoot() == null) {
                actualScaling = 1.0;
            } else {
                actualScaling = scaling;
            }
            scalingGroup.setScaleX(actualScaling);
            scalingGroup.setScaleY(actualScaling);
            
            if (Platform.isSupported(ConditionalFeature.SCENE3D)) {
                scalingGroup.setScaleZ(actualScaling);
            }
            // else {
            //      leave scaleZ unchanged else it breaks zooming when running
            //      with the software pipeline (see DTL-6661).
            // }
        }
    }
    
    private void adjustWorkspace() {
        final Bounds backgroundBounds, extensionBounds;
        
        final Object userSceneGraph;
        if (fxomDocument == null) {
            userSceneGraph = null;
        } else {
            userSceneGraph = fxomDocument.getDisplayNodeOrSceneGraphRoot();
        }
        if ((userSceneGraph instanceof Node) && (layoutException == null)) {
            final Node rootNode = (Node) userSceneGraph;
            
            final Bounds rootBounds = rootNode.getLayoutBounds();
            
            if (rootBounds.isEmpty() 
                    || (rootBounds.getWidth() == 0.0)
                    || (rootBounds.getHeight() == 0.0)) {
                backgroundBounds = new BoundingBox(0.0, 0.0, 0.0, 0.0);
                extensionBounds = new BoundingBox(0.0, 0.0, 0.0, 0.0);
            } else {
                final double scale;
                if ((rootBounds.getDepth() > 0) && autoResize3DContent) {
                    // Content is 3D
                    final double scaleX = AUTORESIZE_SIZE / rootBounds.getWidth();
                    final double scaleY = AUTORESIZE_SIZE / rootBounds.getHeight();
                    final double scaleZ = AUTORESIZE_SIZE / rootBounds.getDepth();
                    scale = Math.min(scaleX, Math.min(scaleY, scaleZ));
                } else {
                    scale = 1.0;
                }
                contentGroup.setScaleX(scale);
                contentGroup.setScaleY(scale);
                contentGroup.setScaleZ(scale);

                final Bounds contentBounds = rootNode.localToParent(rootBounds);
                backgroundBounds = new BoundingBox(0.0, 0.0,
                        contentBounds.getMinX() + contentBounds.getWidth(),
                        contentBounds.getMinY() + contentBounds.getHeight());


                final Bounds unclippedRootBounds = computeUnclippedBounds(rootNode);
                assert unclippedRootBounds.getHeight() != 0.0;
                assert unclippedRootBounds.getWidth() != 0.0;
                assert rootNode.getParent() == contentGroup;
                
                final Bounds unclippedContentBounds = rootNode.localToParent(unclippedRootBounds);
                extensionBounds = computeExtensionBounds(backgroundBounds, unclippedContentBounds);
            }
        } else {
            backgroundBounds = new BoundingBox(0.0, 0.0, 320.0, 150.0);
            extensionBounds = new BoundingBox(0.0, 0.0, 0.0, 0.0);
        }
        
        backgroundPane.setPrefWidth(backgroundBounds.getWidth());
        backgroundPane.setPrefHeight(backgroundBounds.getHeight());
        extensionRect.setX(extensionBounds.getMinX());
        extensionRect.setY(extensionBounds.getMinY());
        extensionRect.setWidth(extensionBounds.getWidth());
        extensionRect.setHeight(extensionBounds.getHeight());
        
        contentSubScene.setWidth(contentGroup.getLayoutBounds().getWidth());
        contentSubScene.setHeight(contentGroup.getLayoutBounds().getHeight());
    }
    
    private static Bounds computeUnclippedBounds(Node node) {
        final Bounds layoutBounds;
        double minX, minY, maxX, maxY, minZ, maxZ;
        
        assert node != null;
        assert node.getLayoutBounds().isEmpty() == false;
        
        layoutBounds = node.getLayoutBounds();
        minX = layoutBounds.getMinX();
        minY = layoutBounds.getMinY();
        maxX = layoutBounds.getMaxX();
        maxY = layoutBounds.getMaxY();
        minZ = layoutBounds.getMinZ();
        maxZ = layoutBounds.getMaxZ();
        
        if (node instanceof Parent) {
            final Parent parent = (Parent) node;
            
            for (Node child : parent.getChildrenUnmodifiable()) {
                final Bounds childBounds = child.getBoundsInParent();
                minX = Math.min(minX, childBounds.getMinX());
                minY = Math.min(minY, childBounds.getMinY());
                maxX = Math.max(maxX, childBounds.getMaxX());
                maxY = Math.max(maxY, childBounds.getMaxY());
                minZ = Math.min(minZ, childBounds.getMinZ());
                maxZ = Math.max(maxZ, childBounds.getMaxZ());
            }
        }
        
        assert minX <= maxX;
        assert minY <= maxY;
        assert minZ <= maxZ;
        
        return new BoundingBox(minX, minY, minZ, maxX-minX, maxY-minY, maxZ-minZ);
    }
    
    
    private static Bounds computeExtensionBounds(Bounds backgroundBounds,
            Bounds unclippedContentBounds) {
        final Bounds totalBounds = unionOfBounds(backgroundBounds, unclippedContentBounds);
        final double backgroundCenterX, backgroundCenterY;
        backgroundCenterX = (backgroundBounds.getMinX() + backgroundBounds.getMaxX()) / 2.0;
        backgroundCenterY = (backgroundBounds.getMinY() + backgroundBounds.getMaxY()) / 2.0;
        assert totalBounds.contains(backgroundCenterX, backgroundCenterY);
        double extensionHalfWidth, extensionHalfHeight;
        extensionHalfWidth = Math.max(
                backgroundCenterX - totalBounds.getMinX(),
                totalBounds.getMaxX() - backgroundCenterX);
        extensionHalfHeight = Math.max(
                backgroundCenterY - totalBounds.getMinY(),
                totalBounds.getMaxY() - backgroundCenterY);
        
        // We a few pixels in order the parent ring of root object
        // to fit inside the extension rect.
        extensionHalfWidth += 20.0;
        extensionHalfHeight += 20.0;
        
        return new BoundingBox(
                backgroundCenterX - extensionHalfWidth,
                backgroundCenterY - extensionHalfHeight,
                extensionHalfWidth * 2,
                extensionHalfHeight * 2);
    }
    
    
    private static Bounds unionOfBounds(Bounds b1, Bounds b2) {
        final Bounds result;
        
        if (b1.isEmpty()) {
            result = b2;
        } else if (b2.isEmpty()) {
            result = b1;
        } else {
            final double minX = Math.min(b1.getMinX(), b2.getMinX());
            final double minY = Math.min(b1.getMinY(), b2.getMinY());
            final double minZ = Math.min(b1.getMinZ(), b2.getMinZ());
            final double maxX = Math.max(b1.getMaxX(), b2.getMaxX());
            final double maxY = Math.max(b1.getMaxY(), b2.getMaxY());
            final double maxZ = Math.max(b1.getMaxZ(), b2.getMaxZ());
            
            assert minX <= maxX;
            assert minY <= maxY;
            assert minZ <= maxZ;
            
            result = new BoundingBox(minX, minY, minZ, maxX-minX, maxY-minY, maxZ-minZ);
        }
        
        return result;
    }
    
    
    private void revealScalingGroup() {
        assert scalingGroup.isVisible() == false;
        
        scalingGroup.setVisible(true);
        scalingGroup.setOpacity(0.0);

        FadeTransition showHost = new FadeTransition(Duration.millis(300), scalingGroup);
        showHost.setFromValue(0.0);
        showHost.setToValue(1.0);
        showHost.play();
    }

    private void contentGroupApplyCss() {
        contentGroup.getStylesheets().setAll(themeStylesheets);
        if (fxomDocument != null) {
            contentGroup.getStylesheets().addAll(fxomDocument.getDisplayStylesheets());
        }
        contentGroup.applyCss();
    }
}


<|start_filename|>app/src/main/java/com/oracle/javafx/scenebuilder/app/message/MessageBarController.java<|end_filename|>
/*
 * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.app.message;

import com.oracle.javafx.scenebuilder.app.i18n.I18N;
import com.oracle.javafx.scenebuilder.kit.editor.EditorController;
import com.oracle.javafx.scenebuilder.kit.editor.messagelog.MessageLogEntry;
import com.oracle.javafx.scenebuilder.kit.editor.panel.util.AbstractFxmlPanelController;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMDocument;

import java.net.URL;

import javafx.animation.FadeTransition;
import javafx.beans.value.ChangeListener;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.Tooltip;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.StackPane;
import javafx.util.Duration;

/**
 *
 */
public class MessageBarController extends AbstractFxmlPanelController {

    private MessagePopupController messageWindowController;
    private int previousTotalNumOfMessages = 0;

    @FXML
    private HBox messageBox;
    @FXML
    private Button messageButton;
    @FXML
    private Label messageLabel;
    @FXML
    private Label statusLabel;
    @FXML
    private StackPane selectionBarHost;
    @FXML
    private HBox messagePart;
    @FXML
    private HBox iconsHbox;

    private final ImageView fileDirtyImage;
    private Tooltip statusLabelTooltip = null;

    public MessageBarController(EditorController editorController) {
        super(MessageBarController.class.getResource("MessageBar.fxml"), I18N.getBundle(), editorController); //NOI18N

        // Initialize file dirty image
        final URL fileDirtyURL = MessageBarController.class.getResource("file-dirty.png"); //NOI18N
        assert fileDirtyURL != null;
        fileDirtyImage = new ImageView(new Image(fileDirtyURL.toExternalForm()));
    }

    public StackPane getSelectionBarHost() {
        return selectionBarHost;
    }

    /*
     * Action Handlers
     */
    @FXML
    void onOpenCloseAction(ActionEvent e) {
        if (messageWindowController == null) {
            messageWindowController = new MessagePopupController(getEditorController());
        }
        if (messageWindowController.isWindowOpened()) {
            messageWindowController.closeWindow();
        } else {
            messageWindowController.openWindow(messageBox);
        }
    }

    /*
     * AbstractPanelController
     */
    @Override
    protected void fxomDocumentDidChange(FXOMDocument oldDocument) {
        // Nothing to do
    }

    @Override
    protected void sceneGraphRevisionDidChange() {
        // Nothing to do
    }

    @Override
    protected void cssRevisionDidChange() {
        // Nothing to do
    }

    @Override
    protected void jobManagerRevisionDidChange() {
        // Nothing to do
    }

    @Override
    protected void editorSelectionDidChange() {
        // Nothing to do
    }

    /*
     * AbstractFxmlPanelController
     */
    @Override
    protected void controllerDidLoadFxml() {

        // Sanity checks
        assert messageBox != null;
        assert messageLabel != null;
        assert statusLabel != null;
        assert selectionBarHost != null;

        // Remove fake data
        messageLabel.setText(""); //NOI18N
        statusLabel.setText(""); //NOI18N
        messageButton.setVisible(false);
        
        // Listens to the message log 
        getEditorController().getMessageLog().revisionProperty().addListener(
                (ChangeListener<Number>) (ov, t, t1) -> messageLogDidChange());
        getEditorController().getMessageLog().numOfWarningMessagesProperty().addListener(
                (ChangeListener<Number>) (ov, t, t1) -> {
                    String numberOfMessages = Integer.toString(t1.intValue());
                    if (t1.intValue() > 9) {
                        numberOfMessages = "*"; //NOI18N
                    }
                    messageButton.setText(numberOfMessages);
                });
        
        statusLabelTooltip = statusLabel.getTooltip();
        
        // Update output components
        messageLogDidChange();
    }

    /*
     * Private
     */
    public void setDocumentDirty(boolean isDirty) {
        if (statusLabel != null) {
            if (isDirty) {
                statusLabel.setGraphic(fileDirtyImage);
                statusLabel.setTooltip(statusLabelTooltip);
            } else {
                statusLabel.setGraphic(null);
                statusLabel.setTooltip(null);
            }
        }
    }
    
    private void messageLogDidChange() {
        assert messageLabel != null;
        
        final MessageLogEntry entry
                = getEditorController().getMessageLog().getYoungestEntry();
        int logSize = getEditorController().getMessageLog().getEntries().size();

        // When an old message is dismissed the message log changes but there's
        // no need to display anything in the message bar.
        if (entry != null && logSize > previousTotalNumOfMessages) {
            // We mask the host
            HBox.setHgrow(messagePart, Priority.ALWAYS);
            getSelectionBarHost().setVisible(false);
            getSelectionBarHost().setManaged(false);
            messageLabel.setManaged(true);
            
            // Styling message area according severity
            setStyle(entry.getType());
                        
            // If no message panel is defined nor in use we compute message bar
            // button. But as soon as a message panel is opened it means one or
            // more message is being displayed so we do not alter the message button.
            if (messageWindowController == null || ! messageWindowController.isWindowOpened()) {
                if (getEditorController().getMessageLog().getWarningEntryCount() == 0) {
                    messageButton.setVisible(false);
                    messageButton.setManaged(false);
                } else {
                    messageButton.setVisible(true);
                    messageButton.setManaged(true);
                }
            }
            
            // Displaying the message
            messageLabel.setText(entry.getText());
            messageLabel.setVisible(true);
            
            // We go back to the host after a given time            
            FadeTransition showHost = new FadeTransition(Duration.seconds(1), messagePart);
            showHost.setFromValue(1.0);
            showHost.setToValue(0.0);
            showHost.setDelay(Duration.seconds(3)); // DTL-5073
            showHost.setOnFinished(t -> {
                messageLabel.setVisible(false);
                messageLabel.setGraphic(null);
                messageLabel.setManaged(false);
                if (getEditorController().getMessageLog().getWarningEntryCount() == 0) {
                    messageButton.setVisible(false);
                    messageButton.setManaged(false);
                }
                resetStyle();
                getSelectionBarHost().setManaged(true);
                getSelectionBarHost().setVisible(true);
                messagePart.setOpacity(1.0);
                HBox.setHgrow(messagePart, Priority.NEVER);
            });
            showHost.play();
        } else if (getEditorController().getMessageLog().getEntryCount() == 0) {
            messageButton.setVisible(false);
            messageButton.setManaged(false);
            
            if (messageWindowController != null && messageWindowController.isWindowOpened()) {
                messageWindowController.closeWindow();
            }
        }

        previousTotalNumOfMessages = logSize;
    }
    
    private void resetStyle() {
        // We clear all previous use, the sole way to control what's going on.
        messageLabel.getStyleClass().removeAll("message-info"); //NOI18N
        messageLabel.getStyleClass().removeAll("message-warning"); //NOI18N
        statusLabel.getStyleClass().removeAll("message-info"); //NOI18N
        statusLabel.getStyleClass().removeAll("message-warning"); //NOI18N
        iconsHbox.getStyleClass().removeAll("message-info"); //NOI18N
        iconsHbox.getStyleClass().removeAll("message-warning"); //NOI18N
    }
    
    private void setStyle(MessageLogEntry.Type type) {
        resetStyle();
        
        switch (type) {
            case INFO:
                messageLabel.getStyleClass().add("message-info"); //NOI18N
                statusLabel.getStyleClass().add("message-info"); //NOI18N
                iconsHbox.getStyleClass().add("message-info"); //NOI18N
                break;
            case WARNING:
                messageLabel.getStyleClass().add("message-warning"); //NOI18N
                statusLabel.getStyleClass().add("message-warning"); //NOI18N
                iconsHbox.getStyleClass().add("message-warning"); //NOI18N
                break;
            default:
                break;
        }
    }
}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/drag/target/AccessoryDropTarget.java<|end_filename|>
/*
 * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.kit.editor.drag.target;

import com.oracle.javafx.scenebuilder.kit.editor.EditorController;
import com.oracle.javafx.scenebuilder.kit.editor.drag.source.AbstractDragSource;
import com.oracle.javafx.scenebuilder.kit.editor.job.Job;
import com.oracle.javafx.scenebuilder.kit.editor.job.BatchJob;
import com.oracle.javafx.scenebuilder.kit.editor.job.InsertAsAccessoryJob;
import com.oracle.javafx.scenebuilder.kit.editor.job.atomic.ModifyObjectJob;
import com.oracle.javafx.scenebuilder.kit.editor.job.atomic.RemoveObjectJob;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMInstance;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMObject;
import com.oracle.javafx.scenebuilder.kit.metadata.property.value.EnumerationPropertyMetadata;
import com.oracle.javafx.scenebuilder.kit.metadata.util.DesignHierarchyMask;
import com.oracle.javafx.scenebuilder.kit.metadata.util.DesignHierarchyMask.Accessory;
import com.oracle.javafx.scenebuilder.kit.metadata.util.InspectorPath;
import com.oracle.javafx.scenebuilder.kit.metadata.util.PropertyName;
import java.util.Objects;
import javafx.geometry.Pos;
import javafx.scene.layout.BorderPane;

/**
 *
 */
public class AccessoryDropTarget extends AbstractDropTarget {

    private final FXOMInstance targetContainer;
    private final Accessory accessory;

    public AccessoryDropTarget(FXOMInstance targetContainer, Accessory accessory) {
        assert targetContainer != null;
        this.targetContainer = targetContainer;
        this.accessory = accessory;
    }

    public Accessory getAccessory() {
        return accessory;
    }


    /*
     * AbstractDropTarget
     */
    @Override
    public FXOMObject getTargetObject() {
        return targetContainer;
    }

    @Override
    public boolean acceptDragSource(AbstractDragSource dragSource) {
        assert dragSource != null;
        
        final boolean result;
        if (dragSource.getDraggedObjects().size() != 1) {
            result = false;
        } else {
            final DesignHierarchyMask m = new DesignHierarchyMask(targetContainer);
            final FXOMObject draggedObject = dragSource.getDraggedObjects().get(0);
            result = m.isAcceptingAccessory(accessory, draggedObject)
                    && m.getAccessory(accessory) == null;
        }
        
        return result;
    }

    @Override
    public Job makeDropJob(AbstractDragSource dragSource, EditorController editorController) {
        assert acceptDragSource(dragSource);
        assert editorController != null;
        
        final boolean shouldRefreshSceneGraph = true;
        final BatchJob result = new BatchJob(editorController,
                shouldRefreshSceneGraph, dragSource.makeDropJobDescription());
        
        final FXOMObject draggedObject = dragSource.getDraggedObjects().get(0);
        final FXOMObject currentParent = draggedObject.getParentObject();
        
        // Two steps :
        //  - remove drag source object from its current parent (if any)
        //  - set the drag source object as accessory of the drop target

        if (currentParent != null) {
            result.addSubJob(new RemoveObjectJob(draggedObject, editorController));
        }
        final Job j = new InsertAsAccessoryJob(draggedObject, 
                targetContainer, accessory, editorController);
        result.addSubJob(j);
        
        if ((targetContainer.getSceneGraphObject() instanceof BorderPane) 
                && (draggedObject instanceof FXOMInstance)) {
            
            // We add a job which sets BorderPane.alignment=CENTER on draggedObject
            final FXOMInstance draggedInstance
                    = (FXOMInstance) draggedObject;
            final PropertyName alignmentName
                    = new PropertyName("alignment", BorderPane.class); //NOI18N
            final EnumerationPropertyMetadata alignmentMeta
                    = new EnumerationPropertyMetadata(alignmentName, Pos.class,
                    "UNUSED", true /* readWrite */, InspectorPath.UNUSED); //NOI18N
            final Job alignmentJob
                    = new ModifyObjectJob(draggedInstance, alignmentMeta, 
                            Pos.CENTER.toString(), editorController);
            result.addSubJob(alignmentJob);
        }
        
        assert result.isExecutable();
        
        return result;
    }

    @Override
    public boolean isSelectRequiredAfterDrop() {
        return true;
    }
    
    /*
     * Objects
     */
    @Override
    public int hashCode() {
        int hash = 3;
        hash = 97 * hash + Objects.hashCode(this.targetContainer);
        hash = 97 * hash + (this.accessory != null ? this.accessory.hashCode() : 0);
        return hash;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final AccessoryDropTarget other = (AccessoryDropTarget) obj;
        if (!Objects.equals(this.targetContainer, other.targetContainer)) {
            return false;
        }
        if (this.accessory != other.accessory) {
            return false;
        }
        return true;
    }

    @Override
    public String toString() {
        return "AccessoryDropTarget{" + "targetContainer=" + targetContainer + ", accessory=" + accessory + '}'; //NOI18N
    }
    
    
}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/drag/DragController.java<|end_filename|>
/*
 * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.kit.editor.drag;

import java.util.List;
import java.util.Timer;
import java.util.TimerTask;

import javafx.application.Platform;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.Property;
import javafx.beans.property.SimpleObjectProperty;
import javafx.scene.input.TransferMode;

import com.oracle.javafx.scenebuilder.kit.editor.EditorController;
import com.oracle.javafx.scenebuilder.kit.editor.drag.source.AbstractDragSource;
import com.oracle.javafx.scenebuilder.kit.editor.drag.source.DocumentDragSource;
import com.oracle.javafx.scenebuilder.kit.editor.drag.target.AbstractDropTarget;
import com.oracle.javafx.scenebuilder.kit.editor.drag.target.RootDropTarget;
import com.oracle.javafx.scenebuilder.kit.editor.job.BatchJob;
import com.oracle.javafx.scenebuilder.kit.editor.job.Job;
import com.oracle.javafx.scenebuilder.kit.editor.job.atomic.BackupSelectionJob;
import com.oracle.javafx.scenebuilder.kit.editor.job.atomic.UpdateSelectionJob;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMObject;
import com.oracle.javafx.scenebuilder.kit.metadata.util.DesignHierarchyPath;

/**
 *
 */
public class DragController {
    
    private final EditorController editorController;
    private final ObjectProperty<AbstractDragSource> dragSourceProperty
            = new SimpleObjectProperty<>(null);
    private final ObjectProperty<AbstractDropTarget> dropTargetProperty
            = new SimpleObjectProperty<>(null);
    private LiveUpdater liveUpdater;
    private Job backupSelectionJob;
    private boolean liveUpdateEnabled;
    private boolean dropAccepted;
    private AbstractDropTarget committedDropTarget;
    private Timer mouseTimer;
    
    public DragController(EditorController editorController) {
        this.editorController = editorController;
    }
    
    public void begin(AbstractDragSource dragSource) {
        assert dragSource != null;
        assert dragSource.isAcceptable();
        assert getDragSource() == null;
        assert getDropTarget() == null;
        assert liveUpdater == null;
        assert backupSelectionJob == null;
        assert dropAccepted == false;
        assert committedDropTarget == null;
        assert mouseTimer == null;
        
        liveUpdater = new LiveUpdater(dragSource, editorController);
        dragSourceProperty.set(dragSource);
        dropTargetProperty.set(null);
        
        // Backup and clear the selection
        backupSelectionJob = new BackupSelectionJob(editorController);
        editorController.getSelection().clear();
    }
    
    public void end() {
        assert getDragSource() != null;
        
        liveUpdater.setDropTarget(null);
        
        /*
         * Note 1: we reset the drop target before performing the drop operation.
         * This makes content panel hide the drop target ring before fxom update
         * and scene graph refresh.
         * Note 2: dropAccepted is reset before dropTargetProperty
         * so that listeners can invoke isDropAccepted().
         */
        dropAccepted = false;
        dropTargetProperty.set(null);

        if (committedDropTarget != null) {
            assert committedDropTarget.acceptDragSource(getDragSource());
            final Job dropJob 
                    = committedDropTarget.makeDropJob(getDragSource(), editorController);
            final Job selectJob 
                    = new UpdateSelectionJob(getDragSource().getDraggedObjects(), editorController);
            final BatchJob batchJob 
                    = new BatchJob(editorController, dropJob.getDescription());
            if (committedDropTarget.isSelectRequiredAfterDrop()) {
                batchJob.addSubJob(backupSelectionJob);
            }
            batchJob.addSubJob(dropJob);
            if (committedDropTarget.isSelectRequiredAfterDrop()) {
                batchJob.addSubJob(selectJob);
            }
            editorController.getJobManager().push(batchJob);
        }
        
        if (mouseTimer != null) {
            mouseTimer.cancel();
            mouseTimer = null;
        }
        liveUpdater = null;
        backupSelectionJob = null;
        committedDropTarget = null;
        dragSourceProperty.set(null);
        
    }

    public AbstractDragSource getDragSource() {
        return dragSourceProperty.get();
    }
    
    public Property<AbstractDragSource> dragSourceProperty() {
        return dragSourceProperty;
    }
    
    public void setDropTarget(AbstractDropTarget newDropTarget) {
        assert getDragSource() != null;
        assert (newDropTarget == null) || (this.committedDropTarget == null);
        
        /*
         * Update drop target property.
         * Note that this.dropAccepted is updated before so that
         * drop target listeners can invoke isDropAccepted().
         */
        if (newDropTarget == null) {
            dropAccepted = false;
        } else if (isDragSourceInParentChain(newDropTarget)) {
            dropAccepted = false;
        } else {
            dropAccepted = newDropTarget.acceptDragSource(getDragSource());
        }
        dropTargetProperty.set(newDropTarget);

        trackMouse();
        
        if (dropAccepted) {
            assert getDropTarget() != null;
            assert getDropTarget().acceptDragSource(getDragSource());
            assert getDragSource().getDraggedObjects().isEmpty() == false;
            
            final FXOMObject firstObject = getDragSource().getDraggedObjects().get(0);
            final FXOMObject currentParent = firstObject.getParentObject();
            final FXOMObject nextParent = getDropTarget().getTargetObject();
            
            if ((currentParent == nextParent) && liveUpdateEnabled) {
                liveUpdater.setDropTarget(newDropTarget);
            }
        }
    }
    
    public AbstractDropTarget getDropTarget() {
        return dropTargetProperty.get();
    }
    
    public Property<AbstractDropTarget> dropTargetProperty() {
        return dropTargetProperty;
    }
    
    public boolean isDropAccepted() {
        return dropAccepted;
    }
    
    public TransferMode[] getAcceptedTransferModes() {
        final TransferMode[] result;
        
        if (getDropTarget() == null) {
            result = TransferMode.NONE;
        } else if (dropAccepted) {
            if (getDragSource() instanceof DocumentDragSource) {
                result = new TransferMode[] { TransferMode.MOVE };
            } else  {
                result = new TransferMode[] { TransferMode.COPY };
            }
        } else {
            result = TransferMode.NONE;
        }
        
        assert (result.length == 0) || (getDropTarget() != null);
        
        return result;
    }
    
    public void commit() {
        assert isDropAccepted();
        assert committedDropTarget == null;
        
        committedDropTarget = getDropTarget();
    }
    
    public boolean isLiveUpdated() {
        return getDropTarget() == liveUpdater.getDropTarget();
    }
    
    /*
     * Private
     */
    
    private void mouseDidStopMoving() {
        if (dropAccepted 
                && (getDropTarget() != liveUpdater.getDropTarget()) 
                && liveUpdateEnabled) {
            liveUpdater.setDropTarget(getDropTarget());
        }
    }
    
    private static final long MOUSE_TIMER_DELAY = 500; // ms
    private void trackMouse() {
        final boolean runAsDaemon = true;
        
        if (mouseTimer == null) {
            mouseTimer = new Timer(runAsDaemon);
        } else {
            mouseTimer.cancel();
            mouseTimer = new Timer(runAsDaemon);
        }
        
        mouseTimer.schedule(new TimerTask() {
            @Override public void run() {
                Platform.runLater(() -> {
                    mouseTimer = null;
                    mouseDidStopMoving();
                });
            }
        }, MOUSE_TIMER_DELAY);
    }
    
    /**
     * Returns true if one of the dragged object is in the parent chain of the
     * specified drop target, false otherwise.
     *
     * @param newDropTarget the drop target
     * @return true if one of the dragged object is in the parent chain of the
     * specified drop target, false otherwise
     */
    private boolean isDragSourceInParentChain(AbstractDropTarget newDropTarget) {
        assert newDropTarget != null;
        boolean result;
        
        if (newDropTarget instanceof RootDropTarget) {
            // dragSource is dragged over an empty document
            result = false;
        } else {
            final List<FXOMObject> draggedObjects
                    = getDragSource().getDraggedObjects();
            final DesignHierarchyPath dropTargetPath
                    = new DesignHierarchyPath(newDropTarget.getTargetObject());
            
            result = false;
            for (FXOMObject draggedObject : draggedObjects) {
                final DesignHierarchyPath draggedObjectPath
                        = new DesignHierarchyPath(draggedObject);
                final DesignHierarchyPath commonPath
                        = draggedObjectPath.getCommonPathWith(dropTargetPath);
                // If one of the dragged objects is in the parent chain 
                // of the drop target, we abort the DND gesture
                if (commonPath.equals(draggedObjectPath)) {
                    result = true;
                    break;
                }
            }
        }
        
        return result;
    }
}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/content/driver/SceneDriver.java<|end_filename|>
package com.oracle.javafx.scenebuilder.kit.editor.panel.content.driver;

import com.oracle.javafx.scenebuilder.kit.editor.drag.target.AbstractDropTarget;
import com.oracle.javafx.scenebuilder.kit.editor.drag.target.AccessoryDropTarget;
import com.oracle.javafx.scenebuilder.kit.editor.panel.content.ContentPanelController;
import com.oracle.javafx.scenebuilder.kit.editor.panel.content.driver.curve.AbstractCurveEditor;
import com.oracle.javafx.scenebuilder.kit.editor.panel.content.driver.handles.AbstractHandles;
import com.oracle.javafx.scenebuilder.kit.editor.panel.content.driver.handles.SceneHandles;
import com.oracle.javafx.scenebuilder.kit.editor.panel.content.driver.pring.AbstractPring;
import com.oracle.javafx.scenebuilder.kit.editor.panel.content.driver.pring.NodePring;
import com.oracle.javafx.scenebuilder.kit.editor.panel.content.driver.resizer.AbstractResizer;
import com.oracle.javafx.scenebuilder.kit.editor.panel.content.driver.tring.AbstractTring;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMInstance;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMObject;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMProperty;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMPropertyC;
import com.oracle.javafx.scenebuilder.kit.metadata.util.DesignHierarchyMask;
import com.oracle.javafx.scenebuilder.kit.metadata.util.PropertyName;

import javafx.geometry.Bounds;
import javafx.scene.Node;
import javafx.scene.Scene;

public class SceneDriver extends AbstractDriver {
    public SceneDriver(ContentPanelController contentPanelController) {
        super(contentPanelController);
    }

    @Override
    public AbstractHandles<?> makeHandles(FXOMObject fxomObject) {
        assert fxomObject.getSceneGraphObject() instanceof Scene;
        assert fxomObject instanceof FXOMInstance;
        return new SceneHandles(contentPanelController, (FXOMInstance) fxomObject);
    }

    @Override
    public AbstractTring<?> makeTring(AbstractDropTarget dropTarget) {
        return null;
    }

    @Override
    public AbstractPring<?> makePring(FXOMObject fxomObject) {
        assert fxomObject.getSceneGraphObject() instanceof Scene;
        DesignHierarchyMask designHierarchyMask = new DesignHierarchyMask(fxomObject);
        FXOMObject root = designHierarchyMask.getAccessory(DesignHierarchyMask.Accessory.ROOT);
        assert root != null;
        assert root.getSceneGraphObject() instanceof Node;
        assert root instanceof FXOMInstance;
        return new NodePring(contentPanelController, (FXOMInstance) root);
    }

    @Override
    public AbstractResizer<?> makeResizer(FXOMObject fxomObject) {
        // Resize gesture does not apply to Scenes
        return null;
    }

    @Override
    public AbstractCurveEditor<?> makeCurveEditor(FXOMObject fxomObject) {
        return null;
    }

    @Override
    public FXOMObject refinePick(Node hitNode, double sceneX, double sceneY, FXOMObject fxomObject) {
        return fxomObject;
    }

    @Override
    public AbstractDropTarget makeDropTarget(FXOMObject fxomObject, double sceneX, double sceneY) {
        assert fxomObject instanceof FXOMInstance;
        return new AccessoryDropTarget((FXOMInstance) fxomObject, DesignHierarchyMask.Accessory.ROOT);
    }

    @Override
    public Node getInlineEditorBounds(FXOMObject fxomObject) {
        return null;
    }

    @Override
    public boolean intersectsBounds(FXOMObject fxomObject, Bounds bounds) {
        assert fxomObject.getSceneGraphObject() instanceof Scene;
        DesignHierarchyMask designHierarchyMask = new DesignHierarchyMask(fxomObject);
        FXOMObject root = designHierarchyMask.getAccessory(DesignHierarchyMask.Accessory.ROOT);
        assert root != null;
        assert root.getSceneGraphObject() instanceof Node;
        Node rootNode = (Node) root.getSceneGraphObject();
        final Bounds rootNodeBounds = rootNode.localToScene(rootNode.getLayoutBounds(), true /* rootScene */);
        return rootNodeBounds.intersects(bounds);
    }
}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/content/mode/PickModeController.java<|end_filename|>
/*
 * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.kit.editor.panel.content.mode;

import com.oracle.javafx.scenebuilder.kit.editor.images.ImageUtils;
import com.oracle.javafx.scenebuilder.kit.editor.panel.content.ContentPanelController;
import com.oracle.javafx.scenebuilder.kit.editor.panel.content.driver.AbstractDriver;
import com.oracle.javafx.scenebuilder.kit.editor.panel.content.driver.pring.AbstractPring;
import com.oracle.javafx.scenebuilder.kit.editor.selection.ObjectSelectionGroup;
import com.oracle.javafx.scenebuilder.kit.editor.selection.Selection;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMDocument;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMObject;
import com.oracle.javafx.scenebuilder.kit.util.Deprecation;
import javafx.event.EventHandler;
import javafx.scene.Cursor;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.input.MouseEvent;

/**
 *
 * 
 */


public class PickModeController extends AbstractModeController {

    private HitNodeChrome hitNodeChrome;
    
    public PickModeController(ContentPanelController contentPanelController) {
        super(contentPanelController);
    }
    
    
    /*
     * AbstractModeController
     */
    
    @Override
    public void willResignActive(AbstractModeController nextModeController) {
        contentPanelController.getGlassLayer().setCursor(Cursor.DEFAULT);
        stopListeningToInputEvents();
        removeHitNodeChrome();
    }

    @Override
    public void didBecomeActive(AbstractModeController previousModeController) {
        assert contentPanelController.getGlassLayer() != null;
        
        updateHitNodeChrome();
        startListeningToInputEvents();
        contentPanelController.getGlassLayer().setCursor(ImageUtils.getCSSCursor());
    }
    
    @Override
    public void editorSelectionDidChange() {
        updateHitNodeChrome();
    }

    @Override
    public void fxomDocumentDidChange(FXOMDocument oldDocument) {
        // Same logic as when the scene graph is changed
        fxomDocumentDidRefreshSceneGraph();
    }

    @Override
    public void fxomDocumentDidRefreshSceneGraph() {
        updateHitNodeChrome();
    }

    @Override
    public void dropTargetDidChange() {
        // Should not be invoked : if drag gesture starts, editor controller
        // will switch to EditModeController.
        assert false;
    }
    
    
    /*
     * Private
     */
    

    private void startListeningToInputEvents() {
        final Node glassLayer = contentPanelController.getGlassLayer();
        assert glassLayer.getOnMousePressed() == null;
        
        glassLayer.setOnMousePressed(mousePressedOnGlassLayerListener);
    }
    
    private void stopListeningToInputEvents() {
        final Node glassLayer = contentPanelController.getGlassLayer();
        glassLayer.setOnMousePressed(null);
    }
    
    private final EventHandler<MouseEvent> mousePressedOnGlassLayerListener
            = e -> mousePressedOnGlassLayer(e);
    
    
    private void mousePressedOnGlassLayer(MouseEvent e) {
        
        
        final Selection selection 
                = contentPanelController.getEditorController().getSelection();
        
        final FXOMDocument fxomDocument
                = contentPanelController.getEditorController().getFxomDocument();
        
        final FXOMObject hitObject;
        final Node hitNode;
        if ((fxomDocument == null) || (fxomDocument.getFxomRoot() == null)) {
            hitObject = null;
            hitNode = null;
        } else {
            final FXOMObject fxomRoot = fxomDocument.getFxomRoot();
            final Object sceneGraphRoot = fxomRoot.getSceneGraphObject();
            if (sceneGraphRoot instanceof Node) {
                hitNode = Deprecation.pick((Node)sceneGraphRoot, e.getSceneX(), e.getSceneY());
                FXOMObject fxomObject = null;
                Node node = hitNode;
                while ((fxomObject == null) && (node != null)) {
                    fxomObject = fxomRoot.searchWithSceneGraphObject(node);
                    node = node.getParent();
                }
                hitObject = fxomObject;
            } else {
                hitObject = null;
                hitNode = null;
            }
        }
        
        if (hitObject == null) {
            selection.clear();
        } else {
            if (selection.isSelected(hitObject)) {
                assert selection.getGroup() instanceof ObjectSelectionGroup;
                selection.updateHitObject(hitObject, hitNode);
            } else {
                selection.select(hitObject, hitNode);
            }
        }
    }
    
    
    private void updateHitNodeChrome() {
        final Selection selection = contentPanelController.getEditorController().getSelection();
        final HitNodeChrome newChrome;
        
        if ((hitNodeChrome == null) 
                || (hitNodeChrome.getFxomObject() != selection.getHitItem())
                || (hitNodeChrome.getHitNode() != selection.getCheckedHitNode())) {
            if ((selection.getHitItem() != null) && (selection.getCheckedHitNode() != null)){
                newChrome = makeHitNodeChrome(selection.getHitItem(), selection.getCheckedHitNode());
            } else {
                newChrome = null;
            }
        } else {
            switch(hitNodeChrome.getState()) {
                default:
                case CLEAN:
                    newChrome = hitNodeChrome;
                    break;
                case NEEDS_RECONCILE:
                    newChrome = hitNodeChrome;
                    hitNodeChrome.reconcile();
                    break;
                case NEEDS_REPLACE:
                    newChrome = makeHitNodeChrome(selection.getHitItem(), selection.getCheckedHitNode());
                    assert newChrome.getState() == HitNodeChrome.State.CLEAN;
                    break;
            }
        }
        
        if (newChrome != hitNodeChrome) {
            final Group rudderLayer = contentPanelController.getRudderLayer();
            if (hitNodeChrome != null) {
                rudderLayer.getChildren().remove(hitNodeChrome.getRootNode());
            }
            hitNodeChrome = newChrome;
            if (hitNodeChrome != null) {
                rudderLayer.getChildren().add(hitNodeChrome.getRootNode());
            }
        } else {
            assert (hitNodeChrome == null) || hitNodeChrome.getState() == AbstractPring.State.CLEAN;
        }
    }
    
    
    private HitNodeChrome makeHitNodeChrome(FXOMObject hitItem, Node hitNode) {
        final HitNodeChrome result;
        
        assert hitItem != null;
        
        /*
         * In some cases, we cannot make a chrome for some hitObject
         * 
         *  MenuButton          <= OK
         *      CustomMenuItem  <= KO because MenuItem are not displayable (case #1)
         *          CheckBox    <= KO because this CheckBox is in a separate scene (Case #2)
         */
        
        final AbstractDriver driver = contentPanelController.lookupDriver(hitItem);
        if (driver == null) {
            // Case #1 above
            result = null;
        } else {
            final FXOMObject closestNodeObject = hitItem.getClosestNode();
            if (closestNodeObject == null) {
                // Document content is not displayable in content panel
                result = null;
            } else {
                assert closestNodeObject.getSceneGraphObject() instanceof Node;
                final Node closestNode = (Node)closestNodeObject.getSceneGraphObject();
                if (closestNode.getScene() == contentPanelController.getPanelRoot().getScene()) {
                    result = new HitNodeChrome(contentPanelController, hitItem, hitNode);
                } else {
                    // Case #2 above
                    result = null;
                }
            }
        }
        
        return result;
    }
    
    
    private void removeHitNodeChrome() {
        if (hitNodeChrome != null) {
            final Group rudderLayer = contentPanelController.getRudderLayer();
            rudderLayer.getChildren().remove(hitNodeChrome.getRootNode());
            hitNodeChrome = null;
        }
    }
}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/preferences/PreferencesRecordArtifact.java<|end_filename|>
/*
 * Copyright (c) 2016, Gluon and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation and Gluon nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.kit.preferences;

import com.oracle.javafx.scenebuilder.kit.editor.panel.library.maven.MavenArtifact;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.prefs.BackingStoreException;
import java.util.prefs.Preferences;

/**
 * Defines artifacts preferences global to the application.
 */
public class PreferencesRecordArtifact {
    
    private final Preferences artifactsRootPreferences;
    private Preferences artifactPreferences;
    private final static String GROUPID  = "groupID";
    private final static String ARTIFACTID  = "artifactId";
    private final static String VERSION  = "version";
    public final static String DEPENDENCIES = "dependencies";
    public final static String FILTER = "filter";
    public final static String PATH = "path";
    
    private String groupId;
    private String artifactId;
    private String version;
    private final MavenArtifact mavenArtifact;
    
    public PreferencesRecordArtifact(Preferences artifactsRootPreferences, MavenArtifact mavenArtifact) {
        this.artifactsRootPreferences = artifactsRootPreferences;
        this.mavenArtifact = mavenArtifact;
    }
    
    public String getGroupId() {
        return groupId;
    }

    public void setGroupId(String groupId) {
        this.groupId = groupId;
    }

    public String getArtifactId() {
        return artifactId;
    }

    public void setArtifactId(String artifactId) {
        this.artifactId = artifactId;
    }

    public String getVersion() {
        return version;
    }

    public void setVersion(String version) {
        this.version = version;
    }

    public String getPath() {
        return mavenArtifact.getPath();
    }

    public MavenArtifact getMavenArtifact() {
        return mavenArtifact;
    }
    /**
     * Read data from the java preferences DB and initialize properties.
     */
    public void readFromJavaPreferences() {

        assert artifactPreferences == null;
        
        // Check if there are some preferences for this artifact
        try {
            final String[] childrenNames = artifactsRootPreferences.childrenNames();
            for (String child : childrenNames) {
                if (child.equals(mavenArtifact.getCoordinates())) {
                    artifactPreferences = artifactsRootPreferences.node(child);
                }
            }
        } catch (BackingStoreException ex) {
            Logger.getLogger(PreferencesRecordArtifact.class.getName()).log(Level.SEVERE, null, ex);
        }
            
        if (artifactPreferences == null) {
            return;
        }
        
        setGroupId(artifactPreferences.get(GROUPID, null));
        setArtifactId(artifactPreferences.get(ARTIFACTID,null));
        setVersion(artifactPreferences.get(VERSION, null));
        mavenArtifact.setDependencies(artifactPreferences.get(DEPENDENCIES, null));
        mavenArtifact.setFilter(artifactPreferences.get(FILTER, null));
        mavenArtifact.setPath(artifactPreferences.get(PATH, null));
    }
    
    /**
     * Write the properties data to the java preferences DB.
     */
    public void writeToJavaPreferences() {
        assert artifactsRootPreferences != null;
        assert mavenArtifact.getCoordinates() != null;
        
        if (artifactPreferences == null) {
            try {
                assert artifactsRootPreferences.nodeExists(mavenArtifact.getCoordinates()) == false;
                // Create a new document preference node under the document root node
                artifactPreferences = artifactsRootPreferences.node(mavenArtifact.getCoordinates());
            } catch(BackingStoreException ex) {
                Logger.getLogger(PreferencesRecordArtifact.class.getName()).log(Level.SEVERE, null, ex);
                return;
            }
        }
        assert artifactPreferences != null;
            
        String[] items = mavenArtifact.getCoordinates().split(":");
        artifactPreferences.put(GROUPID, items[0]);
        artifactPreferences.put(ARTIFACTID, items[1]);
        artifactPreferences.put(VERSION, items[2]);
        artifactPreferences.put(DEPENDENCIES, mavenArtifact.getDependencies());
        artifactPreferences.put(FILTER, mavenArtifact.getFilter());
        artifactPreferences.put(PATH, mavenArtifact.getPath());
    }
}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/inspector/editors/EditorUtils.java<|end_filename|>
/*
 * Copyright (c) 2017, 2018 Gluon and/or its affiliates.
 * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.kit.editor.panel.inspector.editors;

import com.oracle.javafx.scenebuilder.kit.editor.EditorPlatform;
import com.oracle.javafx.scenebuilder.kit.i18n.I18N;
import com.oracle.javafx.scenebuilder.kit.editor.panel.inspector.editors.PropertyEditor.LayoutFormat;
import com.oracle.javafx.scenebuilder.kit.editor.panel.inspector.popupeditors.PopupEditor;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMObject;
import com.oracle.javafx.scenebuilder.kit.metadata.Metadata;
import com.oracle.javafx.scenebuilder.kit.metadata.klass.ComponentClassMetadata;
import com.oracle.javafx.scenebuilder.kit.metadata.property.PropertyMetadata;
import com.oracle.javafx.scenebuilder.kit.metadata.property.ValuePropertyMetadata;
import com.oracle.javafx.scenebuilder.kit.metadata.util.PrefixedValue;
import com.oracle.javafx.scenebuilder.kit.metadata.util.PropertyName;
import javafx.animation.AnimationTimer;
import javafx.animation.FadeTransition;
import javafx.beans.InvalidationListener;
import javafx.beans.Observable;
import javafx.beans.property.BooleanProperty;
import javafx.collections.ObservableList;
import javafx.fxml.FXMLLoader;
import javafx.geometry.Bounds;
import javafx.geometry.VPos;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.control.Control;
import javafx.scene.control.MenuButton;
import javafx.scene.layout.*;

import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Locale;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
        
/**
 * Utility class for property editors.
 *
 *
 */
public class EditorUtils {

    static final String[] FXML_RESERVED_KEYWORDS = {"null"}; //NOI18N
    private static final String FXINCLUDE_JAVADOC_URL = "https://openjfx.io/javadoc/11/javafx.fxml/javafx/fxml/doc-files/introduction_to_fxml.html#include_elements";

    public static void makeWidthStretchable(final Node node) {
        Parent p = node.getParent();
        if (p == null) {
            node.parentProperty().addListener(new InvalidationListener() {
                @Override
                public void invalidated(Observable valueModel) {
                    if (node.getParent() != null) {
                        makeWidthStretchable(node, node.getParent());
                        node.parentProperty().removeListener(this);
                    }
                }
            });
        } else {
            makeWidthStretchable(node, p);
        }
    }

    private static void makeWidthStretchable(Node node, Parent p) {
        if (p != null) {
            if (p instanceof HBox) {
                HBox.setHgrow(node, Priority.ALWAYS);
            } else if (p instanceof GridPane) {
                GridPane.setHgrow(node, Priority.ALWAYS);
            }
        }
        if (node instanceof Region) {
            ((Region) node).setMinWidth(0);
            ((Region) node).setMaxWidth(Double.MAX_VALUE);
        }
    }

    public static void makeUnstretchable(final Control node, double width, double height) {
        node.setPrefWidth(width);
        node.setMinWidth(width);
        node.setMaxWidth(width);
        node.setPrefHeight(height);
        node.setMinHeight(height);
        node.setMaxHeight(height);
    }

    // Replace a node (wich is in the scene graph) with another node (which is NOT in the scene graph)
    // and adapt the layout if provided.
    public static void replaceNode(Node node, Node newNode, LayoutFormat layoutFormat) {
        Parent parent = node.getParent();
        boolean hasGridPaneParent = parent instanceof GridPane;
        if (parent instanceof Pane) {
            // Supporting Pane only should be enough for now...
            ObservableList<Node> children = ((Pane) parent).getChildren();

            // Remove node
            int childIndex = children.indexOf(node);
            int columnIndex = -1;
            int rowIndex = -1;
            if (hasGridPaneParent) {
                columnIndex = GridPane.getColumnIndex(node);
                rowIndex = GridPane.getRowIndex(node);
            }
            children.remove(childIndex);
            // Remove row constraints if needed
            if (hasGridPaneParent && (layoutFormat != null)) {
                ((GridPane) parent).getRowConstraints().remove(rowIndex);
            }

            // Add new node
            GridPane.setRowIndex(newNode, rowIndex);
            GridPane.setColumnIndex(newNode, columnIndex);
            children.add(childIndex, newNode);
            // Add new row constraints if needed
            if (hasGridPaneParent && (layoutFormat != null)) {
                RowConstraints rowConstraints = new RowConstraints();
                if (layoutFormat == LayoutFormat.SIMPLE_LINE_CENTERED) {
                    rowConstraints.setValignment(VPos.CENTER);
                } else if (layoutFormat == LayoutFormat.SIMPLE_LINE_TOP) {
                    rowConstraints.setValignment(VPos.TOP);
                } else {
                    throw new UnsupportedOperationException(
                            "replaceNode() - GridPane: layout change in double line not supported yet!");
                }
                ((GridPane) parent).getRowConstraints().add(rowIndex, rowConstraints);
            }
        }
    }

    public static String valAsStr(Object val) {
        if (val == null) {
            return null;
        }
        String str = val.toString();
        if ((val instanceof Double) && str.endsWith(".0")) { //NOI18N
            str = str.substring(0, str.length() - 2);
        }
        return str;
    }

    /**
     * Takes the given name and formats it for display. Given a camel-case name,
     * it will split the name at each of the upper-case letters, unless multiple
     * uppercase letters are in a series, in which case it treats them as a
     * single name. The initial lower-case letter is upper cased. So a name like
     * "translateX" becomes "Translate X" and a name like "halign" becomes
     * "Halign".
     * <p>
     * Numbers are treated the same as if they were capital letters, such that
     * "MyClass3" would become "My Class 3" and "MyClass23" would become "My
     * Class 23".
     * <p>
     * Underscores are converted to spaces, with the first letter following the
     * underscore converted to upper case. Multiple underscores in a row are
     * treated only as a single space, and any leading or trailing underscores
     * are skipped.
     *
     * @param name
     * @return
     */
    public static String toDisplayName(String name) {
        if (name == null) {
            return name;
        }
        // Replace all underscores with empty spaces
        name = name.replace("_", " "); //NOI18N
        // Trim out any leading or trailing space (which also effectively
        // removes any underscores that were leading or trailing, since the
        // above line had converted them all to spaces).
        name = name.trim();
        // If the resulting name is empty, return an empty string
        if (name.length() == 0) {
            return name;
        }
        // There are now potentially spaces already in the name. If, while
        // iterating over all of the characters in the name we encounter a
        // space, then we will simply step past the space and capitalize the
        // following character.
        name = Character.toUpperCase(name.charAt(0)) + name.substring(1);
        StringBuilder builder = new StringBuilder();
        char ch = name.charAt(0);
        builder.append(ch);
        boolean previousWasDigit = Character.isDigit(ch);
        boolean previousWasCapital = !previousWasDigit;
        for (int i = 1; i < name.length(); i++) {
            ch = name.charAt(i);
            if ((Character.isUpperCase(ch) && !previousWasCapital)
                    || (Character.isUpperCase(ch) && previousWasDigit)) {
                builder.append(" "); //NOI18N
                builder.append(ch);
                previousWasCapital = true;
                previousWasDigit = false;
            } else if ((Character.isDigit(ch) && !previousWasDigit)
                    || (Character.isDigit(ch) && previousWasCapital)) {
                builder.append(" "); //NOI18N
                builder.append(ch);
                previousWasCapital = false;
                previousWasDigit = true;
            } else if (Character.isUpperCase(ch) || Character.isDigit(ch)) {
                builder.append(ch);
            } else if (Character.isWhitespace(ch)) {
                builder.append(" "); //NOI18N
                // There might have been multiple underscores in a row, so
                // we might now have multiple whitespace in a row. Search ahead
                // to the first non-whitespace character.
                ch = name.charAt(++i);
                while (Character.isWhitespace(ch)) {
                    // Note that because we trim the String, it should be
                    // impossible to have trailing whitespace, and thus we
                    // don't have to worry about the ArrayIndexOutOfBounds
                    // condition here.
                    ch = name.charAt(++i);
                }
                builder.append(Character.toUpperCase(ch));
                previousWasDigit = Character.isDigit(ch);
                previousWasCapital = !previousWasDigit;
            } else {
                previousWasCapital = false;
                previousWasDigit = false;
                builder.append(ch);
            }
        }
        return builder.toString();
    }

    // Check if this could be moved in the Metadata classes.
    // Get the component class metadata where a property is defined.
    private static ComponentClassMetadata getDefiningClass(Class<?> clazz, PropertyName propName) {
        Metadata metadata = Metadata.getMetadata();
        ComponentClassMetadata classMeta = metadata.queryComponentMetadata(clazz);
        while (clazz != null) {
            for (PropertyMetadata propMeta : classMeta.getProperties()) {
                if (propMeta.getName().compareTo(propName) == 0) {
                    return classMeta;
                }
            }
            // Check the inherited classes
            classMeta = classMeta.getParentMetadata();
        }
        return null;
    }

    private static class NextFrameTimer extends AnimationTimer {

        final AtomicInteger count = new AtomicInteger(0);
        final int order;
        Runnable callback;

        public NextFrameTimer(Runnable callback) {
            this(callback, 1);
        }

        public NextFrameTimer(Runnable callback, int order) {
            assert order >= 0;
            this.callback = callback;
            this.order = order;
        }

        @Override
        public void handle(long now) {
            if (count.getAndIncrement() == this.order) {
                try {
                    callback.run();
                } finally {
                    stop();
                }
            }
        }
    }

    public static AnimationTimer doEndOfFrame(final Runnable callback) {
        AnimationTimer timer = new NextFrameTimer(callback, 0);
        timer.start();
        return timer;
    }

    public static AnimationTimer doNextFrame(final Runnable callback) {
        AnimationTimer timer = new NextFrameTimer(callback);
        timer.start();
        return timer;
    }

    /*
     * Round a double value, number of decimals depends on the roundingFactor.
     * e.g. round(10.1233, 100) returns 10.12
     */
    public static double round(double value, int roundingFactor) {
        double doubleRounded = Math.round(value * roundingFactor);
        return doubleRounded / roundingFactor;
    }

    public static double computeLeftAnchor(FXOMObject selectedInstance) {
        Node node = getFxNode(selectedInstance);
        return computeLeftAnchor(node, node.getLayoutBounds());
    }

    public static double computeRightAnchor(FXOMObject selectedInstance) {
        Node node = getFxNode(selectedInstance);
        return computeRightAnchor(node, node.getLayoutBounds());
    }

    public static double computeTopAnchor(FXOMObject selectedInstance) {
        Node node = getFxNode(selectedInstance);
        return computeTopAnchor(node, node.getLayoutBounds());
    }

    public static double computeBottomAnchor(FXOMObject selectedInstance) {
        Node node = getFxNode(selectedInstance);
        return computeBottomAnchor(node, node.getLayoutBounds());
    }

    private static double computeLeftAnchor(Node node, Bounds futureLayoutBounds) {
        return node.getLayoutX() + futureLayoutBounds.getMinX() - node.getParent().getLayoutBounds().getMinX();
    }

    private static double computeRightAnchor(Node node, Bounds futureLayoutBounds) {
        return node.getParent().getLayoutBounds().getMaxX() - node.getLayoutX() - futureLayoutBounds.getMaxX();
    }

    private static double computeTopAnchor(Node node, Bounds futureLayoutBounds) {
        return node.getLayoutY() + futureLayoutBounds.getMinY() - node.getParent().getLayoutBounds().getMinY();
    }

    private static double computeBottomAnchor(Node node, Bounds futureLayoutBounds) {
        return node.getParent().getLayoutBounds().getMaxY() - node.getLayoutY() - futureLayoutBounds.getMaxY();
    }

    private static Node getFxNode(FXOMObject selectedInstance) {
        Object selectedObj = selectedInstance.getSceneGraphObject();
        assert selectedObj instanceof Node;
        return (Node) selectedObj;
    }
    
    public static void handleFading(FadeTransition fadeTransition, Node fadingSource) {
        handleFading(fadeTransition, fadingSource, null);
    }

    /*
     * Fade in / fade out a node from its FadeTransition.
     * Fading is activated from a fadingSource node.
     */
    public static void handleFading(FadeTransition fadeTransition, Node fadingSource, BooleanProperty disableProperty) {
        fadingSource.setOnMouseEntered(arg0 -> {
            Node targetNode = fadeTransition.getNode();
            if ((targetNode instanceof MenuButton) && ((MenuButton) targetNode).isShowing()) {
                return;
            }
            if (disableProperty != null && disableProperty.getValue()) {
                // Nothing to do if disabled
                return;
            }
            fadeTo(fadeTransition, 1);
        });
        fadingSource.setOnMouseExited(arg0 -> {
            Node targetNode = fadeTransition.getNode();
            if ((targetNode instanceof MenuButton) && ((MenuButton) targetNode).isShowing()) {
                return;
            }
            if (disableProperty != null && disableProperty.getValue()) {
                // Nothing to do if disabled
                return;
            }
            fadeTo(fadeTransition, 0);
        });
    }

    public static void fadeTo(FadeTransition fadeTransition, double toValue) {
        fadeTransition.stop();
        fadeTransition.setFromValue(fadeTransition.getNode().getOpacity());
        fadeTransition.setToValue(toValue);
        fadeTransition.play();
    }

    protected static void openUrl(Set<Class<?>> selectedClasses, ValuePropertyMetadata propMeta) throws IOException {
        Class<?> clazz = null;
        // In case of static property, we don't care of the selectedClasses
        if (selectedClasses != null) {
            for (Class<?> cl : selectedClasses) {
                clazz = cl;
            }
        }
        PropertyName propertyName = propMeta.getName();
        if (propMeta.isStaticProperty()) {
            clazz = propertyName.getResidenceClass();
        } else {
            clazz = getDefiningClass(clazz, propertyName).getKlass();
        }
        String propNameStr = propertyName.getName();
        // First char in uppercase
        propNameStr = propNameStr.substring(0, 1).toUpperCase(Locale.ENGLISH) + propNameStr.substring(1);
        String methodName;
        String posfix;
        if (clazz.getName().startsWith(EditorPlatform.GLUON_PACKAGE)) {
            posfix = "--";
        } else {
            posfix = "()";
        }
        if (propMeta.getValueClass() == Boolean.class) {
            methodName = "is" + propNameStr + posfix; //NOI18N
        } else if (propMeta.isStaticProperty()) {
            methodName = "get" + propNameStr + "(" + Node.class.getName() + ")"; //NOI18N
        } else {
            methodName = "get" + propNameStr + posfix; //NOI18N
        }

        String url;
        if (clazz.getName().startsWith(EditorPlatform.GLUON_PACKAGE)) {
            url = EditorPlatform.GLUON_JAVADOC_HOME;
        } else {
            url = EditorPlatform.JAVADOC_HOME + clazz.getModule().getName() + "/";
        }
        url += clazz.getName().replaceAll("\\.", "/") + ".html"; //NOI18N
        url += "#" + methodName; //NOI18N
        EditorPlatform.open(url);
    }

    // Specific swap() function for an ObservableList:
    // Collections.swap() directly on the ObservableList generates a "duplicate children added" error
    public static void swap(ObservableList<Node> list, int i, int j) {
        ArrayList<Node> children = new ArrayList<>(list);
        Collections.swap(children, i, j);
        // Workaround for RT-31965: list re-arrangement is not detected...
        // list.setAll(children);
        list.clear();
        list.addAll(children);
    }

    public static Parent loadFxml(String fxmlFileName, Object controller) {
        URL fxmlURL = EditorUtils.class.getResource(fxmlFileName);
        return loadFxml(fxmlURL, controller);
    }

    public static Parent loadPopupFxml(String fxmlFileName, Object controller) {
        URL fxmlURL = PopupEditor.class.getResource(fxmlFileName);
        return loadFxml(fxmlURL, controller);
    }

    // To be used only for inspector editors (which are in the same classpath than this class)
    public static Parent loadFxml(URL fxmlURL, Object controller) {
        final FXMLLoader loader = new FXMLLoader();
        loader.setController(controller);
        loader.setLocation(fxmlURL);
        loader.setResources(I18N.getBundle());

        // setting ClassLoader for OSGi environments
        loader.setClassLoader(controller.getClass().getClassLoader());

        Parent root;
        try {
            root = (Parent) loader.load();
        } catch (IOException ex) {
            throw new RuntimeException("Failed to load " + fxmlURL.getFile(), ex); //NOI18N
        }
        return root;
    }

    public static String getFileName(String urlStr) {
        URL url;
        try {
            url = new URL(urlStr);
        } catch (MalformedURLException ex) {
            System.err.println("Invalid URL: " + urlStr); //NOI18N
            assert false;
            return null;
        }
        String[] urlParts = url.getPath().split("\\/");
        return urlParts[urlParts.length - 1];
    }

    public static boolean areEqual(Object obj1, Object obj2) {
        if ((obj1 == null) || (obj2 == null)) {
            if (obj1 != obj2) {
                return false;
            }
        } else if (!obj1.equals(obj2)) {
            return false;
        }
        return true;
    }

    public static URL getUrl(String suffix, PrefixedValue.Type type, URL fxmlFileLocation) {
        String prefixedString;
        if (suffix.isEmpty()) {
            prefixedString = ""; //NOI18N
        } else {
            prefixedString = (new PrefixedValue(type, suffix)).toString();
        }
        return getUrl(prefixedString, fxmlFileLocation);
    }
    
    // Get the URL corresponding to a PrefixedValue string
//    @SuppressWarnings("UseSpecificCatch")
    public static URL getUrl(String prefixedString, URL fxmlFileLocation) {
        PrefixedValue prefixedValue = new PrefixedValue(prefixedString);
        URL url = null;
        if (prefixedValue.getType() == PrefixedValue.Type.DOCUMENT_RELATIVE_PATH) {
            url = prefixedValue.resolveDocumentRelativePath(fxmlFileLocation);
        } else if (prefixedValue.getType() == PrefixedValue.Type.PLAIN_STRING) {
            try {
                url = new URI(prefixedValue.getSuffix()).toURL();
            } catch (Throwable ex) {
                // Catching *all* the exception is done on purpose.
                // May happen. nothing to do.
            }
        }
        return url;
    }

    public static String getPlainString(String str) {
        // Using PrefixedValue PLAIN_STRING allow to consider special characters (such as @, %,...)
        // as "standard" characters (i.e. to backslash them)
        return new PrefixedValue(PrefixedValue.Type.PLAIN_STRING, str).toString();
    }
    
    // From an url, returns the file name only (without the path)
    public static String getSimpleFileName(String url) {
        File file = new File(url);
        return file.getName();
    }
    
    public static String toString(Object obj) {
        assert obj instanceof String;
        return (String) obj; 
    }
}


<|start_filename|>app/src/main/java/com/oracle/javafx/scenebuilder/app/SceneStyleSheetMenuController.java<|end_filename|>
/*
 * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.app;

import com.oracle.javafx.scenebuilder.app.i18n.I18N;
import com.oracle.javafx.scenebuilder.kit.editor.EditorController;
import com.oracle.javafx.scenebuilder.kit.editor.EditorPlatform;
import com.oracle.javafx.scenebuilder.kit.editor.panel.util.dialog.ErrorDialog;

import java.io.File;
import java.io.IOException;
import java.util.List;

import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.stage.FileChooser;

/**
 *
 */
public class SceneStyleSheetMenuController {
    private final DocumentWindowController documentWindowController;
    
    public SceneStyleSheetMenuController(DocumentWindowController dwc) {
        this.documentWindowController = dwc;
    }

    void performAddSceneStyleSheet() {
        boolean knownFilesModified = false;
        ObservableList<File> knownFiles = documentWindowController.getEditorController().getSceneStyleSheets();
        
        if (knownFiles == null) {
            knownFiles = FXCollections.observableArrayList();
        }
        
        // Open a file chooser for *.css & *.bss
        FileChooser fileChooser = new FileChooser();
        fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter(
                com.oracle.javafx.scenebuilder.kit.i18n.I18N.getString("scenestylesheet.filechooser.filter.msg"),
                "*.css", "*.bss")); //NOI18N
        fileChooser.setInitialDirectory(EditorController.getNextInitialDirectory());
        List<File> selectedFiles = fileChooser.showOpenMultipleDialog(documentWindowController.getStage());
        
        if (selectedFiles != null) {
            assert selectedFiles.isEmpty() == false;
            // Keep track of the user choice for next time
            EditorController.updateNextInitialDirectory(selectedFiles.get(0));
            for (File f : selectedFiles) {
                if (!knownFiles.contains(f)) {
                    knownFiles.add(f);
                    knownFilesModified = true;
                }
            }
        }

        // Update sceneStyleSheet property so that listeners will react accordingly
        if (knownFilesModified) {
            documentWindowController.getEditorController().setSceneStyleSheets(knownFiles);
        }
    }
    
    public void performRemoveSceneStyleSheet(File toRemove) {
        ObservableList<File> knownFiles = documentWindowController.getEditorController().getSceneStyleSheets();
        assert knownFiles != null;
        
        if (knownFiles.contains(toRemove)) {
            knownFiles.remove(toRemove);
            documentWindowController.getEditorController().setSceneStyleSheets(knownFiles);
        }
    }
    
    public void performOpenSceneStyleSheet(File toOpen) {
        try {
            EditorPlatform.open(toOpen.getPath());
        } catch (IOException ioe) {
            final ErrorDialog errorDialog = new ErrorDialog(null);
            errorDialog.setTitle(I18N.getString("error.file.open.title"));
            errorDialog.setMessage(I18N.getString("error.file.open.message"));
            errorDialog.setDetails(I18N.getString("error.filesystem.details"));
            errorDialog.setDebugInfoWithThrowable(ioe);
            errorDialog.showAndWait();
        }
    }
    
}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/selection/ObjectSelectionGroup.java<|end_filename|>
/*
 * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.kit.editor.selection;

import com.oracle.javafx.scenebuilder.kit.fxom.FXOMDocument;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMNodes;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMObject;
import com.oracle.javafx.scenebuilder.kit.metadata.util.DesignHierarchyPath;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import javafx.scene.Node;

/**
 *
 * 
 */
public class ObjectSelectionGroup extends AbstractSelectionGroup {
    
    private final Set<FXOMObject> items = new HashSet<>();
    private final FXOMObject hitItem;
    private final Object hitSceneGraphObject;
    private final Node hitNode;
    
    ObjectSelectionGroup(FXOMObject fxomObject, Node hitNode) {
        assert fxomObject != null;
        this.items.add(fxomObject);
        this.hitItem = fxomObject;
        this.hitSceneGraphObject = fxomObject.getSceneGraphObject();
        this.hitNode = hitNode;
    }
    
    public ObjectSelectionGroup(Collection<FXOMObject> fxomObjects, FXOMObject hitItem, Node hitNode) {
        assert fxomObjects != null;
        assert hitItem != null;
        assert fxomObjects.contains(hitItem);
        this.items.addAll(fxomObjects);
        this.hitItem = hitItem;
        this.hitSceneGraphObject = this.hitItem.getSceneGraphObject();
        this.hitNode = hitNode;
    }
    
    public Set<FXOMObject> getItems() {
        return Collections.unmodifiableSet(items);
    }

    public FXOMObject getHitItem() {
        return hitItem;
    }

    public Node getHitNode() {
        return hitNode;
    }
    
    public boolean isExpired() {
        return hitItem.getSceneGraphObject() != hitSceneGraphObject;
    }
    
    public Node getCheckedHitNode() {
        final Node result;
        
        if ((hitNode == null) || isExpired()) {
            result = getFallbackHitNode();
        } else {
            result = hitNode;
        }
        
        return result;
    }
    
    public Node getFallbackHitNode() {
        final Node result;
        
        if (hitItem.isNode()) {
            result = (Node) hitItem.getSceneGraphObject();
        } else {
            final FXOMObject closestNodeObject = hitItem.getClosestNode();
            if (closestNodeObject != null) {
                result = (Node) closestNodeObject.getSceneGraphObject();
            } else {
                result = null;
            }
        }
        
        return result;
    }
    
    public Set<FXOMObject> getFlattenItems() {
        return FXOMNodes.flatten(items);
    }
    
    public List<FXOMObject> getSortedItems() {
        return FXOMNodes.sort(items);
    }
    
    public boolean hasSingleParent() {
        final boolean result;
        
        if (items.size() == 1) {
            result = true;
        } else {
            final Set<FXOMObject> parents = new HashSet<>();
            for (FXOMObject i : items) {
                parents.add(i.getParentObject());
            }
            result = parents.size() == 1;
        }
        
        return result;
    }
    
    /*
     * AbstractSelectionGroup
     */
    
    @Override
    public FXOMObject getAncestor() {
        final FXOMObject result;
        
        assert items.isEmpty() == false;
        
        switch(items.size()) {

            case 0:
                result = null;
                break;

            case 1:
                result = items.iterator().next().getParentObject();
                break;

            default:
                DesignHierarchyPath commonPath = null;
                for (FXOMObject i : items) {
                    final FXOMObject parent = i.getParentObject();
                    if (parent != null) {
                        final DesignHierarchyPath dph = new DesignHierarchyPath(parent);
                        if (commonPath == null) {
                            commonPath = dph;
                        } else {
                            commonPath = commonPath.getCommonPathWith(dph);
                        }
                    }
                }
                assert commonPath != null; // Else it would mean root is selected twice
                result = commonPath.getLeaf();
                break;
        }
        
        return result;
    }

    @Override
    public boolean isValid(FXOMDocument fxomDocument) {
        assert fxomDocument != null;
        
        boolean result;
        final FXOMObject fxomRoot = fxomDocument.getFxomRoot();
        if (fxomRoot == null) {
            result = false;
        } else {
            result = true;
            for (FXOMObject i : items) {
                final boolean ok = (i == fxomRoot) || i.isDescendantOf(fxomRoot);
                if (ok == false) {
                    result = false;
                    break;
                }
            }
        }
        
        return result;
    }
    
    
    /*
     * Cloneable
     */
    @Override
    public ObjectSelectionGroup clone() throws CloneNotSupportedException {
        return (ObjectSelectionGroup)super.clone();
    }

    
    /*
     * Object
     */
    @Override
    public int hashCode() {
        int hash = 3;
        hash = 41 * hash + Objects.hashCode(this.items);
        hash = 41 * hash + Objects.hashCode(this.hitItem);
        hash = 41 * hash + Objects.hashCode(this.hitNode);
        return hash;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final ObjectSelectionGroup other = (ObjectSelectionGroup) obj;
        if (!Objects.equals(this.items, other.items)) {
            return false;
        }
        if (!Objects.equals(this.hitItem, other.hitItem)) {
            return false;
        }
        if (this.hitNode != other.hitNode) {
            return false;
        }
        return true;
    }
}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/inspector/editors/DurationEditor.java<|end_filename|>
/*
 * Copyright (c) 2016, Gluon and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation and Gluon nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */

package com.oracle.javafx.scenebuilder.kit.editor.panel.inspector.editors;

import com.oracle.javafx.scenebuilder.kit.i18n.I18N;
import com.oracle.javafx.scenebuilder.kit.editor.panel.inspector.editors.util.SBDuration;
import com.oracle.javafx.scenebuilder.kit.metadata.property.ValuePropertyMetadata;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.control.ComboBox;
import javafx.scene.layout.StackPane;
import javafx.util.Duration;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Set;

/**
 * Editor for Duration properties.
 */
public class DurationEditor extends AutoSuggestEditor{

    private static final Map<String, SBDuration> constants = new HashMap<>();

    private static final String MILLISECONDS = I18N.getString("inspector.duration.milliseconds");
    private static final String SECONDS = I18N.getString("inspector.duration.seconds");
    private static final String MINUTES = I18N.getString("inspector.duration.minutes");
    private static final String HOURS = I18N.getString("inspector.duration.hours");

    static {
        // Doesn't work because Duration.valueOf(String) is broken: doesn't accept "INDEFINITE"
//        constants.put("INDEFINITE", new SBDuration(Duration.INDEFINITE));
    }

    @FXML
    private StackPane textfieldStackPane;
    @FXML
    private ComboBox unitsComboBox;



    private Parent root;

    public DurationEditor(ValuePropertyMetadata propMeta, Set<Class<?>> selectedClasses) {
        super(propMeta, selectedClasses, new ArrayList<>(constants.keySet()), AutoSuggestEditor.Type.DOUBLE);
        initialize();
    }

    private void initialize() {
        root = EditorUtils.loadFxml("DurationEditor.fxml", this);

        //
        // Text field
        //
        EventHandler<ActionEvent> onActionListener = event -> {
            if (isHandlingError()) {
                // Event received because of focus lost due to error dialog
                return;
            }
            if (isUpdateFromModel()) {
                // nothing to do
                return;
            }

            Object value = getValue();
            assert value instanceof SBDuration;
            SBDuration valDuration = (SBDuration) value;
            // Check if the entered value is a constant string
            boolean isConstant = constants.get(getTextField().getText().toUpperCase(Locale.ROOT)) != null;
            // Check if the entered value is a constant value
            for (Map.Entry<String, SBDuration> entry : constants.entrySet()) {
                if (value.equals(entry.getValue())) {
                    isConstant = true;
                    break;
                }
            }
            getTextField().selectAll();
            userUpdateValueProperty(valDuration);
        };
        setNumericEditorBehavior(this, getTextField(), onActionListener, false);

        //
        // ComboBox
        //
        localizeComboBox();
        unitsComboBox.getSelectionModel().select(MILLISECONDS);
        unitsComboBox.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {
            SBDuration value = getValue(getTextField().getText(), (String)oldValue);
            getTextField().setText(getNumericValue(value, (String)newValue).toString());
        });

        // Add the AutoSuggest text field in the scene graph
        textfieldStackPane.getChildren().add(super.getRoot());
    }

    private void localizeComboBox() {
        ArrayList<String> localizedStrings = new ArrayList<>();
        for (Object item : unitsComboBox.getItems()) {
            String itemString = (String) item;
            if (itemString.charAt(0) == '%') {
                localizedStrings.add(I18N.getString(itemString.substring(1)));
            } else {
                localizedStrings.add(itemString);
            }
        }
        unitsComboBox.getItems().clear();
        unitsComboBox.getItems().addAll(localizedStrings);
    }

    @Override
    public Node getValueEditor() {
        return super.handleGenericModes(root);
    }

    @Override
    public Object getValue() {
        return getValue(getTextField().getText(), (String)unitsComboBox.getSelectionModel().getSelectedItem());
    }

    private SBDuration getValue(String valueString, String units) {
        if (valueString.isEmpty()) {
            valueString = "0"; //NOI18N
            getTextField().setText(valueString);
            return new SBDuration(Duration.ZERO);
        }
        SBDuration constantValue = constants.get(valueString.toUpperCase(Locale.ROOT));
        if (constantValue != null) {
            return constantValue;
        }
        try {
            if (units.equals(MILLISECONDS)) {
                valueString = valueString + "ms";
            } else if (units.equals(SECONDS)) {
                valueString = valueString + "s";
            } else if (units.equals(MINUTES)) {
                valueString = valueString + "m";
            } else if (units.equals(HOURS)) {
                valueString = valueString + "h";
            }
            return SBDuration.valueOf(valueString);
        } catch (NumberFormatException e) {
            return null;
        }
    }

    @Override
    public void setValue(Object value) {
        setValueGeneric(value);
        if (isSetValueDone()) {
            return;
        }
        if (value == null){
            getTextField().setText("");
            return;
        }

        assert (value instanceof SBDuration);
        SBDuration durationValue = (SBDuration) value;

        // Get the corresponding constant if any
        for (Map.Entry<String, SBDuration> entry : constants.entrySet()) {
            if (value.equals(entry.getValue())) {
                getTextField().setText(entry.getKey());
                return;
            }
        }
        String units = (String) unitsComboBox.getSelectionModel().getSelectedItem();
        getTextField().setText(getNumericValue(durationValue, units).toString());
    }

    private Double getNumericValue(SBDuration durationValue, String units) {
        Double convertedValue = null;
        if (units.equals(MILLISECONDS)) {
            convertedValue = durationValue.toMillis();
        } else if (units.equals(SECONDS)) {
            convertedValue = durationValue.toSeconds();
        } else if (units.equals(MINUTES)) {
            convertedValue = durationValue.toMinutes();
        } else if (units.equals(HOURS)) {
            convertedValue = durationValue.toHours();
        }
        return convertedValue;
    }

    public void reset(ValuePropertyMetadata propMeta, Set<Class<?>> selectedClasses) {
        super.reset(propMeta, selectedClasses, new ArrayList<>(constants.keySet()));
    }

    @Override
    protected void valueIsIndeterminate() {
        handleIndeterminate(getTextField());
    }

    @Override
    public void requestFocus() {
        EditorUtils.doNextFrame(() -> getTextField().requestFocus());
    }

}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/job/gridpane/MoveColumnJob.java<|end_filename|>
/*
 * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.kit.editor.job.gridpane;

import com.oracle.javafx.scenebuilder.kit.editor.EditorController;
import com.oracle.javafx.scenebuilder.kit.editor.job.BatchSelectionJob;
import com.oracle.javafx.scenebuilder.kit.editor.job.Job;
import com.oracle.javafx.scenebuilder.kit.editor.job.gridpane.GridPaneJobUtils.Position;
import com.oracle.javafx.scenebuilder.kit.editor.job.atomic.AddPropertyValueJob;
import com.oracle.javafx.scenebuilder.kit.editor.job.atomic.RemoveObjectJob;
import com.oracle.javafx.scenebuilder.kit.editor.selection.AbstractSelectionGroup;
import com.oracle.javafx.scenebuilder.kit.editor.selection.GridSelectionGroup;
import com.oracle.javafx.scenebuilder.kit.editor.selection.Selection;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMDocument;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMInstance;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMObject;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMProperty;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMPropertyC;
import com.oracle.javafx.scenebuilder.kit.metadata.util.DesignHierarchyMask;
import com.oracle.javafx.scenebuilder.kit.metadata.util.PropertyName;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javafx.scene.layout.ColumnConstraints;

/**
 * Job invoked when moving columns BEFORE or AFTER.
 */
public class MoveColumnJob extends BatchSelectionJob {

    private FXOMObject targetGridPane;
    private final List<Integer> targetIndexes = new ArrayList<>();
    private final GridPaneJobUtils.Position position;

    public MoveColumnJob(final EditorController editorController, final Position position) {
        super(editorController);
        assert position == Position.BEFORE || position == Position.AFTER;
        this.position = position;
    }

    @Override
    protected List<Job> makeSubJobs() {

        final List<Job> result = new ArrayList<>();

        if (GridPaneJobUtils.canPerformMove(getEditorController(), position)) {

            // Retrieve the target GridPane
            final Selection selection = getEditorController().getSelection();
            final AbstractSelectionGroup asg = selection.getGroup();
            assert asg instanceof GridSelectionGroup; // Because of (1)
            final GridSelectionGroup gsg = (GridSelectionGroup) asg;

            targetGridPane = gsg.getParentObject();
            targetIndexes.addAll(gsg.getIndexes());

            // Add sub jobs
            // First move the column constraints
            result.addAll(moveColumnConstraints());
            // Then move the column content
            result.addAll(moveColumnContent());
        }
        return result;
    }

    @Override
    protected String makeDescription() {
        return "Move Column " + position.name(); //NOI18N
    }

    @Override
    protected AbstractSelectionGroup getNewSelectionGroup() {
        final Set<Integer> movedIndexes = new HashSet<>();
        for (int targetIndex : targetIndexes) {
            int movedIndex = position == Position.BEFORE
                    ? targetIndex - 1 : targetIndex + 1;
            movedIndexes.add(movedIndex);
        }
        return new GridSelectionGroup(targetGridPane, GridSelectionGroup.Type.COLUMN, movedIndexes);
    }

    private List<Job> moveColumnConstraints() {

        final List<Job> result = new ArrayList<>();

        // Retrieve the constraints property for the specified target GridPane
        final PropertyName propertyName = new PropertyName("columnConstraints"); //NOI18N
        assert targetGridPane instanceof FXOMInstance;
        FXOMProperty constraintsProperty
                = ((FXOMInstance) targetGridPane).getProperties().get(propertyName);
        // GridPane has no constraints property => no constraints to move
        if (constraintsProperty == null) {
            return result;
        }

        final DesignHierarchyMask mask = new DesignHierarchyMask(targetGridPane);
        for (int targetIndex : targetIndexes) {

            final int positionIndex;
            switch (position) {
                case BEFORE:
                    positionIndex = targetIndex - 1;
                    break;
                case AFTER:
                    positionIndex = targetIndex + 1;
                    break;
                default:
                    assert false;
                    return result;
            }

            // Retrieve the target constraints
            final FXOMObject targetConstraints
                    = mask.getColumnConstraintsAtIndex(targetIndex);

            // The target index is associated to an existing constraints value :
            // we remove the target constraints and add it back at new position
            // No need to move the constraints of the column before/after :
            // indeed, they are automatically shifted while updating the target ones 
            if (targetConstraints != null) {
                // First remove current target constraints
                final Job removeValueJob = new RemoveObjectJob(
                        targetConstraints,
                        getEditorController());
                result.add(removeValueJob);

                // Then add the target constraints at new positionIndex
                final Job addValueJob = new AddPropertyValueJob(
                        targetConstraints,
                        (FXOMPropertyC) constraintsProperty,
                        positionIndex, getEditorController());
                result.add(addValueJob);
            }//
            // The target index is not associated to an existing constraints value :
            // we may need to move the constraints before the target one if any
            else if (position == Position.BEFORE) {
                // Retrieve the constraints before the target one
                final FXOMObject beforeConstraints
                        = mask.getColumnConstraintsAtIndex(targetIndex - 1);

                // The index before is associated to an existing constraints value :
                // we insert a new constraints with default values at the position index
                if (beforeConstraints != null) {
                    // Create new empty constraints for the target column
                    final FXOMInstance addedConstraints = makeColumnConstraintsInstance();
                    final Job addValueJob = new AddPropertyValueJob(
                            addedConstraints,
                            (FXOMPropertyC) constraintsProperty,
                            positionIndex, getEditorController());
                    result.add(addValueJob);
                }
            }
        }
        return result;
    }

    private List<Job> moveColumnContent() {

        final List<Job> result = new ArrayList<>();

        for (int targetIndex : targetIndexes) {

            switch (position) {
                case BEFORE:
                    // First move the target column content
                    result.add(new ReIndexColumnContentJob(
                            getEditorController(),
                            -1, targetGridPane, targetIndex));
                    int beforeIndex = targetIndex - 1;
                    // Then move the content of the column before the target one
                    // If the index before is not part of the target indexes (selected indexes),
                    // we move the column content as many times as consecutive target indexes
                    if (targetIndexes.contains(beforeIndex) == false) {
                        int shiftIndex = 1;
                        while (targetIndexes.contains(targetIndex + shiftIndex)) {
                            shiftIndex++;
                        }
                        result.add(new ReIndexColumnContentJob(
                                getEditorController(),
                                shiftIndex, targetGridPane, beforeIndex));
                    }
                    break;
                case AFTER:
                    // First move the target column content
                    result.add(new ReIndexColumnContentJob(
                            getEditorController(),
                            +1, targetGridPane, targetIndex));
                    int afterIndex = targetIndex + 1;
                    // Then move the content of the column after the target one
                    // If the index after is not part of the target indexes (selected indexes),
                    // we move the column content as many times as consecutive target indexes
                    if (targetIndexes.contains(afterIndex) == false) {
                        int shiftIndex = -1;
                        while (targetIndexes.contains(targetIndex + shiftIndex)) {
                            shiftIndex--;
                        }
                        result.add(new ReIndexColumnContentJob(
                                getEditorController(),
                                shiftIndex, targetGridPane, afterIndex));
                    }
                    break;
                default:
                    assert false;
            }
        }
        return result;
    }

    private FXOMInstance makeColumnConstraintsInstance() {

        // Create new constraints instance
        final FXOMDocument newDocument = new FXOMDocument();
        final FXOMInstance result
                = new FXOMInstance(newDocument, ColumnConstraints.class);
        newDocument.setFxomRoot(result);
        result.moveToFxomDocument(getEditorController().getFxomDocument());

        return result;
    }
}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/content/guides/MovingGuideController.java<|end_filename|>
/*
 * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */

package com.oracle.javafx.scenebuilder.kit.editor.panel.content.guides;

import com.oracle.javafx.scenebuilder.kit.util.MathUtils;
import java.util.Collections;
import java.util.List;
import javafx.geometry.Bounds;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.paint.Paint;

/**
 *
 */
public class MovingGuideController {
    
    private final double MATCH_DISTANCE = 6.0;

    private final HorizontalLineIndex horizontalLineIndex = new HorizontalLineIndex();
    private final VerticalLineIndex verticalLineIndex = new VerticalLineIndex();
    private final MovingGuideRenderer renderer;
    private double suggestedDX;
    private double suggestedDY;
    
    public MovingGuideController(Paint chromeColor, Bounds scopeInScene) {
        this.renderer = new MovingGuideRenderer(chromeColor, scopeInScene);
    }
    
    public void addSampleBounds(Node node) {
        assert node != null;
        assert node.getScene() != null;
        
        final Bounds layoutBounds = node.getLayoutBounds();
        final Bounds boundsInScene = node.localToScene(layoutBounds, true /* rootScene */);
        addSampleBounds(boundsInScene, true /* addMiddle */);
    }
    
    public void addSampleBounds(Bounds boundsInScene, boolean addMiddle) {
        final double minX = boundsInScene.getMinX();
        final double minY = boundsInScene.getMinY();
        final double maxX = boundsInScene.getMaxX();
        final double maxY = boundsInScene.getMaxY();
        final double midX = (minX + maxX) / 2.0;
        final double midY = (minY + maxY) / 2.0;
        
        horizontalLineIndex.addLine(new HorizontalSegment(minX, maxX, minY));
        horizontalLineIndex.addLine(new HorizontalSegment(minX, maxX, maxY));
        verticalLineIndex.addLine(new VerticalSegment(minX, minY, maxY));
        verticalLineIndex.addLine(new VerticalSegment(maxX, minY, maxY));
        
        if (addMiddle) {
            horizontalLineIndex.addLine(new HorizontalSegment(minX, maxX, midY));
            verticalLineIndex.addLine(new VerticalSegment(midX, minY, maxY));
        }
    }
    
    public void clearSampleBounds() {
        horizontalLineIndex.clear();
        verticalLineIndex.clear();
        clear();
    }
    
    public boolean hasSampleBounds() {
        return (horizontalLineIndex.isEmpty() == false) || (verticalLineIndex.isEmpty() == false);
    }
    
    public void clear() {
        renderer.setLines(Collections.emptyList(), Collections.emptyList());
    }
    
    public void match(Bounds targetBounds) {
        List<HorizontalSegment> horizontalMatchingLines;
        List<VerticalSegment> verticalMatchingLines;
        boolean matchedHorizontally = false;
        boolean matchedVertically = false;
        
        // Match horizontal center line of targetBounds
        horizontalMatchingLines 
                = horizontalLineIndex.matchCenter(targetBounds, MATCH_DISTANCE);
        if (horizontalMatchingLines.isEmpty() == false) {
            matchedHorizontally = true;
            final HorizontalSegment line = horizontalMatchingLines.get(0);
            assert MathUtils.equals(line.getY1(), line.getY2());
            final double targetMinY = targetBounds.getMinY();
            final double targetMaxY = targetBounds.getMaxY();
            final double targetMidY = (targetMinY + targetMaxY) / 2.0;
            suggestedDY = line.getY1() - targetMidY;
        }
        
        // Match north boundary of targetBounds
        if (matchedHorizontally == false) {
            horizontalMatchingLines 
                    = horizontalLineIndex.matchNorth(targetBounds, MATCH_DISTANCE);
            if (horizontalMatchingLines.isEmpty() == false) {
                matchedHorizontally = true;
                final HorizontalSegment line = horizontalMatchingLines.get(0);
                assert MathUtils.equals(line.getY1(), line.getY2());
                suggestedDY = line.getY1() - targetBounds.getMinY();
            }
        }
        
        // Match south boundary of targetBounds
        if (matchedHorizontally == false) {
            horizontalMatchingLines 
                    = horizontalLineIndex.matchSouth(targetBounds, MATCH_DISTANCE);
            if (horizontalMatchingLines.isEmpty() == false) {
                matchedHorizontally = true;
                final HorizontalSegment line = horizontalMatchingLines.get(0);
                assert MathUtils.equals(line.getY1(), line.getY2());
                suggestedDY = line.getY1() - targetBounds.getMaxY();
            }
        }
        
        if (matchedHorizontally == false) {
            suggestedDY = 0.0;
        }
        
        // Match vertical center line of targetBounds
        verticalMatchingLines 
                = verticalLineIndex.matchCenter(targetBounds, MATCH_DISTANCE);
        if (verticalMatchingLines.isEmpty() == false) {
            matchedVertically = true;
            final VerticalSegment line = verticalMatchingLines.get(0);
            assert MathUtils.equals(line.getX1(), line.getX2());
            final double targetMinX = targetBounds.getMinX();
            final double targetMaxX = targetBounds.getMaxX();
            final double targetMidX = (targetMinX + targetMaxX) / 2.0;
            suggestedDX = line.getX1() - targetMidX;
        }
        
        // Match west boundary of targetBounds
        if (matchedVertically == false) {
            verticalMatchingLines 
                    = verticalLineIndex.matchWest(targetBounds, MATCH_DISTANCE);
            if (verticalMatchingLines.isEmpty() == false) {
                matchedVertically = true;
                final VerticalSegment line = verticalMatchingLines.get(0);
                assert MathUtils.equals(line.getX1(), line.getX2());
                suggestedDX = line.getX1() - targetBounds.getMinX();
            }
        }
        
        // Match east boundary of targetBounds
        if (matchedVertically == false) {
            verticalMatchingLines 
                    = verticalLineIndex.matchEast(targetBounds, MATCH_DISTANCE);
            if (verticalMatchingLines.isEmpty() == false) {
                matchedVertically = true;
                final VerticalSegment line = verticalMatchingLines.get(0);
                assert MathUtils.equals(line.getX1(), line.getX2());
                suggestedDX = line.getX1() - targetBounds.getMaxX();
            }
        }
        
        if (matchedVertically == false) {
            suggestedDX = 0.0;
        }
        
        renderer.setLines(horizontalMatchingLines, verticalMatchingLines);
    }
    
    
    public double getSuggestedDX() {
        return suggestedDX;
    }
    
    
    public double getSuggestedDY() {
        return suggestedDY;
    }
    
    public Group getGuideGroup() {
        return renderer.getGuideGroup();
    }
}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/content/driver/handles/TreeTableViewHandles.java<|end_filename|>
/*
 * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.kit.editor.panel.content.driver.handles;

import com.oracle.javafx.scenebuilder.kit.editor.panel.content.ContentPanelController;
import com.oracle.javafx.scenebuilder.kit.editor.panel.content.driver.TreeTableViewDesignInfoX;
import com.oracle.javafx.scenebuilder.kit.editor.panel.content.gesture.AbstractGesture;
import com.oracle.javafx.scenebuilder.kit.editor.panel.content.gesture.mouse.ResizeTreeTableColumnGesture;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMInstance;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMObject;
import com.oracle.javafx.scenebuilder.kit.metadata.util.DesignHierarchyMask;
import java.util.List;
import javafx.geometry.Bounds;
import javafx.geometry.Point2D;
import javafx.scene.Cursor;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.control.TreeTableColumn;
import javafx.scene.control.TreeTableView;
import javafx.scene.paint.Color;
import javafx.scene.shape.Line;

/**
 *
 * 
 */
public class TreeTableViewHandles extends AbstractNodeHandles<Node> {
    
    private final Group grips = new Group();
    
    public TreeTableViewHandles(ContentPanelController contentPanelController,
            FXOMInstance fxomInstance) {
        super(contentPanelController, fxomInstance, Node.class);
        assert fxomInstance.getSceneGraphObject() instanceof TreeTableView;
        
        getRootNode().getChildren().add(grips); // Above handles
    }
    
    public TreeTableView<?> getTreeTableView() {
        return (TreeTableView<?>) getSceneGraphObject();
    }
    
    /*
     * AbstractNodeHandles
     */
    @Override
    protected void layoutDecoration() {
        super.layoutDecoration();
             
        // Adjusts the number of grip lines to the number of dividers
        adjustGripCount();
        
        // Updates grip positions
        for (int i = 0, count = getTreeTableView().getColumns().size(); i < count; i++) {
            layoutGrip(i);
        }
    }

    @Override
    public AbstractGesture findGesture(Node node) {
        final AbstractGesture result;
        
        final int gripIndex = grips.getChildren().indexOf(node);
        if (gripIndex != -1) {
            final DesignHierarchyMask m = new DesignHierarchyMask(getFxomInstance());
            final FXOMObject columnObject = m.getSubComponentAtIndex(gripIndex);
            assert columnObject instanceof FXOMInstance;
            result = new ResizeTreeTableColumnGesture(getContentPanelController(), 
                    (FXOMInstance) columnObject);
        } else {
            result = super.findGesture(node);
        }
        
        return result;
    }

    
    /*
     * Private
     */
    
    private void adjustGripCount() {
        final int columnCount = getTreeTableView().getColumns().size();
        final List<Node> gripChildren = grips.getChildren();
        
        while (gripChildren.size() < columnCount) {
            gripChildren.add(makeGripLine());
        }
        while (gripChildren.size() > columnCount) {
            gripChildren.remove(gripChildren.size()-1);
        }
    }
    
    private Line makeGripLine() {
        final Line result = new Line();
        result.setStrokeWidth(SELECTION_HANDLES_SIZE);
        result.setStroke(Color.TRANSPARENT);
        result.setCursor(Cursor.H_RESIZE);
        attachHandles(result);
        return result;
    }
    
    private void layoutGrip(int gripIndex) {
        assert grips.getChildren().get(gripIndex) instanceof Line;
        
        final TreeTableColumn<?,?> tc = getTreeTableView().getColumns().get(gripIndex);
        
        if (tc.isVisible()) {
            final TreeTableViewDesignInfoX di = new TreeTableViewDesignInfoX();
            final Bounds b = di.getColumnHeaderBounds(tc);
            final double startX = b.getMaxX();
            final double startY = b.getMinY();
            final double endY = b.getMaxY();

            final boolean snapToPixel = true;
            final Point2D startPoint = sceneGraphObjectToDecoration(startX, startY, snapToPixel);
            final Point2D endPoint = sceneGraphObjectToDecoration(startX, endY, snapToPixel);

            final Line gripLine = (Line) grips.getChildren().get(gripIndex);
            gripLine.setVisible(true);
            gripLine.setManaged(true);
            gripLine.setStartX(startPoint.getX());
            gripLine.setStartY(startPoint.getY());
            gripLine.setEndX(endPoint.getX());
            gripLine.setEndY(endPoint.getY());
        } else {
            final Line gripLine = (Line) grips.getChildren().get(gripIndex);
            gripLine.setVisible(false);
            gripLine.setManaged(false);
        }
    }
    
    
    /* 
     * Wrapper to avoid the 'leaking this in constructor' warning emitted by NB.
     */
    private void attachHandles(Node node) {
        attachHandles(node, this);
    }
}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/content/guides/HorizontalLineIndex.java<|end_filename|>
/*
 * Copyright (c) 2018, Gluon and/or its affiliates.
 * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */

package com.oracle.javafx.scenebuilder.kit.editor.panel.content.guides;

import com.oracle.javafx.scenebuilder.kit.util.MathUtils;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javafx.geometry.Bounds;
import javafx.geometry.Point2D;

/**
 *
 */
class HorizontalLineIndex {
    
    private static final HorizontalLineComparator comparator = new HorizontalLineComparator();
    
    private final List<HorizontalSegment> lines = new ArrayList<>();
    private boolean sorted;
    

    public void addLine(HorizontalSegment s) {
        lines.add(s);
        sorted = false;
    }
    
    public void clear() {
        lines.clear();
    }
    
    public boolean isEmpty() {
        return lines.isEmpty();
    }
    
    public List<HorizontalSegment> matchNorth(Bounds boundsInScene, double threshold) {
        assert boundsInScene.isEmpty() == false;
        return matchY(boundsInScene.getMinY(), threshold);
    }
    
    public List<HorizontalSegment> matchSouth(Bounds boundsInScene, double threshold) {
        assert boundsInScene.isEmpty() == false;
        return matchY(boundsInScene.getMaxY(), threshold);
    }
    
    public List<HorizontalSegment> matchCenter(Bounds boundsInScene, double threshold) {
        assert boundsInScene.isEmpty() == false;
        final double minY = boundsInScene.getMinY();
        final double maxY = boundsInScene.getMaxY();
        return matchY((minY + maxY) / 2.0, threshold);
    }

    public List<HorizontalSegment> matchPoint(Point2D point, double threshold) {
        assert point != null;
        return matchXY(point.getX(), point.getY(), threshold);
    }
    
    /*
     * Private
     */
    
    private List<HorizontalSegment> matchY(double targetY, double threshold) {
        assert threshold >= 0;
        
        if (sorted == false) {
            Collections.sort(lines, comparator);
        }
        double bestDelta = Double.MAX_VALUE;
        final List<HorizontalSegment> result = new ArrayList<>();
        for (HorizontalSegment l : lines) {
            final double delta = Math.abs(l.getY1() - targetY);
            if (delta < threshold) {
                if (MathUtils.equals(delta, bestDelta)) {
                    result.add(l);
                } else if (delta < bestDelta) {
                    bestDelta = delta;
                    result.clear();
                    result.add(l);
                }
            }
        }
        
        return result;
    }

    private List<HorizontalSegment> matchXY(double targetX, double targetY, double threshold) {
        assert threshold >= 0;

        if (sorted == false) {
            Collections.sort(lines, comparator);
        }
        double bestDelta = Double.MAX_VALUE;
        final List<HorizontalSegment> result = new ArrayList<>();
        for (HorizontalSegment l : lines) {
            final double delta = Math.abs(l.getY1() - targetY);
            if (delta < threshold && targetX >= l.getX1() && targetX <= l.getX2()) {
                if (MathUtils.equals(delta, bestDelta)) {
                    result.add(l);
                } else if (delta < bestDelta) {
                    bestDelta = delta;
                    result.clear();
                    result.add(l);
                }
            }
        }

        return result;
    }
    
}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/job/InsertAsAccessoryJob.java<|end_filename|>
/*
 * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.kit.editor.job;

import com.oracle.javafx.scenebuilder.kit.editor.EditorController;
import com.oracle.javafx.scenebuilder.kit.editor.job.atomic.AddPropertyJob;
import com.oracle.javafx.scenebuilder.kit.editor.job.atomic.AddPropertyValueJob;
import com.oracle.javafx.scenebuilder.kit.editor.selection.AbstractSelectionGroup;
import com.oracle.javafx.scenebuilder.kit.editor.selection.ObjectSelectionGroup;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMCollection;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMDocument;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMInstance;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMObject;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMProperty;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMPropertyC;
import com.oracle.javafx.scenebuilder.kit.metadata.util.DesignHierarchyMask;
import com.oracle.javafx.scenebuilder.kit.metadata.util.DesignHierarchyMask.Accessory;
import com.oracle.javafx.scenebuilder.kit.metadata.util.PropertyName;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * Job used to insert new FXOM objects into an accessory location.
 *
 */
public class InsertAsAccessoryJob extends BatchSelectionJob {

    private final FXOMObject newObject;
    private final FXOMObject targetObject;
    private final Accessory accessory;

    public InsertAsAccessoryJob(
            FXOMObject newObject,
            FXOMObject targetObject,
            Accessory accessory,
            EditorController editorController) {
        super(editorController);

        assert newObject != null;
        assert targetObject != null;
        assert accessory != null;
        assert newObject.getFxomDocument() == getEditorController().getFxomDocument();
        assert targetObject.getFxomDocument() == getEditorController().getFxomDocument();

        this.newObject = newObject;
        this.targetObject = targetObject;
        this.accessory = accessory;
    }

    @Override
    protected List<Job> makeSubJobs() {
        final List<Job> result = new ArrayList<>();
        if (targetObject instanceof FXOMInstance) {

            final DesignHierarchyMask mask = new DesignHierarchyMask(targetObject);
            if (mask.isAcceptingAccessory(accessory, newObject)
                    && mask.getAccessory(accessory) == null) { // (1)

                final FXOMDocument fxomDocument = getEditorController().getFxomDocument();
                final FXOMInstance targetInstance = (FXOMInstance) targetObject;
                final PropertyName accessoryName = mask.getPropertyNameForAccessory(accessory);
                assert accessoryName != null;

                // Property has no value yet because of (1)
                FXOMProperty targetProperty = new FXOMPropertyC(fxomDocument, accessoryName);

                final Job addValueJob
                        = new AddPropertyValueJob(newObject,
                                (FXOMPropertyC) targetProperty,
                                -1,
                                getEditorController());
                result.add(addValueJob);

                if (targetProperty.getParentInstance() == null) {
                    assert targetObject instanceof FXOMInstance;
                    final Job addPropertyJob
                            = new AddPropertyJob(targetProperty, targetInstance,
                                    -1, getEditorController());
                    result.add(addPropertyJob);
                }

                final Job pruneJob = new PrunePropertiesJob(newObject, targetObject,
                        getEditorController());
                if (pruneJob.isExecutable()) {
                    result.add(0, pruneJob);
                }
            }
        }
        return result;
    }

    @Override
    protected String makeDescription() {
        final String result;
        final StringBuilder sb = new StringBuilder();

        sb.append("Insert ");

        if (newObject instanceof FXOMInstance) {
            final Object sceneGraphObject = newObject.getSceneGraphObject();
            if (sceneGraphObject != null) {
                sb.append(sceneGraphObject.getClass().getSimpleName());
            } else {
                sb.append("Unresolved Object");
            }
        } else if (newObject instanceof FXOMCollection) {
            sb.append("Collection");
        } else {
            assert false;
            sb.append(newObject.getClass().getSimpleName());
        }
        result = sb.toString();
        return result;
    }

    @Override
    protected AbstractSelectionGroup getNewSelectionGroup() {
        final Set<FXOMObject> newObjects = new HashSet<>();
        newObjects.add(newObject);
        return new ObjectSelectionGroup(newObjects, newObject, null);
    }
}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/content/HudWindowController.java<|end_filename|>
/*
 * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.kit.editor.panel.content;

import com.oracle.javafx.scenebuilder.kit.editor.panel.content.util.CardinalPoint;
import com.oracle.javafx.scenebuilder.kit.editor.panel.content.util.LineEquation;
import com.oracle.javafx.scenebuilder.kit.editor.panel.util.AbstractFxmlPopupController;
import java.util.List;
import javafx.fxml.FXML;
import javafx.geometry.BoundingBox;
import javafx.geometry.Bounds;
import javafx.geometry.Point2D;
import javafx.scene.control.Label;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.RowConstraints;
import javafx.stage.WindowEvent;

/**
 * @treatAsPrivate
 */
public class HudWindowController extends AbstractFxmlPopupController {
    
    @FXML private GridPane gridPane;
    @FXML private RowConstraints rowConstraint0; // assigned by text editing fxml
    
    private int rowCount;
    
    public HudWindowController() {
        super(HudWindowController.class.getResource("HudWindow.fxml"));
    }

    private CardinalPoint relativePosition = CardinalPoint.SE;

    public CardinalPoint getRelativePosition() {
        return relativePosition;
    }

    public void setRelativePosition(CardinalPoint relativePosition) {
        this.relativePosition = relativePosition;
    }

    public int getRowCount() {
        return rowCount;
    }

    public void setRowCount(int rowCount) {
        // We force fxml to load so that we can call reconfigureGridPane().
        getRoot();
        assert gridPane != null;
        
        this.rowCount = rowCount;
        reconfigureGridPane();
    }
    
    public void setNameAtRowIndex(String name, int rowIndex) {
        assert (0 <= rowIndex);
        assert (rowIndex < gridPane.getRowConstraints().size());
        
        final int nameChildIndex = rowIndex * 2;
        final Label nameLabel = (Label) gridPane.getChildren().get(nameChildIndex);
        nameLabel.setText(name);
    }
    
    public void setValueAtRowIndex(String value, int rowIndex) {
        assert (0 <= rowIndex);
        assert (rowIndex < gridPane.getRowConstraints().size());
        
        final int valueChildIndex = rowIndex * 2+1;
        final Label valueLabel = (Label) gridPane.getChildren().get(valueChildIndex);
        valueLabel.setText(value);
    }
    
    /*
     * AbstractFxmlPopupController
     */

    @Override
    protected void controllerDidLoadFxml() {
        assert gridPane != null;
        assert rowConstraint0 != null;
        
        getRoot().setMouseTransparent(true);
        
        // We start with no row so remove any row constraints put at design time.
        assert rowCount == 0;
        gridPane.getRowConstraints().clear();
        gridPane.getChildren().clear();
        reconfigureGridPane();
    }
    
    @Override
    protected void controllerDidCreatePopup() {
        
        // We must fully control the popup location and visibility
        getPopup().setAutoFix(false);
        getPopup().setAutoHide(false);
        
        // This must be false else the "ESC" key is consumed by the 
        // popup and cannot be received by the content panel gestures.
        getPopup().setConsumeAutoHidingEvents(false);
    }
    
    @Override
    protected void onHidden(WindowEvent event) {
    }

    @Override
    protected void anchorBoundsDidChange() {
        updatePopupLocation();
    }

    @Override
    protected void anchorTransformDidChange() {
        updatePopupLocation();
    }

    @Override
    protected void anchorXYDidChange() {
        updatePopupLocation();
    }

    @Override
    public void updatePopupLocation() {
        // At exit time, closeRequestHandler() is not always called.
        // So this method can be invoked after the anchor has been removed the
        // scene. This looks like a bug in FX...
        // Anway we protect ourself by checking.
        if (getAnchor() != null && getAnchor().getScene() != null) {
            final Point2D popupLocation = computePopupLocation();
            getPopup().setX(popupLocation.getX());
            getPopup().setY(popupLocation.getY());
        }
    }
    
    
    
    /*
     * Private
     */
    
    
    private Point2D computePopupLocation() {
        
        /*
         * 
         *       O-----+               O-----+               O-----+
         *       | NW  |               |  N  |               | NE  |
         *       +-----k               +--k--+               k-----+
         * 
         * 
         *                    o-----------o-----------o
         *                    |                       |
         *       O-----+      |                       |      O-----+
         *       |  W  k      o         anchor        o      k  E  |
         *       +-----+      |          node         |      +-----+
         *                    |                       |
         *                    o-----------o-----------o
         * 
         * 
         *       O-----k               O--k--+               O--k--+
         *       | SW  |               |  S  |               | SE  |
         *       +-----+               +-----+               +-----+
         */
        
        final Bounds anchorBounds = getAnchor().getLayoutBounds();
        final Bounds usefulBounds = clampBounds(anchorBounds, 1.0, 1.0);
        assert usefulBounds.getWidth() > 0.0;
        assert usefulBounds.getHeight() > 0.0;
        final Point2D p0 = relativePosition.getPosition(usefulBounds);
        final Point2D p1 = relativePosition.getOpposite().getPosition(usefulBounds);
        final Point2D sp0 = getAnchor().localToScreen(p0);
        final Point2D sp1 = getAnchor().localToScreen(p1);
        assert sp0 != null;
        assert sp1 != null;
        final LineEquation leq = new LineEquation(sp0, sp1);
        
        
        final Point2D k = leq.pointAtOffset(-30.0);
        
        final double ox, oy; // Point O on the diagram above
        final Bounds popupBounds = getRoot().getLayoutBounds();
        switch(relativePosition) {
            case N:
                ox = k.getX() - popupBounds.getWidth() / 2.0;
                oy = k.getY() - popupBounds.getHeight();
                break;
            case NE:
                ox = k.getX();
                oy = k.getY() - popupBounds.getHeight();
                break;
            case E:
                ox = k.getX();
                oy = k.getY() - popupBounds.getHeight() / 2.0;
                break;
            case SE:
                ox = k.getX();
                oy = k.getY();
                break;
            case S:
                ox = k.getX() - popupBounds.getWidth() / 2.0;
                oy = k.getY();
                break;
            case SW:
                ox = k.getX() - popupBounds.getWidth();
                oy = k.getY();
                break;
            case W:
                ox = k.getX() - popupBounds.getWidth();
                oy = k.getY() - popupBounds.getHeight() / 2.0;
                break;
            case NW:
                ox = k.getX() - popupBounds.getWidth();
                oy = k.getY() - popupBounds.getHeight();
                break;
            default:
                assert false : "unexpected cardinal point:" + this;
                ox = k.getX();
                oy = k.getY();
                break;
        }
        
        return new Point2D(ox, oy);
    }
    
    
    
    private void reconfigureGridPane() {
        
        final List<RowConstraints> rowConstraints = gridPane.getRowConstraints();
        if (rowCount < rowConstraints.size()) {
            // There's too many rows : let's remove some
            while (rowCount < rowConstraints.size()) {
                removeLastRow();
            }
        } else {
            // There's not enough rows : let's add some
            while (rowCount > rowConstraints.size()) {
                appendRow();
            }
        }
    }
    
    
    private void appendRow() {
        final int newRowIndex = gridPane.getRowConstraints().size();
        
        // Add an entry to gridPane.rowConstraints.
        // We clone rowConstraint0 and add it to gridPane.
        final RowConstraints rc = new RowConstraints();
        rc.setFillHeight(rowConstraint0.isFillHeight());
        rc.setMaxHeight(rowConstraint0.getMaxHeight());
        rc.setMinHeight(rowConstraint0.getMinHeight());
        rc.setPercentHeight(rowConstraint0.getPercentHeight());
        rc.setPrefHeight(rowConstraint0.getPrefHeight());
        rc.setValignment(rowConstraint0.getValignment());
        rc.setVgrow(rowConstraint0.getVgrow());
        gridPane.getRowConstraints().add(rc);
        
        // Add two Labels to gridPane.children
        final Label nameLabel = new Label();
        final Label valueLabel = new Label();
        nameLabel.getStyleClass().add("hud-property-label");
        valueLabel.getStyleClass().add("hud-value-label");
        gridPane.getChildren().add(nameLabel);
        gridPane.getChildren().add(valueLabel);
        GridPane.setRowIndex(nameLabel, newRowIndex);
        GridPane.setRowIndex(valueLabel, newRowIndex);
        GridPane.setColumnIndex(nameLabel, 0);
        GridPane.setColumnIndex(valueLabel, 1);
    }
    
    
    private void removeLastRow() {
        assert gridPane.getRowConstraints().size() >= 1;
        assert gridPane.getChildren().size() >= 2;
        
        final int lastRowIndex = gridPane.getRowConstraints().size()-1;
        gridPane.getRowConstraints().remove(lastRowIndex);
        gridPane.getChildren().remove(lastRowIndex * 2 + 1);
        gridPane.getChildren().remove(lastRowIndex * 2 + 0);
    }
    
    /**
     * Returns bounds whose width and height are at least the specified minima.
     * @param b bounds
     * @param minWidth minimum width
     * @param minHeight minimum height
     * @return b if its width/height are above minWidth/minHeight or new adjusted bounds
     */
    private static Bounds clampBounds(Bounds b, double minWidth, double minHeight) {
        assert b != null;
        assert minWidth > 0.0;
        assert minHeight > 0.0;
        
        final Bounds result;
        
        if ((b.getWidth() >= minWidth) && (b.getHeight() >= minHeight)) {
            // Fast track
            result = b;
        } else {
            final double minX, width;
            if (b.getWidth() >= minWidth) {
                minX = b.getMinX();
                width = b.getWidth();
            } else {
                minX = b.getMinX() - minWidth / 2.0;
                width = minWidth;
            }

            final double minY, height;
            if (b.getHeight() >= minHeight) {
                minY = b.getMinY();
                height = b.getHeight();
            } else {
                minY = b.getMinY() - minHeight / 2.0;
                height = minHeight;
            }
            
            result = new BoundingBox(minX, minY, width, height);
        }
        
        return result;
    }
}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/fxom/glue/XMLAttrComparator.java<|end_filename|>
/*
 * Copyright (c) 2018, Gluon and/or its affiliates.
 * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.kit.fxom.glue;

import java.util.Comparator;
import java.util.Map;

/**
 * A Comparator for comparing the indices of the attributes of an element.
 * These indices decides the location of each attribute in the FXML document.
 *
 * This class can be extended to create a customized comparator for each attribute.
 */
class XMLAttrComparator implements Comparator<Map.Entry<String,String>> {

    @Override
    public int compare(Map.Entry<String,String> attr1, Map.Entry<String,String> attr2) {
        assert attr1 != null;
        assert attr2 != null;
        
        final int aoi1 = getAttrOrderIndex(attr1);
        final int aoi2 = getAttrOrderIndex(attr2);
        final int result;
        
        if ((aoi1 == 2) && (aoi2 == 2)) {
            final QualifiedName qn1 = new QualifiedName(attr1.getKey());
            final QualifiedName qn2 = new QualifiedName(attr2.getKey());
            result = qn1.compareTo(qn2);
        } else {
            result = Integer.compare(aoi1, aoi2);
        }
        
        return result;
    }

    /**
     * Returns attribute index for a particular attribute
     * @param attr Attribute of an element whose index is to be found out
     * @return The index of the attribute in the FXML attribute list
     */
    protected int getAttrOrderIndex(Map.Entry<String,String> attr) {
        assert attr != null;
        
        /*
         * fx:id < id < other-attr < fx:controller
         */
        final int result;
        switch(attr.getKey()) {
            case "id": //NOI18N
                result = 0;
                break;
            case "fx:id": //NOI18N
                if (attr.getValue().startsWith("x")) { //NOI18N
                    // Auto-generated fx:id goes at the end
                    result = 10000;
                } else {
                    result = 1;
                }
                break;
            default:
                result = 2;
                break;
            case "fx:value": //NOI18N
                result = 3;
                break;
            case "fx:factory": //NOI18N
                result = 4;
                break;
            case "xmlns": //NOI18N
                result = 5;
                break;
            case "xmlns:fx": //NOI18N
                result = 5;
                break;
            case "fx:controller": //NOI18N
                result = 6;
                break;
        }
        
        return result;
    }
}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/drag/target/CollectionDropTarget.java<|end_filename|>
/*
 * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.kit.editor.drag.target;

import com.oracle.javafx.scenebuilder.kit.editor.EditorController;
import com.oracle.javafx.scenebuilder.kit.editor.drag.source.AbstractDragSource;
import com.oracle.javafx.scenebuilder.kit.editor.job.Job;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMCollection;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMObject;
import java.util.Objects;

/**
 *
 */
public class CollectionDropTarget extends AbstractDropTarget {
    
    private final FXOMCollection targetCollection;
    private final int targetIndex;

    public CollectionDropTarget(FXOMCollection targetCollection, int targetIndex) {
        this.targetCollection = targetCollection;
        this.targetIndex = targetIndex;
    }

    public FXOMCollection getTargetCollection() {
        return targetCollection;
    }

    public int getTargetIndex() {
        return targetIndex;
    }
    
    /*
     * AbstractDropTarget
     */
    @Override
    public FXOMObject getTargetObject() {
        return targetCollection;
    }

    @Override
    public boolean acceptDragSource(AbstractDragSource dragSource) {
        assert dragSource != null;
        
        // TODO(elp) : can we really put any kind of FXOMObject in a collection ?
        return dragSource.getDraggedObjects().isEmpty() == false;
    }

    @Override
    public Job makeDropJob(AbstractDragSource dragSource, EditorController editorController) {
        throw new UnsupportedOperationException("To be implemented"); //NOI18N
    }
    
    @Override
    public boolean isSelectRequiredAfterDrop() {
        return true;
    }
    
    /*
     * Object
     */
    @Override
    public int hashCode() {
        int hash = 5;
        hash = 37 * hash + Objects.hashCode(this.targetCollection);
        hash = 37 * hash + this.targetIndex;
        return hash;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final CollectionDropTarget other = (CollectionDropTarget) obj;
        if (!Objects.equals(this.targetCollection, other.targetCollection)) {
            return false;
        }
        if (this.targetIndex != other.targetIndex) {
            return false;
        }
        return true;
    }

    @Override
    public String toString() {
        return "CollectionDropTarget{" + "targetCollection=" + targetCollection + ", targetIndex=" + targetIndex + '}'; //NOI18N
    }
    
    
}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/inspector/editors/StyleClassEditor.java<|end_filename|>
/*
 * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.kit.editor.panel.inspector.editors;

import com.oracle.javafx.scenebuilder.kit.editor.EditorController;
import com.oracle.javafx.scenebuilder.kit.editor.EditorPlatform;
import com.oracle.javafx.scenebuilder.kit.editor.EditorPlatform.Theme;
import com.oracle.javafx.scenebuilder.kit.i18n.I18N;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMInstance;
import com.oracle.javafx.scenebuilder.kit.metadata.property.ValuePropertyMetadata;
import com.oracle.javafx.scenebuilder.kit.util.CssInternal;
import com.oracle.javafx.scenebuilder.kit.util.URLUtils;

import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javafx.beans.value.ChangeListener;
import javafx.collections.FXCollections;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.control.Button;
import javafx.scene.control.MenuButton;
import javafx.scene.control.MenuItem;
import javafx.scene.control.SeparatorMenuItem;
import javafx.scene.control.TextField;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.StackPane;

/**
 * Editor of the 'styleClass' property. It may contain several css classes, that
 * have their dedicated class (StyleClassItem).
 *
 *
 */
public class StyleClassEditor extends InlineListEditor {

    private Set<FXOMInstance> selectedInstances;
    private Map<String, String> cssClassesMap;
    private List<String> themeClasses;
    private EditorController editorController;

    public StyleClassEditor(ValuePropertyMetadata propMeta, Set<Class<?>> selectedClasses,
            Set<FXOMInstance> selectedInstances, EditorController editorController) {
        super(propMeta, selectedClasses);
        initialize(selectedInstances, editorController);
    }
    
    private void initialize(Set<FXOMInstance> selectedInstances, EditorController editorController) {
        this.selectedInstances = selectedInstances;
        this.editorController = editorController;
        setLayoutFormat(PropertyEditor.LayoutFormat.DOUBLE_LINE);
        themeClasses = CssInternal.getThemeStyleClasses(editorController.getTheme());
        addItem(getNewStyleClassItem());

        // On Theme change, update the themeClasses
        editorController.themeProperty().addListener((ChangeListener<Theme>) (ov, t, t1) -> themeClasses = CssInternal.getThemeStyleClasses(StyleClassEditor.this.editorController.getTheme()));
    }

    private StyleClassItem getNewStyleClassItem() {
        if (cssClassesMap == null) {
            cssClassesMap = CssInternal.getStyleClassesMap(editorController, selectedInstances);
            // We don't want the theme classes to be suggested: remove them from the list
            for (String themeClass : themeClasses) {
                cssClassesMap.remove(themeClass);
            }
        }
        return new StyleClassItem(this, cssClassesMap);
    }

    @Override
    public Object getValue() {
        List<String> value = FXCollections.observableArrayList();
        // Group all the item values in a list
        for (EditorItem styleItem : getEditorItems()) {
            String itemValue = EditorUtils.toString(styleItem.getValue());
            if (itemValue.isEmpty()) {
                continue;
            }
            value.add(itemValue);
        }
        if (value.isEmpty()) {
            // no style class
            return super.getPropertyMeta().getDefaultValueObject();
        } else {
            return value;
        }
    }

    @SuppressWarnings("unchecked")
    @Override
    public void setValue(Object value) {
        setValueGeneric(value);
        if (value == null) {
            reset();
            return;
        }
        assert value instanceof List;
        // Warning : value is the editing list.
        // We do not want to set the valueProperty() to editing list
        setValueGeneric(value);
        if (isSetValueDone()) {
            return;
        }

        Iterator<EditorItem> itemsIter = new ArrayList<>(getEditorItems()).iterator();
        for (String item : (List<String>) value) {
            item = item.trim();
            if (item.isEmpty()) {
                continue;
            }
            
            // We don't want to show the default theme classes
            // (e.g. combo-box, combo-box-base for ComboBox)
            Object defaultValue = getPropertyMeta().getDefaultValueObject();
            assert defaultValue instanceof List;
            List<String> defaultClasses = (List<String>) defaultValue;
            if (defaultClasses.contains(item)) {
                continue;
            }
            
            EditorItem editorItem;
            if (itemsIter.hasNext()) {
                // re-use the current items first
                editorItem = itemsIter.next();
            } else {
                // additional items required
                editorItem = addItem(getNewStyleClassItem());
            }
            editorItem.setValue(item);
        }
        // Empty the remaining items, if needed
        while (itemsIter.hasNext()) {
            EditorItem editorItem = itemsIter.next();
            removeItem(editorItem);
        }
    }

    public void reset(ValuePropertyMetadata propMeta, Set<Class<?>> selectedClasses,
            Set<FXOMInstance> selectedInstances, EditorController editorController) {
        super.reset(propMeta, selectedClasses);
        this.selectedInstances = selectedInstances;
        this.editorController = editorController;
        cssClassesMap = null;
        // add an empty item
        addItem(getNewStyleClassItem());
    }

    @Override
    public void requestFocus() {
        EditorItem firstItem = getEditorItems().get(0);
        assert firstItem instanceof StyleClassItem;
        ((StyleClassItem) firstItem).requestFocus();
    }

    /**
     ***************************************************************************
     *
     * StyleClass item : styleClass text fields, and +/action buttons.
     *
     ***************************************************************************
     */
    private class StyleClassItem extends AutoSuggestEditor implements EditorItem {

        @FXML
        private Button plusBt;
        @FXML
        private MenuButton actionMb;
        @FXML
        private MenuItem removeMi;
        @FXML
        private MenuItem moveUpMi;
        @FXML
        private MenuItem moveDownMi;
        @FXML
        private MenuItem openMi;
        @FXML
        private MenuItem revealMi;
        @FXML
        private StackPane styleClassSp;

        private Parent root;
        private TextField styleClassTf;
        private String currentValue;
        private Map<String, String> cssClassesMap;
        private EditorItemDelegate editor;

        public StyleClassItem(EditorItemDelegate editor, Map<String, String> cssClassesMap) {
//            System.out.println("New StyleClassItem.");
            // It is an AutoSuggestEditor without MenuButton
            super("", "", new ArrayList<>(cssClassesMap.keySet()), false); //NOI18N
            initialize(editor, cssClassesMap);
        }

        // Method to please FindBugs
        private void initialize(EditorItemDelegate editor, Map<String, String> cssClassesMap) {
            this.editor = editor;
            this.cssClassesMap = cssClassesMap;
            root = EditorUtils.loadFxml("StyleClassEditorItem.fxml", this);//NOI18N

            // Add the AutoSuggest text field in the scene graph
            styleClassSp.getChildren().add(super.getRoot());

            styleClassTf = super.getTextField();
            EventHandler<ActionEvent> onActionListener = event -> {
//                    System.out.println("StyleClassItem : onActionListener");
                if (getValue().equals(currentValue)) {
                    // no change
                    return;
                }
                if (styleClassTf.getText().isEmpty()) {
                    remove(null);
                }
//                        System.out.println("StyleEditorItem : COMMIT");
                editor.commit(StyleClassItem.this);
                if ((event != null) && event.getSource() instanceof TextField) {
                    ((TextField) event.getSource()).selectAll();
                }
                updateButtons();
                currentValue = EditorUtils.toString(getValue());
            };

            ChangeListener<String> textPropertyChange = (ov, prevText, newText) -> {
                if (prevText.isEmpty() || newText.isEmpty()) {
                    // Text changed FROM empty value, or TO empty value: buttons status change
                    updateButtons();
                }
            };
            styleClassTf.textProperty().addListener(textPropertyChange);
            updateButtons();

            setTextEditorBehavior(styleClassTf, onActionListener, false);
            ChangeListener<Boolean> focusListener = (observable, oldValue, newValue) -> {
                if (!newValue) {
                    // focus lost: commit
                    editor.editing(false, onActionListener);
                } else {
                    // got focus
                    editor.editing(true, onActionListener);
                }
            };
            styleClassTf.focusedProperty().addListener(focusListener);

            // Initialize menu items text
            removeMi.setText(I18N.getString("inspector.list.remove"));
            moveUpMi.setText(I18N.getString("inspector.list.moveup"));
            moveDownMi.setText(I18N.getString("inspector.list.movedown"));

            // Add suggested classes in the already existing action menu button, 
            // since we do not use the AutoSuggestEditor menu button for this editor.
            if (!cssClassesMap.isEmpty()) {
                actionMb.getItems().add(new SeparatorMenuItem());
            }
            for (String className : cssClassesMap.keySet()) {
                // css classes menu items
                MenuItem menuItem = new MenuItem(className);
                menuItem.setMnemonicParsing(false);
                menuItem.setOnAction(t -> {
                    styleClassTf.setText(className);
                    StyleClassItem.this.getCommitListener().handle(null);
                });
                actionMb.getItems().add(menuItem);
            }

        }

        @Override
        public final Node getNode() {
            return root;
        }

        @Override
        public Object getValue() {
            return EditorUtils.getPlainString(styleClassTf.getText()).trim();
        }

        @Override
        public void setValue(Object styleClass) {
            styleClassTf.setText(EditorUtils.toString(styleClass).trim());
            updateButtons();
            currentValue = EditorUtils.toString(getValue());
        }

        @Override
        public void reset() {
            styleClassTf.setText(""); //NOI18N
        }

        // Please findBugs
        @Override
        public void requestFocus() {
            super.requestFocus();
        }

        @Override
        public void setValueAsIndeterminate() {
            handleIndeterminate(styleClassTf);
        }

        @Override
        public MenuItem getMoveUpMenuItem() {
            return moveUpMi;
        }

        @Override
        public MenuItem getMoveDownMenuItem() {
            return moveDownMi;
        }

        @Override
        public MenuItem getRemoveMenuItem() {
            return removeMi;
        }

        @Override
        public Button getPlusButton() {
            return plusBt;
        }

        @Override
        public Button getMinusButton() {
            // not used here
            return null;
        }

        @FXML
        void add(ActionEvent event) {
            StyleClassEditor.StyleClassItem styleClassItem = getNewStyleClassItem();
            editor.add(this, styleClassItem);
            styleClassItem.requestFocus();

        }

        @FXML
        void remove(ActionEvent event) {
            editor.remove(this);
        }

        @FXML
        void up(ActionEvent event) {
            editor.up(this);
        }

        @FXML
        void down(ActionEvent event) {
            editor.down(this);
        }

        @FXML
        void plusBtTyped(KeyEvent event) {
            if (event.getCode() == KeyCode.ENTER) {
                editor.add(this, getNewStyleClassItem());
            }
        }

        @FXML
        void open(ActionEvent event) {
            String urlStr = cssClassesMap.get(getValue());
            if (urlStr == null) {
                return;
            }
            try {
                EditorPlatform.open(urlStr);
            } catch (IOException ex) {
                editorController.getMessageLog().logWarningMessage(
                        "inspector.stylesheet.cannotopen", urlStr); //NOI18N
            }
        }

        @FXML
        void reveal(ActionEvent event) {
            String urlStr = cssClassesMap.get(getValue());
            if (urlStr == null) {
                return;
            }
            try {
                File file = URLUtils.getFile(urlStr);
                if (file == null) { // urlStr is not a file URL
                    return;
                }
                EditorPlatform.revealInFileBrowser(file);
            } catch (URISyntaxException | IOException ex) {
                editorController.getMessageLog().logWarningMessage(
                        "inspector.stylesheet.cannotreveal", urlStr); //NOI18N
            }
        }

        private void updateButtons() {
            if (styleClassTf.getText().isEmpty()) {
                // if no content, disable plus
                plusBt.setDisable(true);
                removeMi.setDisable(false);
            } else {
                // enable plus and minus
                plusBt.setDisable(false);
                removeMi.setDisable(false);
            }
            // set text of open / reveal menu items
            String stylesheetUrl = cssClassesMap.get(getValue());
            if (stylesheetUrl == null) {
                // className is unknown: open / reveal should not be visible
                openMi.setVisible(false);
                revealMi.setVisible(false);
            } else {
                openMi.setVisible(true);
                revealMi.setVisible(true);
                String stylesheet = EditorUtils.getSimpleFileName(stylesheetUrl);
                openMi.setText(I18N.getString("inspector.list.open", stylesheet));
                if (EditorPlatform.IS_MAC) {
                    revealMi.setText(I18N.getString("inspector.list.reveal.finder", stylesheet));
                } else {
                    revealMi.setText(I18N.getString("inspector.list.reveal.explorer", stylesheet));
                }
            }
        }

        @SuppressWarnings("unused")
        protected void disablePlusButton(boolean disable) {
            plusBt.setDisable(disable);
        }

        @SuppressWarnings("unused")
        protected void disableRemove(boolean disable) {
            removeMi.setDisable(disable);
        }
    }
}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/job/wrap/WrapInSplitPaneJob.java<|end_filename|>
/*
 * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.kit.editor.job.wrap;

import com.oracle.javafx.scenebuilder.kit.editor.EditorController;
import com.oracle.javafx.scenebuilder.kit.editor.job.JobUtils;
import com.oracle.javafx.scenebuilder.kit.editor.job.wrap.FXOMObjectCourseComparator.BidimensionalComparator;
import com.oracle.javafx.scenebuilder.kit.editor.job.wrap.FXOMObjectCourseComparator.GridCourse;
import com.oracle.javafx.scenebuilder.kit.editor.job.wrap.FXOMObjectCourseComparator.UnidimensionalComparator;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMObject;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import javafx.geometry.Bounds;
import javafx.geometry.Orientation;
import javafx.scene.Node;
import javafx.scene.control.SplitPane;

/**
 * Job used to wrap selection in a SplitPane.
 */
public class WrapInSplitPaneJob extends AbstractWrapInSubComponentJob {

    public WrapInSplitPaneJob(EditorController editorController) {
        super(editorController);
        newContainerClass = SplitPane.class;
    }

    @Override
    protected void modifyNewContainer(final List<FXOMObject> children) {
        super.modifyNewContainer(children);

        // Update the SplitPane orientation depending on its children positionning
        final Orientation orientation = getOrientation(children);
        JobUtils.setOrientation(newContainer, SplitPane.class, orientation.name());
    }
    
    @Override
    protected Collection<FXOMObject> sortChildren(List<FXOMObject> children) {
        final List<FXOMObject> sorted = new ArrayList<>(children);
        final Orientation orientation = getOrientation(children);
        Collections.sort(sorted, UnidimensionalComparator.of(orientation));
        return sorted;
    }

    private Orientation getOrientation(final List<FXOMObject> fxomObjects) {
        int cols = computeSizeByCourse(fxomObjects, GridCourse.COL_BY_COL);
        if (cols == fxomObjects.size()) {
            return Orientation.HORIZONTAL;
        }
        int rows = computeSizeByCourse(fxomObjects, GridCourse.ROW_BY_ROW);
        if (rows == fxomObjects.size()) {
            return Orientation.VERTICAL;
        }
        final Orientation orientation = cols >= rows
                ? Orientation.HORIZONTAL : Orientation.VERTICAL;
        return orientation;
    }

    private int computeSizeByCourse(
            final List<FXOMObject> fxomObjects,
            final GridCourse course) {

        final BidimensionalComparator comparator = new BidimensionalComparator(course);
        FXOMObject lastObject = null;
        int rc = 0;
        int max = -1;
        for (FXOMObject currentObject : fxomObjects) {
            if (lastObject != null) {
                if (comparator.compare(lastObject, currentObject) != 0) {
                    final Node lastNode = (Node) lastObject.getSceneGraphObject();
                    final Node currentNode = (Node) currentObject.getSceneGraphObject();
                    final Bounds lastBounds = lastNode.getBoundsInParent();
                    final Bounds currentBounds = currentNode.getBoundsInParent();
                    if (course.getMinY(currentBounds) >= course.getMaxY(lastBounds)) {
                        rc++;
                    }
                }
            }
            max = Math.max(max, rc);
            lastObject = currentObject;
        }
        return max;
    }
}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/fxom/FXOMRefresher.java<|end_filename|>
/*
 * Copyright (c) 2019, Gluon and/or its affiliates.
 * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.kit.fxom;

import com.oracle.javafx.scenebuilder.kit.metadata.Metadata;
import com.oracle.javafx.scenebuilder.kit.metadata.property.ValuePropertyMetadata;
import com.oracle.javafx.scenebuilder.kit.metadata.property.value.DoubleArrayPropertyMetadata;
import com.oracle.javafx.scenebuilder.kit.metadata.property.value.list.ListValuePropertyMetadata;
import com.oracle.javafx.scenebuilder.kit.metadata.util.PropertyName;

import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.SplitPane;
import javafx.stage.Window;

import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;
import java.util.Set;

/**
 *
 *
 */
class FXOMRefresher {

    public void refresh(FXOMDocument document) {
        String fxmlText = null;
        try {
            fxmlText = document.getFxmlText(false);
            final FXOMDocument newDocument
                    = new FXOMDocument(fxmlText,
                    document.getLocation(),
                    document.getClassLoader(),
                    document.getResources(),
                    false /* normalized */);
            final TransientStateBackup backup = new TransientStateBackup(document);
            // if the refresh should not take place (e.g. due to an error), remove a property from intrinsic
            if (newDocument.getSceneGraphRoot() == null && newDocument.getFxomRoot() == null) {
                removeIntrinsicProperty(document);
            } else {
                refreshDocument(document, newDocument);
            }
            backup.restore();
            synchronizeDividerPositions(document);
        } catch (RuntimeException | IOException x) {
            final StringBuilder sb = new StringBuilder();
            sb.append("Bug in ");
            sb.append(getClass().getSimpleName());
            if (fxmlText != null) {
                try {
                    final File fxmlFile = File.createTempFile("DTL-5996-", ".fxml");
                    try (PrintWriter pw = new PrintWriter(fxmlFile, "UTF-8")) {
                        pw.write(fxmlText);
                        sb.append(": FXML dumped in ");
                        sb.append(fxmlFile.getPath());
                    }
                } catch (IOException xx) {
                    sb.append(": no FXML dumped");
                }
            } else {
                sb.append(": no FXML dumped");
            }
            throw new IllegalStateException(sb.toString(), x);
        }
    }

    private void removeIntrinsicProperty(FXOMDocument document) {
        FXOMInstance fxomRoot = (FXOMInstance) document.getFxomRoot();
        if (fxomRoot != null) {
            FXOMPropertyC propertyC = (FXOMPropertyC) fxomRoot.getProperties().get(new PropertyName("children"));
            if (propertyC.getValues().get(0) instanceof FXOMIntrinsic) {
                FXOMIntrinsic fxomIntrinsic = (FXOMIntrinsic) propertyC.getValues().get(0);
                fxomIntrinsic.removeCharsetProperty();
            }
        }
    }

    /*
     * Private (stylesheet)
     */

    private void refreshDocument(FXOMDocument currentDocument, FXOMDocument newDocument) {
        // Transfers scene graph object from newDocument to currentDocument
        currentDocument.setSceneGraphRoot(newDocument.getSceneGraphRoot());
        // Transfers display node from newDocument to currentDocument
        currentDocument.setDisplayNode(newDocument.getDisplayNode());
        // Transfers display stylesheets from newDocument to currentDocument
        currentDocument.setDisplayStylesheets(newDocument.getDisplayStylesheets());
        // Simulates Scene's behavior : automatically adds "root" styleclass if
        // if the scene graph root is a Parent instance or wraps a Parent instance
        if (currentDocument.getSceneGraphRoot() instanceof Parent) {
            final Parent rootParent = (Parent) currentDocument.getSceneGraphRoot();
            rootParent.getStyleClass().add(0, "root");
        } else if (currentDocument.getSceneGraphRoot() instanceof Scene
                || currentDocument.getSceneGraphRoot() instanceof Window) {
            Node displayNode = currentDocument.getDisplayNode();
            if (displayNode != null && displayNode instanceof Parent) {
                displayNode.getStyleClass().add(0, "root");
            }
        }
        // Recurses
        if (currentDocument.getFxomRoot() != null) {
            refreshFxomObject(currentDocument.getFxomRoot(), newDocument.getFxomRoot());
        }
    }


    private void refreshFxomObject(FXOMObject currentObject, FXOMObject newObject) {
        assert currentObject != null;
        assert newObject != null;
        assert currentObject.getClass() == newObject.getClass();
        currentObject.setSceneGraphObject(newObject.getSceneGraphObject());
        if (currentObject instanceof FXOMInstance) {
            refreshFxomInstance((FXOMInstance) currentObject, (FXOMInstance) newObject);
        } else if (currentObject instanceof FXOMCollection) {
            refreshFxomCollection((FXOMCollection) currentObject, (FXOMCollection) newObject);
        } else if (currentObject instanceof FXOMIntrinsic) {
            refreshFxomIntrinsic((FXOMIntrinsic) currentObject, (FXOMIntrinsic) newObject);
        } else {
            assert false : "Unexpected fxom object " + currentObject;
        }

//        assert currentObject.equals(newObject) : "currentValue=" + currentObject +
//                                               "  newValue=" + newObject;
    }


    private void refreshFxomInstance(FXOMInstance currentInstance, FXOMInstance newInstance) {
        assert currentInstance != null;
        assert newInstance != null;
        assert currentInstance.getClass() == newInstance.getClass();
        currentInstance.setDeclaredClass(newInstance.getDeclaredClass());
        final Set<PropertyName> currentNames = currentInstance.getProperties().keySet();
        final Set<PropertyName> newNames = newInstance.getProperties().keySet();
        assert currentNames.equals(newNames);
        for (PropertyName name : currentNames) {
            final FXOMProperty currentProperty = currentInstance.getProperties().get(name);
            final FXOMProperty newProperty = newInstance.getProperties().get(name);
            refreshFxomProperty(currentProperty, newProperty);
        }
    }

    private void refreshFxomCollection(FXOMCollection currentCollection, FXOMCollection newCollection) {
        assert currentCollection != null;
        assert newCollection != null;
        currentCollection.setDeclaredClass(newCollection.getDeclaredClass());
        refreshFxomObjects(currentCollection.getItems(), newCollection.getItems());
    }

    private void refreshFxomIntrinsic(FXOMIntrinsic currentIntrinsic, FXOMIntrinsic newIntrinsic) {
        assert currentIntrinsic != null;
        assert newIntrinsic != null;
        currentIntrinsic.setSourceSceneGraphObject(newIntrinsic.getSourceSceneGraphObject());
        currentIntrinsic.getProperties().clear();
        currentIntrinsic.fillProperties(newIntrinsic.getProperties());
    }

    private void refreshFxomProperty(FXOMProperty currentProperty, FXOMProperty newProperty) {
        assert currentProperty != null;
        assert newProperty != null;
        assert currentProperty.getName().equals(newProperty.getName());
        if (currentProperty instanceof FXOMPropertyT) {
            assert newProperty instanceof FXOMPropertyT;
            assert ((FXOMPropertyT) currentProperty).getValue().equals(((FXOMPropertyT) newProperty).getValue());
        } else {
            assert currentProperty instanceof FXOMPropertyC;
            assert newProperty instanceof FXOMPropertyC;
            final FXOMPropertyC currentPC = (FXOMPropertyC) currentProperty;
            final FXOMPropertyC newPC = (FXOMPropertyC) newProperty;
            refreshFxomObjects(currentPC.getValues(), newPC.getValues());
        }
    }


    private void refreshFxomObjects(List<FXOMObject> currentObjects, List<FXOMObject> newObjects) {
        assert currentObjects != null;
        assert newObjects != null;
        assert currentObjects.size() == newObjects.size();
        for (int i = 0, count = currentObjects.size(); i < count; i++) {
            final FXOMObject currentObject = currentObjects.get(i);
            final FXOMObject newObject = newObjects.get(i);
            if (currentObject instanceof FXOMIntrinsic || newObject instanceof FXOMIntrinsic) {
                handleRefreshIntrinsic(currentObject, newObject);
            } else {
                refreshFxomObject(currentObject, newObject);
            }
        }
    }

    private void handleRefreshIntrinsic(FXOMObject currentObject, FXOMObject newObject) {
        if (currentObject instanceof FXOMIntrinsic && newObject instanceof FXOMIntrinsic) {
            refreshFxomObject(currentObject, newObject);
        } else if (newObject instanceof FXOMIntrinsic) {
            FXOMInstance fxomInstance = getFxomInstance((FXOMIntrinsic) newObject);
            refreshFxomObject(currentObject, fxomInstance);
        } else if (currentObject instanceof FXOMIntrinsic) {
            FXOMInstance fxomInstance = getFxomInstance((FXOMIntrinsic) currentObject);
            refreshFxomObject(fxomInstance, newObject);
        }
    }

    private FXOMInstance getFxomInstance(FXOMIntrinsic intrinsic) {
        FXOMInstance fxomInstance = new FXOMInstance(intrinsic.getFxomDocument(), intrinsic.getGlueElement());
        fxomInstance.setSceneGraphObject(intrinsic.getSourceSceneGraphObject());
        fxomInstance.setDeclaredClass(intrinsic.getClass());
        if (!intrinsic.getProperties().isEmpty()) {
            fxomInstance.fillProperties(intrinsic.getProperties());
        }
        return fxomInstance;
    }
    
    /*
     * The case of SplitPane.dividerPositions property
     * -----------------------------------------------
     * 
     * When user adds a child to a SplitPane, this adds a new entry in
     * SplitPane.children property but also adds a new value to 
     * SplitPane.dividerPositions by side-effect.
     * 
     * The change in SplitPane.dividerPositions is performed at scene graph
     * level by FX. Thus it is unseen by FXOM. 
     * 
     * So in that case we perform a special operation which copies value of 
     * SplitPane.dividerPositions into FXOMProperty representing 
     * dividerPositions in FXOM.
     */

    private void synchronizeDividerPositions(FXOMDocument document) {
        final FXOMObject fxomRoot = document.getFxomRoot();
        if (fxomRoot != null) {
            final Metadata metadata
                    = Metadata.getMetadata();
            final PropertyName dividerPositionsName
                    = new PropertyName("dividerPositions");
            final List<FXOMObject> candidates
                    = fxomRoot.collectObjectWithSceneGraphObjectClass(SplitPane.class);

            for (FXOMObject fxomObject : candidates) {
                if (fxomObject instanceof FXOMInstance) {
                    final FXOMInstance fxomInstance = (FXOMInstance) fxomObject;
                    assert fxomInstance.getSceneGraphObject() instanceof SplitPane;
                    final SplitPane splitPane
                            = (SplitPane) fxomInstance.getSceneGraphObject();
                    splitPane.layout();
                    final ValuePropertyMetadata vpm
                            = metadata.queryValueProperty(fxomInstance, dividerPositionsName);
                    assert vpm instanceof ListValuePropertyMetadata
                            : "vpm.getClass()=" + vpm.getClass().getSimpleName();
                    final DoubleArrayPropertyMetadata davpm
                            = (DoubleArrayPropertyMetadata) vpm;
                    davpm.synchronizeWithSceneGraphObject(fxomInstance);
                }
            }
        }
    }


//    
//    
//    private void reloadStylesheets(final Parent p) {
//        assert p != null;
//        assert p.getScene() != null;
//        
//        if (p.getStylesheets().isEmpty() == false) {
//            final List<String> stylesheets = new ArrayList<>();
//            stylesheets.addAll(p.getStylesheets());
////            p.getStylesheets().clear();
////            p.impl_processCSS(true);
//            p.getStylesheets().setAll(stylesheets);
////            p.impl_processCSS(true);
//        }
//        for (Node child : p.getChildrenUnmodifiable()) {
//            if (child instanceof Parent) {
//                reloadStylesheets((Parent)child);
//            }
//        }
//        
//    }
}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/content/driver/ToolBarDriver.java<|end_filename|>
/*
 * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.kit.editor.panel.content.driver;

import com.oracle.javafx.scenebuilder.kit.editor.drag.target.AbstractDropTarget;
import com.oracle.javafx.scenebuilder.kit.editor.drag.target.ContainerZDropTarget;
import com.oracle.javafx.scenebuilder.kit.editor.panel.content.ContentPanelController;
import com.oracle.javafx.scenebuilder.kit.editor.panel.content.driver.tring.AbstractTring;
import com.oracle.javafx.scenebuilder.kit.editor.panel.content.driver.tring.GenericParentTring;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMInstance;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMObject;
import com.oracle.javafx.scenebuilder.kit.metadata.util.DesignHierarchyMask;
import javafx.scene.control.ToolBar;

/**
 *
 */
public class ToolBarDriver extends AbstractNodeDriver {

    public ToolBarDriver(ContentPanelController contentPanelController) {
        super(contentPanelController);
    }

    /*
     * AbstractDriver
     */
    @Override
    public AbstractDropTarget makeDropTarget(FXOMObject fxomObject, double sceneX, double sceneY) {
        
        assert fxomObject instanceof FXOMInstance;
        assert fxomObject.getSceneGraphObject() instanceof ToolBar;
        
        final int targetIndex = GenericParentTring.lookupCrackIndex(fxomObject, sceneX, sceneY);
        
        final FXOMObject beforeChild;
        if (targetIndex == -1) {
            beforeChild = null;
        } else {
            final DesignHierarchyMask m = new DesignHierarchyMask(fxomObject);
            if (targetIndex < m.getSubComponentCount()) {
                beforeChild = m.getSubComponentAtIndex(targetIndex);
            } else {
                beforeChild = null;
            }
        }
        
        return new ContainerZDropTarget((FXOMInstance)fxomObject, beforeChild);
    }
    
    
    @Override
    public AbstractTring<?> makeTring(AbstractDropTarget dropTarget) {
        assert dropTarget instanceof ContainerZDropTarget; 
        assert dropTarget.getTargetObject() instanceof FXOMInstance;
        assert dropTarget.getTargetObject().getSceneGraphObject() instanceof ToolBar;
        
        final ContainerZDropTarget zDropTarget = (ContainerZDropTarget) dropTarget;
        final int targetIndex;
        if (zDropTarget.getBeforeChild() == null) {
            targetIndex = -1;
        } else {
            targetIndex = zDropTarget.getBeforeChild().getIndexInParentProperty();
        }
        return new GenericParentTring(contentPanelController, 
                (FXOMInstance) dropTarget.getTargetObject(),
                targetIndex);
    }
}


<|start_filename|>app/src/main/java/com/oracle/javafx/scenebuilder/app/DocumentWatchingController.java<|end_filename|>
/*
 * Copyright (c) 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */

package com.oracle.javafx.scenebuilder.app;

import com.oracle.javafx.scenebuilder.app.i18n.I18N;
import com.oracle.javafx.scenebuilder.kit.editor.EditorController;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMDocument;
import com.oracle.javafx.scenebuilder.kit.util.FileWatcher;

import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;

import javafx.beans.value.ChangeListener;
import javafx.collections.ObservableList;

/**
 *
 */
public class DocumentWatchingController implements FileWatcher.Delegate {
    
    private final DocumentWindowController documentWindowController;
    private final EditorController editorController;
    private final ResourceController resourceController;
    private final SceneStyleSheetMenuController sceneStyleSheetMenuController;
    private final FileWatcher fileWatcher 
            = new FileWatcher(2000 /* ms */, this, DocumentWindowController.class.getSimpleName());
    
    
    public DocumentWatchingController(DocumentWindowController documentWindowController) {
        this.documentWindowController = documentWindowController;
        this.editorController = documentWindowController.getEditorController();
        this.resourceController = documentWindowController.getResourceController();
        this.sceneStyleSheetMenuController = documentWindowController.getSceneStyleSheetMenuController();
        
        this.editorController.sceneStyleSheetProperty().addListener(
                (ChangeListener<ObservableList<File>>) (ov, t,
                        t1) -> update());
    }
    
    public void start() {
        fileWatcher.start();
    }

    
    public void stop() {
        fileWatcher.stop();
    }
    
    
    public void update() {
        /*
         * The file watcher associated to this document window controller watches:
         *  1) the file holding the FXML document (if any)
         *  2) the resource file set in the Preview menu (if any)
         *  3) the style sheets files set in the Preview menu (if any)
         */
        
        final List<Path> targets = new ArrayList<>();
        
        // 1)
        final FXOMDocument fxomDocument = editorController.getFxomDocument();
        if ((fxomDocument != null) && (fxomDocument.getLocation() != null)) {
            try {
                final File fxmlFile = new File(fxomDocument.getLocation().toURI());
                targets.add(fxmlFile.toPath());
            } catch(URISyntaxException x) {
                throw new IllegalStateException("Bug", x); //NOI18N
            }
        }
        
        // 2)
        if (resourceController.getResourceFile() != null) {
            targets.add(resourceController.getResourceFile().toPath());
        }
        
        // 3)
        if (editorController.getSceneStyleSheets() != null) {
            for (File sceneStyleSheet : editorController.getSceneStyleSheets()) {
                targets.add(sceneStyleSheet.toPath());
            }
        }
        
        fileWatcher.setTargets(targets);
    }
    
    public void removeDocumentTarget() {
        final FXOMDocument fxomDocument = editorController.getFxomDocument();
        assert fxomDocument != null;
        assert fxomDocument.getLocation() != null;
        
        try {
            final File fxmlFile = new File(fxomDocument.getLocation().toURI());
            assert fileWatcher.getTargets().contains(fxmlFile.toPath());
            fileWatcher.removeTarget(fxmlFile.toPath());
        } catch(URISyntaxException x) {
            throw new IllegalStateException("Bug", x); //NOI18N
        }
    }
    
    /*
     * FileWatcher.Delegate
     */
    
    @Override
    public void fileWatcherDidWatchTargetCreation(Path target) {
        // Ignored
    }

    @Override
    public void fileWatcherDidWatchTargetDeletion(Path target) {
        if (isPathMatchingResourceLocation(target)) {
            // Resource file has disappeared
            resourceController.performRemoveResource(); 
            // Call above has invoked
            //      - FXOMDocument.refreshSceneGraph()
            //      - DocumentWatchingController.update()
            editorController.getMessageLog().logInfoMessage("log.info.file.deleted", 
                    I18N.getBundle(), target.getFileName());
        } else if (isPathMatchingSceneStyleSheet(target)) {
            sceneStyleSheetMenuController.performRemoveSceneStyleSheet(target.toFile());
            // Call above has invoked
            //      - FXOMDocument.reapplyCSS()
            //      - DocumentWatchingController.update()
            editorController.getMessageLog().logInfoMessage("log.info.file.deleted", 
                    I18N.getBundle(), target.getFileName());
        }
        /* 
         * Else it's the document file which has disappeared : 
         * We ignore this event : file will be recreated when user runs
         * the save command.
         */
    }

    @Override
    public void fileWatcherDidWatchTargetModification(Path target) {
        if (isPathMatchingResourceLocation(target)) {
            // Resource file has been modified -> refresh the scene graph
            resourceController.performReloadResource(); 
            // Call above has invoked FXOMDocument.refreshSceneGraph()
            editorController.getMessageLog().logInfoMessage("log.info.reload", 
                    I18N.getBundle(), target.getFileName());
            
        } else if (isPathMatchingDocumentLocation(target)) {
            if (documentWindowController.isDocumentDirty() == false) {
                // Try to reload the fxml text on disk
                try {
                    documentWindowController.reload();
                    editorController.getMessageLog().logInfoMessage("log.info.reload", 
                            I18N.getBundle(), target.getFileName());
                } catch(IOException x) {
                    // Here we silently ignore the failure :
                    // loadFromFile() has failed but left the document unchanged.
                }
            }
        } else if (isPathMatchingSceneStyleSheet(target)) {
            final FXOMDocument fxomDocument = editorController.getFxomDocument();
            if (fxomDocument != null) {
                fxomDocument.reapplyCSS(target);
                editorController.getMessageLog().logInfoMessage("log.info.reload", 
                        I18N.getBundle(), target.getFileName());
            }
        }
    }
    
    
    /*
     * Private
     */
    
    private boolean isPathMatchingDocumentLocation(Path p) {
        final boolean result;
        
        final FXOMDocument fxomDocument = editorController.getFxomDocument();
        if ((fxomDocument != null) && (fxomDocument.getLocation() != null)) {
            try {
                final File fxmlFile = new File(fxomDocument.getLocation().toURI());
                result = p.equals(fxmlFile.toPath());
            } catch(URISyntaxException x) {
                throw new IllegalStateException("Bug", x); //NOI18N
            }
        } else {
            result = false;
        }
        
        return result;
    }
    
    private boolean isPathMatchingResourceLocation(Path p) {
        final boolean result;
        
        if (resourceController.getResourceFile() != null) {
            result = p.equals(resourceController.getResourceFile().toPath());
        } else {
            result = false;
        }
        
        return result;
    }
    
    
    private boolean isPathMatchingSceneStyleSheet(Path p) {
        final boolean result;
        
        if (editorController.getSceneStyleSheets() != null) {
            result = editorController.getSceneStyleSheets().contains(p.toFile());
        } else {
            result = false;
        }
        
        return result;
    }
    
}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/hierarchy/treeview/HierarchyTreeCell.java<|end_filename|>
/*
 * Copyright (c) 2017 Gluon and/or its affiliates.
 * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.kit.editor.panel.hierarchy.treeview;

import com.oracle.javafx.scenebuilder.kit.editor.EditorController;
import com.oracle.javafx.scenebuilder.kit.editor.drag.DragController;
import com.oracle.javafx.scenebuilder.kit.editor.drag.target.AbstractDropTarget;
import com.oracle.javafx.scenebuilder.kit.editor.drag.target.AccessoryDropTarget;
import com.oracle.javafx.scenebuilder.kit.editor.drag.target.ContainerZDropTarget;
import com.oracle.javafx.scenebuilder.kit.editor.drag.target.GridPaneDropTarget;
import com.oracle.javafx.scenebuilder.kit.editor.drag.target.RootDropTarget;
import com.oracle.javafx.scenebuilder.kit.i18n.I18N;
import com.oracle.javafx.scenebuilder.kit.editor.images.ImageUtils;
import com.oracle.javafx.scenebuilder.kit.editor.job.atomic.ModifyFxIdJob;
import com.oracle.javafx.scenebuilder.kit.editor.job.atomic.ModifyObjectJob;
import com.oracle.javafx.scenebuilder.kit.editor.panel.hierarchy.HierarchyDNDController;
import com.oracle.javafx.scenebuilder.kit.editor.panel.hierarchy.HierarchyItem;
import com.oracle.javafx.scenebuilder.kit.editor.panel.hierarchy.AbstractHierarchyPanelController;
import com.oracle.javafx.scenebuilder.kit.editor.panel.hierarchy.AbstractHierarchyPanelController.BorderSide;
import com.oracle.javafx.scenebuilder.kit.editor.panel.hierarchy.AbstractHierarchyPanelController.DisplayOption;
import com.oracle.javafx.scenebuilder.kit.editor.panel.hierarchy.HierarchyDNDController.DroppingMouseLocation;
import com.oracle.javafx.scenebuilder.kit.editor.report.CSSParsingReport;
import com.oracle.javafx.scenebuilder.kit.editor.util.InlineEditController;
import com.oracle.javafx.scenebuilder.kit.editor.util.InlineEditController.Type;
import com.oracle.javafx.scenebuilder.kit.editor.report.ErrorReport;
import com.oracle.javafx.scenebuilder.kit.editor.report.ErrorReportEntry;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMDocument;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMInstance;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMIntrinsic;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMNode;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMObject;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMPropertyT;
import com.oracle.javafx.scenebuilder.kit.glossary.Glossary;
import com.oracle.javafx.scenebuilder.kit.metadata.Metadata;
import com.oracle.javafx.scenebuilder.kit.metadata.property.ValuePropertyMetadata;
import com.oracle.javafx.scenebuilder.kit.metadata.util.DesignHierarchyMask;
import com.oracle.javafx.scenebuilder.kit.metadata.util.DesignHierarchyMask.Accessory;
import com.oracle.javafx.scenebuilder.kit.metadata.util.PropertyName;

import java.net.URL;
import java.util.List;
import java.util.Set;

import javafx.beans.value.ChangeListener;
import javafx.beans.value.WeakChangeListener;
import javafx.collections.ObservableList;
import javafx.css.CssParser;
import javafx.event.EventHandler;
import javafx.scene.control.TreeCell;
import javafx.scene.control.TreeItem;
import javafx.scene.input.DragEvent;
import javafx.scene.input.MouseEvent;
import javafx.geometry.Bounds;
import javafx.geometry.Point2D;
import javafx.scene.Cursor;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Control;
import javafx.scene.control.Label;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextInputControl;
import javafx.scene.control.Tooltip;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.MouseButton;
import javafx.scene.layout.Border;
import javafx.scene.layout.BorderStroke;
import javafx.scene.layout.BorderStrokeStyle;
import javafx.scene.layout.BorderWidths;
import javafx.scene.layout.CornerRadii;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Paint;
import javafx.scene.shape.Line;
import javafx.util.Callback;

/**
 * TreeCells used by the hierarchy TreeView.
 *
 * p
 *
 * @param <T>
 */
public class HierarchyTreeCell<T extends HierarchyItem> extends TreeCell<HierarchyItem> {

    private final AbstractHierarchyPanelController panelController;

    static final String TREE_CELL_GRAPHIC = "tree-cell-graphic";
    public static final String HIERARCHY_FIRST_CELL = "hierarchy-first-cell";
    static final String HIERARCHY_PLACE_HOLDER_LABEL = "hierarchy-place-holder-label";
    static final String HIERARCHY_READWRITE_LABEL = "hierarchy-readwrite-label";
    // Style class used for lookup
    static final String HIERARCHY_TREE_CELL = "hierarchy-tree-cell";

    private final HBox graphic = new HBox();
    private final Label placeHolderLabel = new Label();
    private final Label classNameInfoLabel = new Label();
    private final Label displayInfoLabel = new Label();
    private final ImageView placeHolderImageView = new ImageView();
    private final ImageView classNameImageView = new ImageView();
    private final ImageView warningBadgeImageView = new ImageView();
    private final ImageView includedFileImageView = new ImageView();
    // Stack used to add badges over the top of the node icon
    private final StackPane iconsStack = new StackPane();
    // We use a label to set a tooltip over the node icon 
    // (StackPane does not allow to set tooltips)
    private final Label iconsLabel = new Label();
    private final Tooltip warningBadgeTooltip = new Tooltip();

    // Vertical line used when inserting an item in order to indicate 
    // the parent into which the item will be inserted.
    // Horizontal lines are handled directly by the cell and are built using CSS only.
    //
    // This line will be added to / removed from the skin of the panel control
    // during DND gestures.
    private final Line insertLineIndicator = new Line();

    // Listener for the display option used to update the display info label
    final ChangeListener<DisplayOption> displayOptionListener = (ov, t, t1) -> {
        // Update display info for non empty cells
        if (!isEmpty() && getItem() != null && !getItem().isEmpty()) {
            final String displayInfo = getItem().getDisplayInfo(t1);
            displayInfoLabel.setText(displayInfo);
            displayInfoLabel.setManaged(getItem().hasDisplayInfo(t1));
            displayInfoLabel.setVisible(getItem().hasDisplayInfo(t1));
        }
    };

    public HierarchyTreeCell(final AbstractHierarchyPanelController c) {
        super();
        this.panelController = c;

        iconsStack.getChildren().setAll(
                classNameImageView,
                warningBadgeImageView);
        iconsLabel.setGraphic(iconsStack);
        // RT-31645 : we cannot dynamically update the HBox graphic children 
        // in the cell.updateItem method.
        // We set once the graphic children, then we update the managed property
        // of the children depending on the cell item. 
        graphic.getChildren().setAll(
                includedFileImageView,
                placeHolderImageView,
                iconsLabel,
                placeHolderLabel,
                classNameInfoLabel,
                displayInfoLabel);

        // Add style class used when invoking lookupAll
        this.getStyleClass().add(HIERARCHY_TREE_CELL);

        // Update vertical insert line indicator stroke width
        insertLineIndicator.setStrokeWidth(2.0);

        // CSS
        graphic.getStyleClass().add(TREE_CELL_GRAPHIC);
        updatePlaceHolder();
        displayInfoLabel.getStyleClass().add(HIERARCHY_READWRITE_LABEL);
        placeHolderLabel.getStyleClass().add(HIERARCHY_PLACE_HOLDER_LABEL);
        // Layout
        classNameInfoLabel.setMinWidth(Control.USE_PREF_SIZE);
        displayInfoLabel.setMaxWidth(Double.MAX_VALUE);
        HBox.setHgrow(displayInfoLabel, Priority.ALWAYS);

        panelController.displayOptionProperty().addListener(
                new WeakChangeListener<>(displayOptionListener));

        // Key events
        //----------------------------------------------------------------------
        final EventHandler<KeyEvent> keyEventHandler = e -> filterKeyEvent(e);
        this.addEventFilter(KeyEvent.ANY, keyEventHandler);

        // Mouse events
        //----------------------------------------------------------------------
        final EventHandler<MouseEvent> mouseEventHandler = e -> filterMouseEvent(e);
        this.addEventFilter(MouseEvent.ANY, mouseEventHandler);

        // Drag events
        //----------------------------------------------------------------------
        final HierarchyDNDController dndController = panelController.getDNDController();

        setOnDragDropped(event -> {
            final TreeItem<HierarchyItem> treeItem
                    = HierarchyTreeCell.this.getTreeItem();
            // Forward to the DND controller
            dndController.handleOnDragDropped(treeItem, event);

            // CSS
            panelController.clearBorderColor(HierarchyTreeCell.this);
            // Remove insert line indicator
            panelController.removeFromPanelControlSkin(insertLineIndicator);
        });
        setOnDragEntered(event -> {
            final TreeItem<HierarchyItem> treeItem
                    = HierarchyTreeCell.this.getTreeItem();
            // Forward to the DND controller
            dndController.handleOnDragEntered(treeItem, event);
        });
        setOnDragExited(event -> {
            final TreeItem<HierarchyItem> treeItem
                    = HierarchyTreeCell.this.getTreeItem();
            final Bounds bounds = HierarchyTreeCell.this.getLayoutBounds();
            final Point2D point = HierarchyTreeCell.this.localToScene(bounds.getMinX(), bounds.getMinY(), true /* rootScene */);
            final DroppingMouseLocation location;
            if (event.getSceneY() <= point.getY()) {
                location = DroppingMouseLocation.TOP;
            } else {
                location = DroppingMouseLocation.BOTTOM;
            }

            // Forward to the DND controller
            dndController.handleOnDragExited(treeItem, event, location);

            // CSS
            panelController.clearBorderColor(HierarchyTreeCell.this);
            // Remove insert line indicator
            panelController.removeFromPanelControlSkin(insertLineIndicator);
        });
        setOnDragOver(event -> {
            final TreeItem<HierarchyItem> treeItem
                    = HierarchyTreeCell.this.getTreeItem();
            final DragController dragController
                    = panelController.getEditorController().getDragController();
            final DroppingMouseLocation location = getDroppingMouseLocation(event);

            // Forward to the DND controller
            dndController.handleOnDragOver(treeItem, event, location); // (1)

            panelController.clearBorderColor();
            // Update vertical insert line indicator stroke color
            final Paint paint = panelController.getParentRingColor();
            insertLineIndicator.setStroke(paint);
            // Remove insert line indicator
            panelController.removeFromPanelControlSkin(insertLineIndicator);

            // If an animation timeline is running 
            // (auto-scroll when DND to the top or bottom of the Hierarchy),
            // we do not display insert indicators.
            if (panelController.isTimelineRunning()) {
                return;
            }

            // Drop target has been updated because of (1)
            if (dragController.isDropAccepted()) {

                final AbstractDropTarget dropTarget = dragController.getDropTarget();
                final FXOMObject dropTargetObject = dropTarget.getTargetObject();
                final TreeItem<?> rootTreeItem = getTreeView().getRoot();

                if (dropTarget instanceof RootDropTarget) {
                    // No visual feedback in case of dropping the root node
                    return;
                }

                //==========================================================
                // ACCESSORIES :
                //
                // No need to handle the insert line indicator.
                // Border is set either on the accessory place holder cell
                // or on the accessory owner cell.
                //==========================================================
                if (dropTarget instanceof AccessoryDropTarget) {

                    final AccessoryDropTarget accessoryDropTarget = (AccessoryDropTarget) dropTarget;
                    final TreeCell<?> cell;

                    // TreeItem is null when dropping below the datas
                    // => the drop target is the root
                    if (treeItem == null) {
                        cell = HierarchyTreeViewUtils.getTreeCell(getTreeView(), rootTreeItem);
                    } else {
                        final HierarchyItem item = treeItem.getValue();
                        assert item != null;

                        if (item.isPlaceHolder()) {
                            cell = HierarchyTreeCell.this;
                        } else if (accessoryDropTarget.getAccessory() == Accessory.GRAPHIC) {
                            // Check if an empty graphic TreeItem has been added
                            final TreeItem<HierarchyItem> graphicTreeItem
                                    = dndController.getEmptyGraphicTreeItemFor(treeItem);
                            if (graphicTreeItem != null) {
                                cell = HierarchyTreeViewUtils.getTreeCell(getTreeView(), graphicTreeItem);
                            } else {
                                final TreeItem<HierarchyItem> accessoryOwnerTreeItem1
                                        = panelController.lookupTreeItem(dropTargetObject);
                                cell = HierarchyTreeViewUtils.getTreeCell(getTreeView(), accessoryOwnerTreeItem1);
                            }
                        } else {
                            final TreeItem<HierarchyItem> accessoryOwnerTreeItem2
                                    = panelController.lookupTreeItem(dropTargetObject);
                            cell = HierarchyTreeViewUtils.getTreeCell(getTreeView(), accessoryOwnerTreeItem2);
                        }
                    }

                    panelController.setBorder(cell, BorderSide.TOP_RIGHT_BOTTOM_LEFT);
                }//
                //==========================================================
                // SUB COMPONENTS :
                //
                // Need to handle the insert line indicator.
                //==========================================================
                else {
                    assert dropTarget instanceof ContainerZDropTarget
                            || dropTarget instanceof GridPaneDropTarget;
                    TreeItem<?> startTreeItem;
                    TreeCell<?> startCell, stopCell;

                    // TreeItem is null when dropping below the datas
                    // => the drop target is the root
                    if (treeItem == null) {
                        if (rootTreeItem.isLeaf() || !rootTreeItem.isExpanded()) {
                            final TreeCell<?> rootCell = HierarchyTreeViewUtils.getTreeCell(getTreeView(), 0);
                            panelController.setBorder(rootCell, BorderSide.TOP_RIGHT_BOTTOM_LEFT);
                        } else {
                            final TreeItem<?> lastTreeItem = panelController.getLastVisibleTreeItem(rootTreeItem);
                            final TreeCell<?> lastCell = HierarchyTreeViewUtils.getTreeCell(getTreeView(), lastTreeItem);
                            // As we are dropping below the datas, the last cell is visible
                            assert lastCell != null;
                            panelController.setBorder(lastCell, BorderSide.BOTTOM);

                            // Update vertical insert line
                            startTreeItem = rootTreeItem;
                            startCell = HierarchyTreeViewUtils.getTreeCell(getTreeView(), startTreeItem);
                            stopCell = lastCell;
                            updateInsertLineIndicator(startCell, stopCell);
                            panelController.addToPanelControlSkin(insertLineIndicator);
                        }

                    } else {
                        final HierarchyItem item = treeItem.getValue();
                        assert item != null;

                        if (item.isPlaceHolder() || item.getFxomObject() == dropTargetObject) {
                            // The place holder item is filled with a container
                            // accepting sub components
                            panelController.setBorder(HierarchyTreeCell.this, BorderSide.TOP_RIGHT_BOTTOM_LEFT);
                        } else {
                            // REORDERING :
                            // To avoid visual movement of the horizontal border when
                            // dragging from one cell to another,
                            // we always set the border on the cell bottom location :
                            // - if we handle REORDER BELOW gesture, just set the bottom 
                            // border on the current cell
                            // - if we handle REORDER ABOVE gesture, we set the bottom 
                            // border on the previous cell
                            //
                            switch (location) {

                                // REORDER ABOVE gesture
                                case TOP:
                                    if (treeItem == rootTreeItem) {
                                        panelController.setBorder(HierarchyTreeCell.this, BorderSide.TOP_RIGHT_BOTTOM_LEFT);
                                    } else {
                                        final int index = getIndex();
                                        // Retrieve the previous cell
                                        // Note : we set the border on the bottom of the previous cell 
                                        // instead of using the top of the current cell in order to avoid
                                        // visual gap when DND from one cell to another
                                        final TreeCell<?> previousCell
                                                = HierarchyTreeViewUtils.getTreeCell(getTreeView(), index - 1);
                                        // The previous cell is null when the item is not visible
                                        if (previousCell != null) {
                                            panelController.setBorder(previousCell, BorderSide.BOTTOM);
                                        }

                                        // Update vertical insert line
                                        startTreeItem = panelController.lookupTreeItem(dropTarget.getTargetObject());
                                        startCell = HierarchyTreeViewUtils.getTreeCell(getTreeView(), startTreeItem);
                                        stopCell = previousCell;
                                        updateInsertLineIndicator(startCell, stopCell);
                                        panelController.addToPanelControlSkin(insertLineIndicator);
                                    }
                                    break;

                                // REPARENT gesture
                                case CENTER:
                                    if (treeItem.isLeaf() || !treeItem.isExpanded()) {
                                        panelController.setBorder(HierarchyTreeCell.this, BorderSide.TOP_RIGHT_BOTTOM_LEFT);
                                    } else {
                                        // Reparent to the treeItem as last child
                                        final TreeItem<?> lastTreeItem = panelController.getLastVisibleTreeItem(treeItem);
                                        final TreeCell<?> lastCell = HierarchyTreeViewUtils.getTreeCell(getTreeView(), lastTreeItem);
                                        // Last cell is null when the item is not visible
                                        if (lastCell != null) {
                                            panelController.setBorder(lastCell, BorderSide.BOTTOM);
                                        }

                                        // Update vertical insert line
                                        startTreeItem = getTreeItem();
                                        startCell = HierarchyTreeViewUtils.getTreeCell(getTreeView(), startTreeItem);
                                        stopCell = lastCell;
                                        updateInsertLineIndicator(startCell, stopCell);
                                        panelController.addToPanelControlSkin(insertLineIndicator);
                                    }
                                    break;

                                // REORDER BELOW gesture
                                case BOTTOM:
                                    if (treeItem == rootTreeItem
                                            && (treeItem.isLeaf() || !treeItem.isExpanded())) {
                                        panelController.setBorder(HierarchyTreeCell.this, BorderSide.TOP_RIGHT_BOTTOM_LEFT);
                                    } else {
                                        // Reparent to the treeItem as first child
                                        panelController.setBorder(HierarchyTreeCell.this, BorderSide.BOTTOM);

                                        // Update vertical insert line
                                        startTreeItem = panelController.lookupTreeItem(dropTarget.getTargetObject());
                                        startCell = HierarchyTreeViewUtils.getTreeCell(getTreeView(), startTreeItem);
                                        stopCell = HierarchyTreeCell.this;
                                        updateInsertLineIndicator(startCell, stopCell);
                                        panelController.addToPanelControlSkin(insertLineIndicator);
                                    }
                                    break;

                                default:
                                    assert false;
                                    break;
                            }
                        }
                    }
                }
            }
        });
    }

    @Override
    public void updateItem(HierarchyItem item, boolean empty) {
        super.updateItem(item, empty);

        // The cell is not empty (TreeItem is not null) 
        // AND the TreeItem value is not null
        if (!empty && item != null) {
            updateLayout(item);
            setGraphic(graphic);
            setText(null);
            // Update parent ring when scrolling / resizing vertically / expanding and collapsing
            panelController.updateParentRing();
        } else {
            assert item == null;
            setGraphic(null);
            setText(null);
            // Clear CSS for empty cells
            panelController.clearBorderColor(this);
        }
    }

    public final void updatePlaceHolder() {
        final Paint paint = panelController.getParentRingColor();
        placeHolderLabel.setTextFill(paint);
        final BorderWidths bw = new BorderWidths(1);
        final BorderStroke bs = new BorderStroke(paint, BorderStrokeStyle.SOLID,
                CornerRadii.EMPTY, bw);
        final Border b = new Border(bs);
        placeHolderLabel.setBorder(b);
    }

    private void filterKeyEvent(final KeyEvent ke) {
        // empty
    }

    private void filterMouseEvent(final MouseEvent me) {

        if (me.getEventType() == MouseEvent.MOUSE_PRESSED
                && me.getButton() == MouseButton.PRIMARY) {

            // Mouse pressed on a non empty cell :
            // => we may start inline editing
            if (isEmpty() == false) { // (1)
                if (me.getClickCount() >= 2) {
                    // Start inline editing the display info on double click OVER the display info label
                    // Double click over the class name label will end up with the native expand/collapse behavior
                    final HierarchyItem item = getItem();
                    assert item != null; // Because of (1)
                    final DisplayOption option = panelController.getDisplayOption();
                    if (item.hasDisplayInfo(option)
                            && item.isResourceKey(option) == false // Do not allow inline editing of the I18N value
                            && displayInfoLabel.isHover()) {
                        startEditingDisplayInfo();
                        // Consume the event so the native expand/collapse behavior is not performed
                        me.consume();
                    }
                }
            } //
            // Mouse pressed on an empty cell
            // => we perform select none
            else {
                // We clear the TreeView selection.
                // Note that this is not the same as invoking selection.clear().
                // Indeed, when empty BorderPane place holders are selected,
                // the SB selection is empty whereas the TreeView selection is not.
                getTreeView().getSelectionModel().clearSelection();
            }
        }
        updateCursor(me);
    }

    private void updateCursor(final MouseEvent me) {
        final Scene scene = getScene();

        if (scene == null) {
            // scene may be null when tree view is collapsed
            return;
        }
        // When another window is focused (just like the preview window), 
        // we use default cursor
        if (!getScene().getWindow().isFocused()) {
            scene.setCursor(Cursor.DEFAULT);
            return;
        }
        if (isEmpty()) {
            scene.setCursor(Cursor.DEFAULT);
        } else {
            final TreeItem<HierarchyItem> rootTreeItem = getTreeView().getRoot();
            final HierarchyItem item = getTreeItem().getValue();
            assert item != null;
            boolean isRoot = getTreeItem() == rootTreeItem;
            boolean isEmpty = item.isEmpty();

            if (me.getEventType() == MouseEvent.MOUSE_ENTERED) {
                if (!me.isPrimaryButtonDown()) {
                    // Cannot DND root or place holder items
                    if (isRoot || isEmpty) {
                        setCursor(Cursor.DEFAULT);
                    } else {
                        setCursor(Cursor.OPEN_HAND);
                    }
                }
            } else if (me.getEventType() == MouseEvent.MOUSE_PRESSED) {
                // Cannot DND root or place holder items
                if (isRoot || isEmpty) {
                    setCursor(Cursor.DEFAULT);
                } else {
                    setCursor(Cursor.CLOSED_HAND);
                }
            } else if (me.getEventType() == MouseEvent.MOUSE_RELEASED) {
                // Cannot DND root or place holder items
                if (isRoot || isEmpty) {
                    setCursor(Cursor.DEFAULT);
                } else {
                    setCursor(Cursor.OPEN_HAND);
                }
            } else if (me.getEventType() == MouseEvent.MOUSE_EXITED) {
                setCursor(Cursor.DEFAULT);
            }
        }
    }

    /**
     * *************************************************************************
     * Inline editing
     *
     * We cannot use the FX inline editing because it occurs on selection +
     * simple mouse click
     * *************************************************************************
     */
    public void startEditingDisplayInfo() {
        assert getItem().hasDisplayInfo(panelController.getDisplayOption());
        final InlineEditController inlineEditController
                = panelController.getEditorController().getInlineEditController();
        final TextInputControl editor;
        final Type type;
        final String initialValue;

        // 1) Build the TextInputControl used to inline edit
        //----------------------------------------------------------------------
        // INFO display option may use either a TextField or a TextArea
        if (panelController.getDisplayOption() == DisplayOption.INFO) {
            final String info = getItem().getDescription();
            final Object sceneGraphObject = getItem().getFxomObject().getSceneGraphObject();
            if (sceneGraphObject instanceof TextArea || DesignHierarchyMask.containsLineFeed(info)) {
                type = Type.TEXT_AREA;
            } else {
                type = Type.TEXT_FIELD;
            }
            // displayInfoLabel.getText() may be truncated to a single line description
            // We set the initial value with the complete description value
            initialValue = getItem().getDescription();
        } //
        // FXID and NODEID options use a TextField
        else {
            type = Type.TEXT_FIELD;
            initialValue = displayInfoLabel.getText();
        }
        editor = inlineEditController.createTextInputControl(type, displayInfoLabel, initialValue);
        // CSS
        final ObservableList<String> styleSheets
                = panelController.getPanelRoot().getStylesheets();
        editor.getStylesheets().addAll(styleSheets);
        editor.getStyleClass().add("theme-presets"); //NOI18N
        editor.getStyleClass().add(InlineEditController.INLINE_EDITOR);

        // 2) Build the COMMIT Callback
        // This callback will be invoked to commit the new value
        // It returns true if the value is unchanged or if the commit succeeded, 
        // false otherwise
        //----------------------------------------------------------------------
        final Callback<String, Boolean> requestCommit = newValue -> {
            // 1) Check the input value is valid
            // 2) If valid, commit the new value and return true
            // 3) Otherwise, return false
            final HierarchyItem item = getItem();
            // Item may be null when invoking UNDO while inline editing session is on going
            if (item != null) {
                final FXOMObject fxomObject = item.getFxomObject();
                final DisplayOption option = panelController.getDisplayOption();
                final EditorController editorController = panelController.getEditorController();
                switch (option) {
                    case INFO:
                    case NODEID:
                        if (fxomObject instanceof FXOMInstance) {
                            final FXOMInstance fxomInstance = (FXOMInstance) fxomObject;
                            final PropertyName propertyName = item.getPropertyNameForDisplayInfo(option);
                            assert propertyName != null;
                            final ValuePropertyMetadata vpm
                                    = Metadata.getMetadata().queryValueProperty(fxomInstance, propertyName);
                            final ModifyObjectJob job1
                                    = new ModifyObjectJob(fxomInstance, vpm, newValue, editorController);
                            if (job1.isExecutable()) {
                                editorController.getJobManager().push(job1);
                            }
                        }
                        break;
                    case FXID:
                        assert newValue != null;
                        final String fxId = newValue.isEmpty() ? null : newValue;
                        final ModifyFxIdJob job2
                                = new ModifyFxIdJob(fxomObject, fxId, editorController);
                        if (job2.isExecutable()) {

                            // If a controller class has been defined, 
                            // check if the fx id is an injectable field
                            final String controllerClass
                                    = editorController.getFxomDocument().getFxomRoot().getFxController();
                            if (controllerClass != null && fxId != null) {
                                final URL location = editorController.getFxmlLocation();
                                final Class<?> clazz = fxomObject.getSceneGraphObject() == null ? null
                                        : fxomObject.getSceneGraphObject().getClass();
                                final Glossary glossary = editorController.getGlossary();
                                final List<String> fxIds1 = glossary.queryFxIds(location, controllerClass, clazz);
                                if (fxIds1.contains(fxId) == false) {
                                    editorController.getMessageLog().logWarningMessage(
                                            "log.warning.no.injectable.fxid", fxId);
                                }
                            }

                            // Check duplicared fx ids
                            final FXOMDocument fxomDocument = editorController.getFxomDocument();
                            final Set<String> fxIds2 = fxomDocument.collectFxIds().keySet();
                            if (fxIds2.contains(fxId)) {
                                editorController.getMessageLog().logWarningMessage(
                                        "log.warning.duplicate.fxid", fxId);
                            }
                            
                            editorController.getJobManager().push(job2);

                        } else if (fxId != null) {
                            editorController.getMessageLog().logWarningMessage(
                                    "log.warning.invalid.fxid", fxId);
                        }
                        break;
                    default:
                        assert false;
                        return false;
                }
            }
            return true;
        };
        inlineEditController.startEditingSession(editor, displayInfoLabel, requestCommit, null);
    }

    private void updateLayout(HierarchyItem item) {
        assert item != null;
        final FXOMObject fxomObject = item.getFxomObject();

        // Update styling
        this.getStyleClass().removeAll(HIERARCHY_FIRST_CELL);
        if (fxomObject != null && fxomObject.getParentObject() == null) {
            this.getStyleClass().add(HIERARCHY_FIRST_CELL);
        }

        // Update ImageViews
        final Image placeHolderImage = item.getPlaceHolderImage();
        placeHolderImageView.setImage(placeHolderImage);
        placeHolderImageView.setManaged(placeHolderImage != null);

        final Image classNameImage = item.getClassNameIcon();
        classNameImageView.setImage(classNameImage);
        classNameImageView.setManaged(classNameImage != null);

        // Included file
        if (fxomObject instanceof FXOMIntrinsic
                && ((FXOMIntrinsic) fxomObject).getType() == FXOMIntrinsic.Type.FX_INCLUDE) {
            final URL resource = ImageUtils.getNodeIconURL("Included.png"); //NOI18N
            includedFileImageView.setImage(ImageUtils.getImage(resource));
            includedFileImageView.setManaged(true);
        } else {
            includedFileImageView.setImage(null);
            includedFileImageView.setManaged(false);
        }

        final List<ErrorReportEntry> entries = getErrorReportEntries(item);
        if (entries != null) {
            assert !entries.isEmpty();
            // Update tooltip with the first entry
            final ErrorReportEntry entry = entries.get(0);
            warningBadgeTooltip.setText(getErrorReport(entry));
            warningBadgeImageView.setImage(ImageUtils.getWarningBadgeImage());
            warningBadgeImageView.setManaged(true);
            iconsLabel.setTooltip(warningBadgeTooltip);
        } else {
            warningBadgeTooltip.setText(null);
            warningBadgeImageView.setImage(null);
            warningBadgeImageView.setManaged(false);
            iconsLabel.setTooltip(null);
        }

        // Update Labels
        final String placeHolderInfo = item.getPlaceHolderInfo();
        placeHolderLabel.setText(placeHolderInfo);
        placeHolderLabel.setManaged(item.isEmpty());
        placeHolderLabel.setVisible(item.isEmpty());

        final String classNameInfo = item.getClassNameInfo();
        classNameInfoLabel.setText(classNameInfo);
        classNameInfoLabel.setManaged(classNameInfo != null);
        classNameInfoLabel.setVisible(classNameInfo != null);

        final DisplayOption option = panelController.getDisplayOption();
        final String displayInfo = item.getDisplayInfo(option);
        // Do not allow inline editing of the I18N value
        if (item.isResourceKey(option)) {
            displayInfoLabel.getStyleClass().removeAll(HIERARCHY_READWRITE_LABEL);
        } else {
            if (displayInfoLabel.getStyleClass().contains(HIERARCHY_READWRITE_LABEL) == false) {
                displayInfoLabel.getStyleClass().add(HIERARCHY_READWRITE_LABEL);
            }
        }
        displayInfoLabel.setText(displayInfo);
        displayInfoLabel.setManaged(item.hasDisplayInfo(option));
        displayInfoLabel.setVisible(item.hasDisplayInfo(option));
    }

    private List<ErrorReportEntry> getErrorReportEntries(HierarchyItem item) {
        if (item == null || item.isEmpty()) {
            return null;
        }
        final EditorController editorController = panelController.getEditorController();
        final ErrorReport errorReport = editorController.getErrorReport();
        final FXOMObject fxomObject = item.getFxomObject();
        assert fxomObject != null;
        return errorReport.query(fxomObject, !getTreeItem().isExpanded());
    }
    
    public String getErrorReport(final ErrorReportEntry entry) {

        final StringBuilder result = new StringBuilder();

        final FXOMNode fxomNode = entry.getFxomNode();

        switch (entry.getType()) {
            case UNRESOLVED_CLASS:
                result.append(I18N.getString("hierarchy.unresolved.class"));
                break;
            case UNRESOLVED_LOCATION:
                result.append(I18N.getString("hierarchy.unresolved.location"));
                break;
            case UNRESOLVED_RESOURCE:
                result.append(I18N.getString("hierarchy.unresolved.resource"));
                break;
            case INVALID_CSS_CONTENT:
                assert entry.getCssParsingReport() != null;
                result.append(makeCssParsingErrorString(entry.getCssParsingReport()));
                break;
            case UNSUPPORTED_EXPRESSION:
                result.append(I18N.getString("hierarchy.unsupported.expression"));
                break;
        }
        result.append(" "); //NOI18N
        if (fxomNode instanceof FXOMPropertyT) {
            final FXOMPropertyT fxomProperty = (FXOMPropertyT) fxomNode;
            result.append(fxomProperty.getValue());
        } else if (fxomNode instanceof FXOMIntrinsic) {
            final FXOMIntrinsic fxomIntrinsic = (FXOMIntrinsic) fxomNode;
            result.append(fxomIntrinsic.getSource());
        } else if (fxomNode instanceof FXOMObject) {
            final FXOMObject fxomObject = (FXOMObject) fxomNode;
            final DesignHierarchyMask mask = new DesignHierarchyMask(fxomObject);
            result.append(mask.getClassNameInfo());
        }

        return result.toString();
    }
    
    private void updateInsertLineIndicator(
            final TreeCell<?> startTreeCell,
            final TreeCell<?> stopTreeCell) {

        //----------------------------------------------------------------------
        // START POINT CALCULATION
        //----------------------------------------------------------------------
        // Retrieve the disclosure node from which the vertical line will start
        double startX, startY;
        if (startTreeCell != null) {
            final Node disclosureNode = startTreeCell.getDisclosureNode();
            final Bounds startBounds = startTreeCell.getLayoutBounds();
            final Point2D startCellPoint = startTreeCell.localToParent(
                    startBounds.getMinX(), startBounds.getMinY());

            final Bounds disclosureNodeBounds = disclosureNode.getLayoutBounds();
            final Point2D disclosureNodePoint = disclosureNode.localToParent(
                    disclosureNodeBounds.getMinX(), disclosureNodeBounds.getMinY());

            // Initialize start point to the disclosure node of the start cell
            startX = startCellPoint.getX()
                    + disclosureNodePoint.getX()
                    + disclosureNodeBounds.getWidth() / 2 + 1; // +1 px tuning
            startY = startCellPoint.getY()
                    + disclosureNodePoint.getY()
                    + disclosureNodeBounds.getHeight() - 6; // -6 px tuning
        } else {
            // The start cell is not visible :
            // x is set to the current cell graphic
            // y is set to the top of the TreeView / TreeTableView
            final Bounds graphicBounds = getGraphic().getLayoutBounds();
            final Point2D graphicPoint = getGraphic().localToParent(
                    graphicBounds.getMinX(), graphicBounds.getMinY());

            startX = graphicPoint.getX();
            startY = panelController.getContentTopY();
        }

        //----------------------------------------------------------------------
        // END POINT CALCULATION
        //----------------------------------------------------------------------
        double endX, endY;
        endX = startX;
        if (stopTreeCell != null) {
            final Bounds stopBounds = stopTreeCell.getLayoutBounds();
            final Point2D stopCellPoint = stopTreeCell.localToParent(
                    stopBounds.getMinX(), stopBounds.getMinY());

            // Initialize end point to the end cell
            endY = stopCellPoint.getY()
                    + stopBounds.getHeight() // Add the stop cell height
                    - 1; // -1 px tuning
        } else {
            // The stop cell is not visisble :
            // y is set to the bottom of the TreeView / TreeTableView
            endY = panelController.getContentBottomY();
        }

        insertLineIndicator.setStartX(startX);
        insertLineIndicator.setStartY(startY);
        insertLineIndicator.setEndX(endX);
        insertLineIndicator.setEndY(endY);
    }

    private DroppingMouseLocation getDroppingMouseLocation(final DragEvent event) {
        final DroppingMouseLocation location;
        if (this.getTreeItem() != null) {
            if ((getHeight() * 0.25) > event.getY()) {
                location = DroppingMouseLocation.TOP;
            } else if ((getHeight() * 0.75) < event.getY()) {
                location = DroppingMouseLocation.BOTTOM;
            } else {
                location = DroppingMouseLocation.CENTER;
            }
        } else {
            location = DroppingMouseLocation.BOTTOM;
        }
        return location;
    }
    
    private String makeCssParsingErrorString(CSSParsingReport r) {
        final StringBuilder result = new StringBuilder();
        
        if (r.getIOException() != null) {
            result.append(r.getIOException());
        } else {
            assert r.getParseErrors().isEmpty() == false;
            int errorCount = 0;
            for (CssParser.ParseError e : r.getParseErrors()) {
                result.append(e.getMessage());
                errorCount++;
                if (errorCount < 5) {
                    result.append('\n');
                } else {
                    result.append("...");
                    break;
                }
            }
        }
        
        return result.toString();
    }
    
}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/inspector/editors/ButtonTypeEditor.java<|end_filename|>
/*
 * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.kit.editor.panel.inspector.editors;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;

import javafx.beans.value.ChangeListener;
import javafx.collections.FXCollections;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonType;
import javafx.scene.control.ChoiceBox;
import javafx.scene.control.MenuItem;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.util.StringConverter;

import com.oracle.javafx.scenebuilder.kit.metadata.property.ValuePropertyMetadata;
import com.oracle.javafx.scenebuilder.kit.metadata.property.value.ButtonTypePropertyMetadata;

/**
 * Editor of the DialogPane.buttonTypes property. It may contain several
 * ButtonType, that have their dedicated class (ButtonTypeItem).
 *
 *
 */
public class ButtonTypeEditor extends InlineListEditor {

    private static Map<String, ButtonType> predefinedButtonsNames = new TreeMap<>();
    private Collection<ButtonType> buttonList = new TreeSet<ButtonType>(getButtonTypeComparator());

    public ButtonTypeEditor(ValuePropertyMetadata propMeta, Set<Class<?>> selectedClasses) {
        super(propMeta, selectedClasses);
        initialize();
    }

    private void initialize() {
        setLayoutFormat(PropertyEditor.LayoutFormat.DOUBLE_LINE);
        // Build a sorted map, with button names as keys
        for (Entry<ButtonType, String> entry : ButtonTypePropertyMetadata.getButtonTypeMap().entrySet()) {
            predefinedButtonsNames.put(entry.getValue(), entry.getKey());
        }
        updateButtonLists();
        addItem(getNewButtonTypeItem());
    }

    private ButtonTypeItem getNewButtonTypeItem() {
        return new ButtonTypeItem(this, buttonList);
    }

    private void updateButtonLists() {
        // As we don't want several buttons with the same type,
        // we need to prevent this by updating the buttons types list in each
        // item.
        buttonList.clear();
        buttonList.addAll(predefinedButtonsNames.values());
        for (EditorItem item : getEditorItems()) {
            Object itemValueObj = item.getValue();
            if (itemValueObj == null) {
                continue;
            }
            assert itemValueObj instanceof ButtonType;
            ButtonType itemValue = (ButtonType) itemValueObj;
            buttonList.remove(itemValue);
        }
        for (EditorItem item : getEditorItems()) {
            assert item instanceof ButtonTypeItem;
            ((ButtonTypeItem) item).updateButtonList(buttonList);
        }
    }

    private Comparator<ButtonType> getButtonTypeComparator() {
        Comparator<ButtonType> comparator = (ButtonType bt1, ButtonType bt2) -> bt1.getText().compareTo(bt2.getText());
        return comparator;
    }

    @Override
    public Object getValue() {
        List<ButtonType> value = FXCollections.observableArrayList();
        // Group all the item values in a list
        for (EditorItem buttonTypeItem : getEditorItems()) {
            Object itemValueObj = buttonTypeItem.getValue();
            if (itemValueObj == null) {
                continue;
            }
            assert itemValueObj instanceof ButtonType;
            ButtonType itemValue = (ButtonType) itemValueObj;
            value.add(itemValue);
        }
        if (value.isEmpty()) {
            // no button type
            return super.getPropertyMeta().getDefaultValueObject();
        } else {
            return value;
        }
    }

    @SuppressWarnings("unchecked")
    @Override
    public void setValue(Object value) {
        setValueGeneric(value);
        if (value == null) {
            reset();
            return;
        }
        assert value instanceof List;
        // Warning : value is the editing list.
        // We do not want to set the valueProperty() to editing list
        setValueGeneric(value);
        if (isSetValueDone()) {
            return;
        }

        Iterator<EditorItem> itemsIter = new ArrayList<>(getEditorItems()).iterator();
        for (ButtonType item : (List<ButtonType>) value) {
            EditorItem editorItem;
            if (itemsIter.hasNext()) {
                // re-use the current items first
                editorItem = itemsIter.next();
                assert editorItem instanceof ButtonTypeItem;
                ((ButtonTypeItem)editorItem).reset(predefinedButtonsNames.values());
            } else {
                // additional items required
                editorItem = addItem(getNewButtonTypeItem());
            }
            editorItem.setValue(item);
        }
        // Empty the remaining items, if needed
        while (itemsIter.hasNext()) {
            EditorItem editorItem = itemsIter.next();
            removeItem(editorItem);
        }

        // Update the button list
        updateButtonLists();
    }

    public void reset(ValuePropertyMetadata propMeta, Set<Class<?>> selectedClasses) {
        super.reset(propMeta, selectedClasses);
        buttonList.clear();
        buttonList.addAll(predefinedButtonsNames.values());
        // add an empty item
        addItem(getNewButtonTypeItem());
    }

    @Override
    public void requestFocus() {
        EditorItem firstItem = getEditorItems().get(0);
        assert firstItem instanceof ButtonTypeItem;
        ((ButtonTypeItem) firstItem).requestFocus();
    }

    @Override
    public void commit(EditorItem source) {
        super.commit(source);
        updateButtonLists();
    }

    @Override
    public void remove(EditorItem source) {
        super.remove(source);
        updateButtonLists();
    }

    /**
     ***************************************************************************
     *
     * ButtonType item : button types ChoiceBox, and +/- buttons.
     *
     ***************************************************************************
     */
    private class ButtonTypeItem implements EditorItem {

        @FXML
        private Button plusBt;
        @FXML
        private Button minusBt;
        @FXML
        private ChoiceBox<ButtonType> buttonTypeCb;

        private Parent root;
        private EditorItemDelegate editor;

        public ButtonTypeItem(EditorItemDelegate editor, Collection<ButtonType> buttonList) {
//            System.out.println("New ButtonTypeItem.");
            // It is an AutoSuggestEditor without MenuButton
            initialize(editor, buttonList);
        }

        // Method to please FindBugs
        private void initialize(EditorItemDelegate editor, Collection<ButtonType> buttonList) {
            this.editor = editor;
            root = EditorUtils.loadFxml("ButtonTypeEditorItem.fxml", this);//NOI18N

            buttonTypeCb.setConverter(getButtonTypeConverter());
            buttonTypeCb.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<ButtonType>() {
                public void changed(javafx.beans.value.ObservableValue<? extends ButtonType> value,
                        ButtonType prevValue, ButtonType newValue) {
                    editor.commit(ButtonTypeItem.this);
                    updatePlusMinusButtons();
                };
            });

            updateButtonList(buttonList);
            updatePlusMinusButtons();
        }

        public void updateButtonList(Collection<ButtonType> buttonList) {
            // Remove ChoiceBox items that are not in the new list (but keep the
            // current value)
            for (ButtonType buttonType : buttonList) {
                if (!buttonTypeCb.getItems().contains(buttonType)) {
                    buttonTypeCb.getItems().add(buttonType);
                }
            }
            ArrayList<ButtonType> currentItems = new ArrayList<>(buttonTypeCb.getItems());
            for (ButtonType buttonType : currentItems) {
                if (!buttonList.contains(buttonType) && buttonType != getValue()) {
                    buttonTypeCb.getItems().remove(buttonType);
                }
            }
        }

        @Override
        public final Node getNode() {
            return root;
        }

        @Override
        public Object getValue() {
            return buttonTypeCb.getSelectionModel().getSelectedItem();
        }

        @Override
        public void setValue(Object buttonType) {
            assert buttonType instanceof ButtonType;
            buttonTypeCb.getSelectionModel().select((ButtonType) buttonType);
            updatePlusMinusButtons();
        }

        @Override
        public void reset() {
            buttonTypeCb.getSelectionModel().clearSelection();
        }
        
        public void reset(Collection<ButtonType> buttonList) {
            buttonTypeCb.getItems().clear();
            updateButtonList(buttonList);
        }
        

        // Please findBugs
        public void requestFocus() {
            buttonTypeCb.requestFocus();
        }

        @Override
        public void setValueAsIndeterminate() {
            handleIndeterminate(buttonTypeCb);
        }

        @Override
        public MenuItem getMoveUpMenuItem() {
            // not used here
            return null;
        }

        @Override
        public MenuItem getMoveDownMenuItem() {
            // not used here
            return null;
        }

        @Override
        public MenuItem getRemoveMenuItem() {
            // not used here
            return null;
        }

        @Override
        public Button getPlusButton() {
            return plusBt;
        }

        @Override
        public Button getMinusButton() {
            return minusBt;
        }

        @FXML
        void add(ActionEvent event) {
            ButtonTypeEditor.ButtonTypeItem buttonTypeItem = getNewButtonTypeItem();
            editor.add(this, buttonTypeItem);
            buttonTypeItem.requestFocus();
        }

        @FXML
        void remove(ActionEvent event) {
            editor.remove(this);
        }

        @FXML
        void plusBtTyped(KeyEvent event) {
            if (event.getCode() == KeyCode.ENTER) {
                editor.add(this, getNewButtonTypeItem());
            }
        }

        private void updatePlusMinusButtons() {
            if (buttonTypeCb.getSelectionModel().isEmpty()) {
                // if no selection, or no additional possible button,
                // disable plus and minus buttons
                plusBt.setDisable(true);
                minusBt.setDisable(true);
            } else {
                // enable plus and minus
                plusBt.setDisable(false);
                minusBt.setDisable(false);
            }
        }

        @SuppressWarnings("unused")
        protected void disablePlusButton(boolean disable) {
            plusBt.setDisable(disable);
        }

        @SuppressWarnings("unused")
        protected void disableMinusButton(boolean disable) {
            minusBt.setDisable(disable);
        }

        private StringConverter<ButtonType> getButtonTypeConverter() {
            Map<ButtonType, String> predefinedButtons = ButtonTypePropertyMetadata.getButtonTypeMap();
            return new StringConverter<ButtonType>() {

                @Override
                public String toString(ButtonType buttonType) {
                    return predefinedButtons.get(buttonType);
                }

                @Override
                public ButtonType fromString(String buttonName) {
                    for (Entry<ButtonType, String> entry : predefinedButtons.entrySet()) {
                        if (entry.getValue().equals(buttonName)) {
                            return entry.getKey();
                        }
                    }
                    assert false;
                    return null;
                }
            };
        }

    }
}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/library/maven/MavenRepositorySystem.java<|end_filename|>
/*
 * Copyright (c) 2016, Gluon and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation and Gluon nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.kit.editor.panel.library.maven;

import com.oracle.javafx.scenebuilder.kit.editor.panel.library.maven.repository.Repository;
import com.oracle.javafx.scenebuilder.kit.editor.panel.library.maven.preset.MavenPresets;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import com.oracle.javafx.scenebuilder.kit.preferences.RepositoryPreferences;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.maven.repository.internal.MavenRepositorySystemUtils;
import org.codehaus.plexus.util.FileUtils;
import org.eclipse.aether.AbstractRepositoryListener;
import org.eclipse.aether.DefaultRepositorySystemSession;
import org.eclipse.aether.RepositoryEvent;
import org.eclipse.aether.RepositorySystem;
import org.eclipse.aether.artifact.Artifact;
import org.eclipse.aether.artifact.DefaultArtifact;
import org.eclipse.aether.collection.CollectRequest;
import org.eclipse.aether.connector.basic.BasicRepositoryConnectorFactory;
import org.eclipse.aether.graph.Dependency;
import org.eclipse.aether.graph.DependencyFilter;
import org.eclipse.aether.impl.DefaultServiceLocator;
import org.eclipse.aether.installation.InstallRequest;
import org.eclipse.aether.installation.InstallationException;
import org.eclipse.aether.metadata.DefaultMetadata;
import org.eclipse.aether.metadata.Metadata;
import org.eclipse.aether.repository.Authentication;
import org.eclipse.aether.repository.LocalRepository;
import org.eclipse.aether.repository.RemoteRepository;
import org.eclipse.aether.repository.RepositoryPolicy;
import org.eclipse.aether.resolution.ArtifactRequest;
import org.eclipse.aether.resolution.ArtifactResolutionException;
import org.eclipse.aether.resolution.ArtifactResult;
import org.eclipse.aether.resolution.DependencyRequest;
import org.eclipse.aether.resolution.DependencyResolutionException;
import org.eclipse.aether.resolution.VersionRangeRequest;
import org.eclipse.aether.resolution.VersionRangeResolutionException;
import org.eclipse.aether.resolution.VersionRangeResult;
import org.eclipse.aether.spi.connector.RepositoryConnectorFactory;
import org.eclipse.aether.spi.connector.transport.TransporterFactory;
import org.eclipse.aether.transfer.AbstractTransferListener;
import org.eclipse.aether.transfer.TransferEvent;
import org.eclipse.aether.transport.file.FileTransporterFactory;
import org.eclipse.aether.transport.http.HttpTransporterFactory;
import org.eclipse.aether.util.artifact.JavaScopes;
import org.eclipse.aether.util.filter.DependencyFilterUtils;
import org.eclipse.aether.util.repository.AuthenticationBuilder;
import org.eclipse.aether.version.Version;

public class MavenRepositorySystem {

    // TODO: Manage List of Repositories
    // TODO: Manage private repositories and credentials
    
    private RepositorySystem system;
    
    private DefaultRepositorySystemSession session;
    
    private LocalRepository localRepo;
    
    private VersionRangeResult rangeResult;
    
    private final boolean onlyReleases;
    
    private BasicRepositoryConnectorFactory basicRepositoryConnectorFactory;

    private final String userM2Repository;
    private final String tempM2Repository;
    private final RepositoryPreferences repositoryPreferences;
    
    public MavenRepositorySystem(boolean onlyReleases, String userM2Repository, String tempM2Repository,
                                 RepositoryPreferences repositoryPreferences) {
        this.onlyReleases = onlyReleases;
        this.userM2Repository = userM2Repository;
        this.tempM2Repository = tempM2Repository;
        this.repositoryPreferences = repositoryPreferences;
        initRepositorySystem();
    }
    
    private void initRepositorySystem() {
        DefaultServiceLocator locator = MavenRepositorySystemUtils.newServiceLocator();
        locator.addService(RepositoryConnectorFactory.class, BasicRepositoryConnectorFactory.class);
        locator.addService(TransporterFactory.class, FileTransporterFactory.class);
        locator.addService(TransporterFactory.class, HttpTransporterFactory.class);
        locator.setErrorHandler(new DefaultServiceLocator.ErrorHandler() {
            @Override
            public void serviceCreationFailed(Class<?> type, Class<?> impl, Throwable exception) {
                throw new RuntimeException(exception);
            }
        });
        
        basicRepositoryConnectorFactory = new BasicRepositoryConnectorFactory();
        basicRepositoryConnectorFactory.initService(locator);
        
        system = locator.getService(RepositorySystem.class);

        session = MavenRepositorySystemUtils.newSession();

        localRepo = new LocalRepository(new File(userM2Repository));
        session.setLocalRepositoryManager(system.newLocalRepositoryManager(session, localRepo));

        // TODO: log file transfers
        session.setTransferListener(new AbstractTransferListener() {
            @Override
            public void transferSucceeded(TransferEvent event) { }

            @Override
            public void transferFailed(TransferEvent event) { }
        });
        
        // TODO: Log repository changes
        session.setRepositoryListener(new AbstractRepositoryListener() {
            @Override
            public void artifactResolved(RepositoryEvent event) { }
        });
        
    }

    public RepositorySystem getRepositorySystem() {
        return system;
    }
    
    public DefaultRepositorySystemSession getRepositorySession() {
        return session;
    }
    
    public List<RemoteRepository> getRepositories() {
        final List<RemoteRepository> list = MavenPresets.getPresetRepositories().stream()
                .filter(r -> !onlyReleases ||
                        (onlyReleases && !r.getId().toUpperCase(Locale.ROOT).contains("SNAPSHOT")))
                .map(this::createRepository)
                .collect(Collectors.toList());
        list.addAll(repositoryPreferences.getRepositories().stream()
                .filter(r -> !onlyReleases ||
                        (onlyReleases && !r.getId().toUpperCase(Locale.ROOT).contains("SNAPSHOT")))
                .map(this::createRepository)
                .collect(Collectors.toList()));
        return list;
    }
    
    public RemoteRepository getRemoteRepository(Version version) {
        if (rangeResult == null || version == null) {
            return null;
        }
        return getRepositories()
                .stream()
                .filter(r -> r.getId().equals(rangeResult.getRepository(version).getId()))
                .findFirst()
                .orElse(new RemoteRepository
                        .Builder(MavenPresets.LOCAL, "default", session.getLocalRepository().getBasedir().getAbsolutePath())
                        .build());
    }
    
    public RemoteRepository getRemoteRepository(String name) {
        return getRepositories()
                .stream()
                .filter(r -> r.getId().equals(name))
                .findFirst()
                .orElse(new RemoteRepository
                        .Builder(MavenPresets.LOCAL, "default", session.getLocalRepository().getBasedir().getAbsolutePath())
                        .build());
    }
    
    public List<Version> findVersions(Artifact artifact) {
        VersionRangeRequest rangeRequest = new VersionRangeRequest();
        rangeRequest.setArtifact(artifact);
        rangeRequest.setRepositories(getRepositories());
        try {
            rangeResult = system.resolveVersionRange(session, rangeRequest);
            cleanMetadata(artifact);
            
            return rangeResult.getVersions();
        } catch (VersionRangeResolutionException ex) { } 
        return new ArrayList<>();
    }
    
    public Version findLatestVersion(Artifact artifact) {
        VersionRangeRequest rangeRequest = new VersionRangeRequest();
        rangeRequest.setArtifact(artifact);
        rangeRequest.setRepositories(getRepositories());
        try {
            rangeResult = system.resolveVersionRange(session, rangeRequest);
            cleanMetadata(artifact);
            return rangeResult.getVersions()
                    .stream()
                    .filter(v -> !v.toString().toLowerCase(Locale.ROOT).contains("snapshot"))
                    .sorted((v1, v2) -> v2.compareTo(v1))
                    .findFirst()
                    .orElse(null);
        } catch (VersionRangeResolutionException ex) { } 
        return null;
    }
    
    private void cleanMetadata(Artifact artifact) {
        final String path = localRepo.getBasedir().getAbsolutePath() + File.separator
                        + artifact.getGroupId().replaceAll("\\.", Matcher.quoteReplacement(File.separator)) + File.separator
                        + artifact.getArtifactId() + File.separator;
        final DefaultMetadata metadata = new DefaultMetadata("maven-metadata.xml", Metadata.Nature.RELEASE);
        getRepositories()
            .stream()
            .map(r -> session.getLocalRepositoryManager().getPathForRemoteMetadata(metadata, r, ""))
            .forEach(s -> {
                File file = new File(path + s);
                if (file.exists()) {
                    try {
                        Files.delete(file.toPath());
                        Files.delete(new File(path + s + ".sha1").toPath());
                    } catch (IOException ex) { }
                }
            });
    }
        
    public String resolveArtifacts(RemoteRepository remoteRepository, Artifact... artifact) {
        
        LocalRepository localTmpRepo = new LocalRepository(tempM2Repository);
        session.setLocalRepositoryManager(system.newLocalRepositoryManager(session, localTmpRepo));
        
        List<Artifact> artifacts = Stream.of(artifact)
                .map(a -> {
                    ArtifactRequest artifactRequest = new ArtifactRequest();
                    artifactRequest.setArtifact(a);
                    artifactRequest.setRepositories(remoteRepository == null ? getRepositories() : 
                            Arrays.asList(remoteRepository));
                    return artifactRequest;
                })
                .map(ar -> {
                    try {
                        ArtifactResult result = system.resolveArtifact(session, ar);
                        return result.getArtifact();
                    } catch (ArtifactResolutionException ex) { }
                    return null;
                })
                .filter(Objects::nonNull)
                .collect(Collectors.toList()); 
        
        session.setLocalRepositoryManager(system.newLocalRepositoryManager(session, localRepo));
        
        List<File> sha1Files = null;
        if (artifacts != null && !artifacts.isEmpty()) {
            sha1Files = artifacts.stream()
                .filter(a -> a.getFile() != null)
                .map(a -> new File(a.getFile().getAbsolutePath().concat(".sha1")))
                .collect(Collectors.toList());
        
            InstallRequest installRequest = new InstallRequest();
            installRequest.setArtifacts(artifacts);
            try {
                system.install(session, installRequest);
            } catch (InstallationException ex) { }
        } 

        // return path from local m2
        ArtifactRequest artifactRequest = new ArtifactRequest();
        artifactRequest.setArtifact(artifact[0]);
        String absolutePath = "";
        try {
            final File jarFile = system.resolveArtifact(session, artifactRequest).getArtifact().getFile();
            absolutePath = jarFile.getAbsolutePath();
            if (sha1Files != null) {
                sha1Files.stream()
                    .forEach(f -> {
                        try {
                            FileUtils.copyFile(f, new File(jarFile.getParent() + File.separator + f.getName()));
                        } catch (IOException ex) { }
                    });
            }
        } catch (ArtifactResolutionException ex) { }
        
        try {
            FileUtils.deleteDirectory(tempM2Repository);
        } catch (IOException ex) { }
        
        return absolutePath;
    }
        
    public String resolveDependencies(RemoteRepository remoteRepository, Artifact artifact) {
        DependencyFilter classpathFlter = DependencyFilterUtils.classpathFilter(JavaScopes.COMPILE);
        CollectRequest collectRequest = new CollectRequest();
        collectRequest.setRoot(new Dependency(artifact, JavaScopes.COMPILE));
        collectRequest.setRepositories(remoteRepository == null ? getRepositories() : 
                Arrays.asList(remoteRepository));

        DependencyRequest dependencyRequest = new DependencyRequest(collectRequest, classpathFlter);
        try {
            List<ArtifactResult> artifactResults = system.resolveDependencies(session, dependencyRequest)
                    .getArtifactResults();
            
            return artifactResults.stream()
                    .skip(1) // exclude jar itself
                    .map(a -> a.getArtifact().getFile().getAbsolutePath())
                    .collect(Collectors.joining(File.pathSeparator));
        } catch (DependencyResolutionException ex) { }
        return "";
    }
    
    private RemoteRepository createRepository(Repository repository) {
        Authentication auth = null;
        if (repository.getUser() != null && !repository.getUser().isEmpty() && 
            repository.getPassword() != null && !repository.getPassword().isEmpty()) {
            auth = new AuthenticationBuilder()
                    .addUsername(repository.getUser())
                    .addPassword(repository.getPassword())
                    .build();
        }
        
        final RemoteRepository repo = new RemoteRepository
                .Builder(repository.getId() , repository.getType(), repository.getURL())
                .setSnapshotPolicy(onlyReleases ? new RepositoryPolicy(false, null, null) : new RepositoryPolicy())
                .setAuthentication(auth)
                .build();
        return repo;
    }
    
    public String validateRepository(Repository repository) {
        RemoteRepository remoteRepository = createRepository(repository);
        
        ArtifactRequest artifactRequest = new ArtifactRequest();
        artifactRequest.setArtifact(new DefaultArtifact("test:test:1.0"));
        artifactRequest.setRepositories(Arrays.asList(remoteRepository));
        try {
            system.resolveArtifact(session, artifactRequest);
        } catch (ArtifactResolutionException ex) {
            final String rootCauseMessage = ExceptionUtils.getRootCauseMessage(ex);
            if (rootCauseMessage != null && !rootCauseMessage.contains("ArtifactNotFoundException")) {
                return rootCauseMessage;
            }
        }
        
        return "";
    }
}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/fxom/TransientObject.java<|end_filename|>
/*
 * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.kit.fxom;

import com.oracle.javafx.scenebuilder.kit.fxom.glue.GlueElement;
import com.oracle.javafx.scenebuilder.kit.metadata.util.PropertyName;
import java.util.ArrayList;
import java.util.List;
import javafx.beans.DefaultProperty;

/**
 *
 * 
 */
class TransientObject extends TransientNode {
    
    private final Class<?> declaredClass;
    private final String unknownClassName;
    private final GlueElement glueElement;
    private final List<FXOMProperty> properties = new ArrayList<>();
    private final List<FXOMObject> collectedItems = new ArrayList<>();
    private String fxRootType;
    
    public TransientObject(
            TransientNode parentNode, 
            Class<?> declaredClass, 
            GlueElement glueElement) {
        super(parentNode);
        
        assert declaredClass != null;
        assert glueElement != null;
        assert glueElement.getTagName().equals(PropertyName.makeClassFullName(declaredClass)) ||
                glueElement.getTagName().equals(declaredClass.getCanonicalName());
        
        this.declaredClass = declaredClass;
        this.unknownClassName = null;
        this.glueElement = glueElement;
    }

    public TransientObject(
            TransientNode parentNode, 
            String unknownClassName, 
            GlueElement glueElement) {
        super(parentNode);
        
        assert unknownClassName != null;
        assert glueElement != null;
        assert glueElement.getTagName().equals(unknownClassName);
        
        this.declaredClass = null;
        this.unknownClassName = unknownClassName;
        this.glueElement = glueElement;
    }

    public TransientObject(
            TransientNode parentNode, 
            GlueElement glueElement) {
        super(parentNode);
        
        assert glueElement != null;
        assert glueElement.getTagName().equals("fx:root");
        
        this.declaredClass = null;
        this.unknownClassName = null;
        this.glueElement = glueElement;
    }

    public List<FXOMProperty> getProperties() {
        return properties;
    }

    public List<FXOMObject> getCollectedItems() {
        return collectedItems;
    }

    public void setFxRootType(String fxRootType) {
        this.fxRootType = fxRootType;
    }
    
    
    public FXOMObject makeFxomObject(FXOMDocument fxomDocument) {
        final FXOMObject result;
        
        if (declaredClass != null) {
            assert getSceneGraphObject() != null;
            
            if (getSceneGraphObject() instanceof List) {
                assert properties.isEmpty();
                
                result = new FXOMCollection(fxomDocument, glueElement,
                                          declaredClass, getSceneGraphObject(), 
                                          collectedItems);
            } else {
                assert fxRootType == null;
                
                addDefaultProperty(fxomDocument, declaredClass);
                result = new FXOMInstance(fxomDocument, glueElement, 
                                          declaredClass, getSceneGraphObject(),
                                          properties);
            }
        } else if (unknownClassName != null) {
            // This is an unresolved instance
            assert glueElement.getTagName().equals(unknownClassName);
            assert fxRootType == null;
            result = new FXOMInstance(fxomDocument, glueElement, properties);
        } else {
            // This is an fx:root'ed instance
            assert glueElement.getTagName().equals("fx:root");
            assert fxRootType != null;

            final Class<?> rootClass = getSceneGraphObject().getClass();
            assert fxRootType.equals(rootClass.getName())
                    || fxRootType.equals(rootClass.getSimpleName());
            addDefaultProperty(fxomDocument, rootClass);
            result = new FXOMInstance(fxomDocument, glueElement, 
                                      rootClass, getSceneGraphObject(),
                                      properties);
        }

        if (result instanceof FXOMInstance) {
            final Class<?> declaredClassFromResult = ((FXOMInstance) result).getDeclaredClass();
            if (fxomDocument.getInitialDeclaredClasses() != null && 
                    ! fxomDocument.getInitialDeclaredClasses().contains(declaredClassFromResult)) {
                fxomDocument.getInitialDeclaredClasses().add(declaredClassFromResult);
            }
        }
        return result;
    }
    
    
    /*
     * Private
     */
    
    private void addDefaultProperty(FXOMDocument fxomDocument, Class<?> klass) {
        final DefaultProperty annotation = klass.getAnnotation(DefaultProperty.class);
        if ((annotation != null) && (collectedItems.size() >= 1)) {
            assert annotation.value() != null;
            final PropertyName defaultPropertyName = new PropertyName(annotation.value());
            createDefaultProperty(defaultPropertyName, fxomDocument);
        }
    }
    
    private void createDefaultProperty(PropertyName defaultName, FXOMDocument fxomDocument) {
        /*
         * From :
         * 
         *  <Pane>                          this.glueElement
         *      ...
         *      <Button text="B1" />        this.collectedItems.get(0).glueElement   //NOI18N
         *      <TextField />               this.collectedItems.get(1).glueElement
         *      <Label text="Label" />      this.collectedItems.get(2).glueElement   //NOI18N
         *      ...
         *  </Pane>
         * 
         * go to:
         * 
         *  <Pane>                          this.glueElement
         *      ...
         *      <children>                  syntheticElement
         *          <Button text="B1" />    this.collectedItems.get(0).glueElement   //NOI18N
         *          <TextField />           this.collectedItems.get(1).glueElement
         *          <Label text="Label" />  this.collectedItems.get(2).glueElement   //NOI18N
         *      </children>
         *      ...
         *  </Pane>
         *
         */
        
        final GlueElement propertyElement
                = new GlueElement(glueElement.getDocument(), 
                        defaultName.toString(),  glueElement);
        propertyElement.setSynthetic(true);
        propertyElement.addBefore(collectedItems.get(0).getGlueElement());
        
        for (FXOMObject item : collectedItems) {
            item.getGlueElement().addToParent(propertyElement);
        }
        
        final TransientProperty transientProperty 
                = new TransientProperty(this, defaultName, propertyElement);
        transientProperty.getValues().addAll(collectedItems);
        
        properties.add(transientProperty.makeFxomProperty(fxomDocument));
    }
}


<|start_filename|>app/src/main/java/com/oracle/javafx/scenebuilder/app/CssPanelMenuController.java<|end_filename|>
/*
 * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.app;

import com.oracle.javafx.scenebuilder.app.i18n.I18N;
import com.oracle.javafx.scenebuilder.kit.editor.panel.css.CssPanelController;
import javafx.scene.control.MenuItem;


/**
 *
 */
class CssPanelMenuController {
     private boolean showStyledOnly = false;
    private boolean splitDefaults = false;
    private final CssPanelController cssPanelController;

    public CssPanelMenuController(CssPanelController cssPanelController) {
        this.cssPanelController = cssPanelController;
    }

    public void viewRules() {
        cssPanelController.changeView(CssPanelController.View.RULES);
    }

    public void viewTable() {
        cssPanelController.changeView(CssPanelController.View.TABLE);
    }

    public void viewText() {
        cssPanelController.changeView(CssPanelController.View.TEXT);
    }

    public void copyStyleablePath() {
        cssPanelController.copyStyleablePath();
    }

    public void splitDefaultsAction(MenuItem cssPanelSplitDefaultsMi) {
        cssPanelController.splitDefaultsAction();
        splitDefaults = !splitDefaults;
        if (splitDefaults) {
            cssPanelSplitDefaultsMi.setText(I18N.getString("csspanel.defaults.join"));
        } else {
            cssPanelSplitDefaultsMi.setText(I18N.getString("csspanel.defaults.split"));
        }
    }

    public void showStyledOnly(MenuItem cssPanelShowStyledOnlyMi) {
        cssPanelController.showStyledOnly();
        showStyledOnly = !showStyledOnly;
        if (showStyledOnly) {
            cssPanelShowStyledOnlyMi.setText(I18N.getString("csspanel.show.default.values"));
        } else {
            cssPanelShowStyledOnlyMi.setText(I18N.getString("csspanel.hide.default.values"));
        }
    }

}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/content/guides/HorizontalSegment.java<|end_filename|>
/*
 * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */

package com.oracle.javafx.scenebuilder.kit.editor.panel.content.guides;

/**
 *
 */
class HorizontalSegment extends AbstractSegment {
    
    private final double x1;
    private final double x2;
    private final double y;
    private final double length;
    
    public HorizontalSegment(double x1, double x2, double y) {
        this.x1 = x1;
        this.x2 = x2;
        this.y = y;
        this.length = Math.abs(x2 - x1);
    }

    /*
     * AbstractSegment
     */

    @Override
    public double getX1() {
        return x1;
    }

    @Override
    public double getX2() {
        return x2;
    }

    @Override
    public double getY1() {
        return y;
    }

    @Override
    public double getY2() {
        return y;
    }

    @Override
    public double getLength() {
        return length;
    }

    /*
     * Object
     */
    @Override
    public int hashCode() {
        int hash = 7;
        hash = 89 * hash + (int) (Double.doubleToLongBits(this.x1) ^ (Double.doubleToLongBits(this.x1) >>> 32));
        hash = 89 * hash + (int) (Double.doubleToLongBits(this.x2) ^ (Double.doubleToLongBits(this.x2) >>> 32));
        hash = 89 * hash + (int) (Double.doubleToLongBits(this.y) ^ (Double.doubleToLongBits(this.y) >>> 32));
        return hash;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final HorizontalSegment other = (HorizontalSegment) obj;
        if (Double.doubleToLongBits(this.x1) != Double.doubleToLongBits(other.x1)) {
            return false;
        }
        if (Double.doubleToLongBits(this.x2) != Double.doubleToLongBits(other.x2)) {
            return false;
        }
        if (Double.doubleToLongBits(this.y) != Double.doubleToLongBits(other.y)) {
            return false;
        }
        return true;
    }


    /*
     * Comparable
     */
    @Override
    public int compareTo(AbstractSegment o) {
        assert o != null;
        return Double.compare(this.length, o.getLength());
    }
}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/job/gridpane/v2/SpanJob.java<|end_filename|>
/*
 * Copyright (c) 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.kit.editor.job.gridpane.v2;

import com.oracle.javafx.scenebuilder.kit.editor.EditorController;
import com.oracle.javafx.scenebuilder.kit.editor.EditorController.EditAction;
import com.oracle.javafx.scenebuilder.kit.i18n.I18N;
import com.oracle.javafx.scenebuilder.kit.editor.job.BatchDocumentJob;
import com.oracle.javafx.scenebuilder.kit.editor.job.Job;
import com.oracle.javafx.scenebuilder.kit.editor.job.atomic.ModifyObjectJob;
import com.oracle.javafx.scenebuilder.kit.editor.selection.AbstractSelectionGroup;
import com.oracle.javafx.scenebuilder.kit.editor.selection.ObjectSelectionGroup;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMInstance;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMObject;
import com.oracle.javafx.scenebuilder.kit.metadata.Metadata;
import com.oracle.javafx.scenebuilder.kit.metadata.property.ValuePropertyMetadata;
import com.oracle.javafx.scenebuilder.kit.metadata.util.DesignHierarchyMask;
import com.oracle.javafx.scenebuilder.kit.metadata.util.PropertyName;
import java.util.ArrayList;
import java.util.List;
import javafx.scene.layout.GridPane;

/**
 * Job used to support Modify > GridPane > Increase/Decrease Row/Column span.
 * This job is multi-selection proof: it will do something on all items of the
 * selection if and only if required conditions are met by all of them.
 */
public class SpanJob extends BatchDocumentJob {
    private final EditAction editAction;

    public SpanJob(EditorController editorController, EditAction editAction) {
        super(editorController);
        this.editAction = editAction;
    }

    @Override
    protected List<Job> makeSubJobs() {
        final List<Job> jobList = new ArrayList<>();
        final AbstractSelectionGroup selectionGroup = getEditorController().getSelection().getGroup();
        
        // Do we have an asset selected which is a standard one (not a grid) ?
        if (selectionGroup instanceof ObjectSelectionGroup) {
            // Is that asset enclosed in a grid ?
            if (selectionGroup.getAncestor() != null
                    && selectionGroup.getAncestor().getSceneGraphObject() instanceof GridPane) {
                DesignHierarchyMask gridDHM = new DesignHierarchyMask(selectionGroup.getAncestor());
                int columnCount = gridDHM.getColumnsSize();
                int rowCount = gridDHM.getRowsSize();
                List<FXOMObject> items = ((ObjectSelectionGroup)selectionGroup).getSortedItems();
                
                // Create a job for all items then check each is executable.
                // As soon as one is not executable the job list is made empty
                // so that no change will be performed by the job, eventually.
                for (FXOMObject fxomObject : items) {
                    if (fxomObject instanceof FXOMInstance) {
                        Job job = createJob((FXOMInstance)fxomObject, columnCount, rowCount);

                        if (job.isExecutable()) {
                            jobList.add(job);
                        } else {
                            jobList.clear();
                            break;
                        }
                    }
                }
            }
        }
        
        return jobList;
    }

    @Override
    protected String makeDescription() {
        String description = ""; //NOI18N
        
        switch (editAction) {
            default:
            case DECREASE_COLUMN_SPAN:
                description = I18N.getString("job.decrease.column.span");
                break;
            case INCREASE_COLUMN_SPAN:
                description = I18N.getString("job.increase.column.span");
                break;
            case DECREASE_ROW_SPAN:
                description = I18N.getString("job.decrease.row.span");
                break;
            case INCREASE_ROW_SPAN:
                description = I18N.getString("job.increase.row.span");
                break;
        }
        
        assert ! description.isEmpty();
        return description;
    }
    
    private Job createJob(FXOMInstance candidate, int columnCount, int rowCount) {
        PropertyName propName = null;
        int newSpan = 1;
        
        switch (editAction) {
            default:
            case DECREASE_COLUMN_SPAN:
                newSpan = getNewSpan(TREND.DECREASE,
                        getValue(PROPERTY.COLUMN_INDEX, candidate),
                        getValue(PROPERTY.COLUMN_SPAN, candidate), columnCount);
                propName = new PropertyName(getName(PROPERTY.COLUMN_SPAN), GridPane.class);
                break;
            case INCREASE_COLUMN_SPAN:
                newSpan = getNewSpan(TREND.INCREASE,
                        getValue(PROPERTY.COLUMN_INDEX, candidate),
                        getValue(PROPERTY.COLUMN_SPAN, candidate), columnCount);
                propName = new PropertyName(getName(PROPERTY.COLUMN_SPAN), GridPane.class);
                break;
            case DECREASE_ROW_SPAN:
                newSpan = getNewSpan(TREND.DECREASE,
                        getValue(PROPERTY.ROW_INDEX, candidate),
                        getValue(PROPERTY.ROW_SPAN, candidate), rowCount);
                propName = new PropertyName(getName(PROPERTY.ROW_SPAN), GridPane.class);
                break;
            case INCREASE_ROW_SPAN:
                newSpan = getNewSpan(TREND.INCREASE,
                        getValue(PROPERTY.ROW_INDEX, candidate),
                        getValue(PROPERTY.ROW_SPAN, candidate), rowCount);
                propName = new PropertyName(getName(PROPERTY.ROW_SPAN), GridPane.class);
                break;
        }

        final ValuePropertyMetadata vpm
                = Metadata.getMetadata().queryValueProperty(candidate, propName);
        final ModifyObjectJob columnSpanJob = new ModifyObjectJob(
                candidate, vpm, newSpan, getEditorController());

        return columnSpanJob;
    }
    
    // May return a value identical to given span one.
    private int getNewSpan(TREND trend, int index, int span, int count) {
        int newSpan = span;
        switch(trend) {
            case DECREASE:
                if (span > 1) {
                    newSpan = span - 1;
                }
                break;
            case INCREASE:
                if (index+1 < count && span < count) {
                    newSpan = span + 1;
                }
                break;
        }
        
        return newSpan;
    }

    private enum TREND {DECREASE, INCREASE};
    private enum PROPERTY {COLUMN_INDEX, COLUMN_SPAN, ROW_INDEX, ROW_SPAN};
    
    private int getValue(PROPERTY property, FXOMInstance candidate) {
        String propertyName = getName(property);
        final PropertyName propName = new PropertyName(propertyName, GridPane.class);
        final ValuePropertyMetadata vpm
                = Metadata.getMetadata().queryValueProperty(candidate, propName);
        Object value = vpm.getValueObject(candidate);
        
        // Span value can be null
        if (value == null && (property == PROPERTY.COLUMN_SPAN || property == PROPERTY.ROW_SPAN)) {
            value = Integer.valueOf(1);
        }
        assert value instanceof Integer;
        
        return (Integer)value;
    }
    
    private String getName(PROPERTY property) {
        String propertyName = ""; //NOI18N
        
        switch (property) {
            case COLUMN_INDEX:
                propertyName = "columnIndex"; //NOI18N
                break;
            case COLUMN_SPAN:
                propertyName = "columnSpan"; //NOI18N
                break;
            case ROW_INDEX:
                propertyName = "rowIndex"; //NOI18N
                break;
            case ROW_SPAN:
                propertyName = "rowSpan"; //NOI18N
                break;
        }
        
        assert ! propertyName.isEmpty();
        return propertyName;
    }
    
}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/preferences/MavenPreferences.java<|end_filename|>
/*
 * Copyright (c) 2016, Gluon and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation and Gluon nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.kit.preferences;

import com.oracle.javafx.scenebuilder.kit.editor.panel.library.maven.MavenArtifact;
import com.oracle.javafx.scenebuilder.kit.preferences.PreferencesRecordArtifact;

import java.io.File;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class MavenPreferences {

    /***************************************************************************
     *                                                                         *
     * Instance fields                                                         *
     *                                                                         *
     **************************************************************************/

    private final Map<String, PreferencesRecordArtifact> recordArtifacts;

    /***************************************************************************
     *                                                                         *
     * Support Classes                                                         *
     *                                                                         *
     **************************************************************************/

    public MavenPreferences() {
        this.recordArtifacts = new HashMap<>();
    }

    /***************************************************************************
     *                                                                         *
     * Methods                                                                 *
     *                                                                         *
     **************************************************************************/

    public PreferencesRecordArtifact getRecordArtifact(String coordinates) {
        return recordArtifacts.get(coordinates);
    }
    
    public void addRecordArtifact(String key, PreferencesRecordArtifact object) {
        recordArtifacts.put(key, object);
    }
    
    public void removeRecordArtifact(String coordinates) {
        recordArtifacts.remove(coordinates);
    }
    
    /*
     * Single Artifact
     */
    
    private String getArtifactJarPath(MavenArtifact artifact) {
        return recordArtifacts.get(artifact.getCoordinates()).getPath();
    }
    
    private List<String> getArtifactJarDependencies(MavenArtifact artifact) {
        String dep = recordArtifacts.get(artifact.getCoordinates()).getMavenArtifact().getDependencies();
        if (dep != null && !dep.isEmpty()) {
                return Stream.of(dep.split(File.pathSeparator)).collect(Collectors.toList());
        } 
        return new ArrayList<>();
    }
    
    public File getArtifactFile(MavenArtifact artifact) {
        String path = getArtifactJarPath(artifact);
        if (path != null && !path.isEmpty()) {
            File file = new File(path);
            if (file.exists()) {
                return file;
            }
        }
        return null;
    }
    
    public List<File> getArtifactFileWithDependencies(MavenArtifact artifact) {
        String path = getArtifactJarPath(artifact);
        if (path != null && !path.isEmpty()) {
        
            List<File> jarPaths = new ArrayList<>();
            jarPaths.add(getArtifactFile(artifact));
            
            jarPaths.addAll(getArtifactJarDependencies(artifact)
                    .stream()
                    .filter(d -> d != null && !d.isEmpty())
                    .map(File::new)
                    .filter(File::exists)
                    .collect(Collectors.toList()));
            return jarPaths;
        }
        return null;
    }
    
    public List<String> getArtifactFilter(MavenArtifact artifact) {
        String filter = recordArtifacts.get(artifact.getCoordinates()).getMavenArtifact().getFilter();
        if (filter != null && !filter.isEmpty()) {
                return Stream.of(filter.split(File.pathSeparator)).collect(Collectors.toList());
        } 
        return new ArrayList<>();
    }
    
    /*
     * All Artifacts 
     */
    
    private List<String> getArtifactsJarsPaths() {
        return recordArtifacts.values()
                .stream()
                .map(PreferencesRecordArtifact::getPath)
                .distinct()
                .collect(Collectors.toList());
    }
    
    private List<String> getArtifactsJarsDependencies() {
        return recordArtifacts.values()
                .stream()
                .map(p -> p.getMavenArtifact().getDependencies())
                .filter(d -> d != null && !d.isEmpty())
                .flatMap(d -> Stream.of(d.split(File.pathSeparator)))
                .distinct()
                .collect(Collectors.toList());
    }
    
    public List<File> getArtifactsFiles() {
        return getArtifactsJarsPaths().stream()
                .filter(s -> s != null && !s.isEmpty())
                .map(File::new)
                .filter(File::exists)
                .collect(Collectors.toList());
    }
    
    public List<File> getArtifactsFilesWithDependencies() {
        List<String> jarsPaths = getArtifactsJarsPaths();
        jarsPaths.addAll(getArtifactsJarsDependencies());
        return jarsPaths.stream()
                .filter(s -> s != null && !s.isEmpty())
                .map(File::new)
                .filter(File::exists)
                .collect(Collectors.toList());
    }
    
    public List<Path> getArtifactsPaths() {
        return getArtifactsFiles()
                .stream()
                .map(File::toPath)
                .collect(Collectors.toList());
    }
    public List<Path> getArtifactsPathsWithDependencies() {
        return getArtifactsFilesWithDependencies()
                .stream()
                .map(File::toPath)
                .collect(Collectors.toList());
    }
    
    public List<String> getArtifactsFilter() {
        return recordArtifacts.values()
                .stream()
                .map(p -> p.getMavenArtifact().getFilter())
                .filter(f -> f != null && !f.isEmpty())
                .flatMap(f -> Stream.of(f.split(File.pathSeparator)))
                .distinct()
                .collect(Collectors.toList());
    }
    
    public List<String> getArtifactsCoordinates() {
        return recordArtifacts.entrySet()
                .stream()
                .map(Map.Entry::getKey)
                .collect(Collectors.toList());
    }
    
}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/util/MathUtils.java<|end_filename|>
/*
 * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.kit.util;

/**
 *
 */
public class MathUtils {
    
    /**
     * Returns true if the two specified double values are approximately equals.
     * @param v1 first double to check
     * @param v2 second double to check
     * @return true if the two specified double values are approximately equals.
     */
    public static boolean equals(double v1, double v2) {
        return Math.abs(v1 - v2) < 0.0000001;
    }
    
    /**
     * Returns true if the two specified double values are equal with
     * the specified tolerance.
     * 
     * @param v1 first double to check
     * @param v2 second double to check
     * @param tolerance a strictly positive value
     * @return true if the two specified double values are approximately equals.
     */
    public static boolean equals(double v1, double v2, double tolerance) {
        assert tolerance > 0.0;
        return Math.abs(v1 - v2) <= tolerance;
    }
    
    
    /**
     * Return the distance between two points.
     * @param x1 x coordinate of the first point
     * @param y1 y coordinate of the first point
     * @param x2 x coordinate of the second point
     * @param y2 x coordinate of the second point
     * @return the distance between (x1, y1) and (x2, y2)
     */
    public static double distance(double x1, double y1, double x2, double y2) {
        final double dx = x2 - x1;
        final double dy = y2 - y1;
        return Math.sqrt(dx * dx + dy * dy);
    }
}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/drag/target/ContainerXYDropTarget.java<|end_filename|>
/*
 * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.kit.editor.drag.target;

import com.oracle.javafx.scenebuilder.kit.editor.EditorController;
import com.oracle.javafx.scenebuilder.kit.editor.drag.source.AbstractDragSource;
import com.oracle.javafx.scenebuilder.kit.editor.job.BatchJob;
import com.oracle.javafx.scenebuilder.kit.editor.job.InsertAsSubComponentJob;
import com.oracle.javafx.scenebuilder.kit.editor.job.Job;
import com.oracle.javafx.scenebuilder.kit.editor.job.atomic.RelocateNodeJob;
import com.oracle.javafx.scenebuilder.kit.editor.job.atomic.RemoveObjectJob;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMInstance;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMIntrinsic;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMObject;
import com.oracle.javafx.scenebuilder.kit.metadata.util.DesignHierarchyMask;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import javafx.geometry.Bounds;
import javafx.geometry.Point2D;
import javafx.scene.Node;
import javafx.scene.Parent;

/**
 *
 */
public class ContainerXYDropTarget extends AbstractDropTarget {

    private final FXOMInstance targetContainer;
    private final double sceneX;
    private final double sceneY;

    public ContainerXYDropTarget(FXOMInstance targetContainer, double sceneX, double sceneY) {
        assert targetContainer != null;
        assert targetContainer.getSceneGraphObject() instanceof Parent;
        this.targetContainer = targetContainer;
        this.sceneX = sceneX;
        this.sceneY = sceneY;
    }

    public double getSceneX() {
        return sceneX;
    }

    public double getSceneY() {
        return sceneY;
    }
    

    /*
     * AbstractDropTarget
     */
    @Override
    public FXOMObject getTargetObject() {
        return targetContainer;
    }

    @Override
    public boolean acceptDragSource(AbstractDragSource dragSource) {
        assert dragSource != null;
        
        final boolean result;
        if (dragSource.getDraggedObjects().isEmpty()) {
            result = false;
        } else {
            boolean containsIntrinsic = false;
            for (FXOMObject draggedObject : dragSource.getDraggedObjects()) {
                if (draggedObject instanceof FXOMIntrinsic) {
                    containsIntrinsic = true;
                }
            }
            if (containsIntrinsic) {
                result = false;
            } else {
                final DesignHierarchyMask m = new DesignHierarchyMask(targetContainer);
                result = m.isAcceptingSubComponent(dragSource.getDraggedObjects());
            }
        }
        
        return result;
    }

    @Override
    public Job makeDropJob(AbstractDragSource dragSource, EditorController editorController) {
        assert acceptDragSource(dragSource);
        assert editorController != null;
        
        
        final List<FXOMObject> draggedObjects = dragSource.getDraggedObjects();
        final FXOMObject hitObject = dragSource.getHitObject();
        final double hitX = dragSource.getHitX();
        final double hitY = dragSource.getHitY();
        final FXOMObject currentParent = hitObject.getParentObject();
        
        final BatchJob result;
        if (currentParent == targetContainer) {
            // It's a relocating job
            assert hitObject.getSceneGraphObject() instanceof Node;
            assert hitObject instanceof FXOMInstance;
            
            final boolean shouldRefreshSceneGraph = false;
            result = new BatchJob(editorController, 
                    shouldRefreshSceneGraph, dragSource.makeDropJobDescription());
            
            final Point2D dxy = computeRelocationDXY((FXOMInstance) hitObject, hitX, hitY);
            for (FXOMObject draggedObject : dragSource.getDraggedObjects()) {
                assert draggedObject.getSceneGraphObject() instanceof Node;
                assert draggedObject instanceof FXOMInstance;
                final Node draggedNode = (Node) draggedObject.getSceneGraphObject();
                final double newLayoutX = Math.round(draggedNode.getLayoutX() + dxy.getX());
                final double newLayoutY = Math.round(draggedNode.getLayoutY() + dxy.getY());
                result.addSubJob(new RelocateNodeJob((FXOMInstance)draggedObject, 
                        newLayoutX, newLayoutY, editorController));
            }
        } else {
            // It's a reparening job :
            //  - remove drag source objects from their current parent (if any)
            //  - add drag source objects to this drop target
            //  - relocate the drag source objects
            //  - adjust toggle group declaration (if any)
            
            final boolean shouldRefreshSceneGraph = true;
            result = new BatchJob(editorController, 
                    shouldRefreshSceneGraph, dragSource.makeDropJobDescription());
            
            if (currentParent != null) {
                for (FXOMObject draggedObject : draggedObjects) {
                    result.addSubJob(new RemoveObjectJob(draggedObject,
                            editorController));
                }
            }
            for (FXOMObject draggedObject : draggedObjects) {
                result.addSubJob(new InsertAsSubComponentJob(
                        draggedObject, targetContainer, -1, editorController));
            }
            
            // Computes dragged object positions relatively to hitObject
            assert hitObject.getSceneGraphObject() instanceof Node;
            final Node hitNode = (Node) hitObject.getSceneGraphObject();
            final double layoutX0 = hitNode.getLayoutX();
            final double layoutY0 = hitNode.getLayoutY();
            final Map<FXOMObject, Point2D> layoutDXY = new HashMap<>();
            for (FXOMObject draggedObject : draggedObjects) {
                assert draggedObject.getSceneGraphObject() instanceof Node;
                final Node draggedNode = (Node) draggedObject.getSceneGraphObject();
                final double layoutDX = draggedNode.getLayoutX() - layoutX0;
                final double layoutDY = draggedNode.getLayoutY() - layoutY0;
                layoutDXY.put(draggedObject, new Point2D(layoutDX, layoutDY));
            }
            
            final Parent targetParent = (Parent)targetContainer.getSceneGraphObject();
            final Point2D targetCenter = targetParent.sceneToLocal(sceneX, sceneY, true /* rootScene */);
            final Bounds layoutBounds = hitNode.getLayoutBounds();
            final Point2D currentOrigin = hitNode.localToParent(0.0, 0.0);
            final Point2D currentCenter = hitNode.localToParent(
                    (layoutBounds.getMinX() + layoutBounds.getMaxX()) / 2.0,
                    (layoutBounds.getMinY() + layoutBounds.getMaxY()) / 2.0);
            final double currentDX = currentOrigin.getX() - currentCenter.getX();
            final double currentDY = currentOrigin.getY() - currentCenter.getY();
            final double targetOriginX = targetCenter.getX() + currentDX;
            final double targetOriginY = targetCenter.getY() + currentDY;
            
            for (FXOMObject draggedObject : draggedObjects) {
                assert draggedObject instanceof FXOMInstance;
                final Point2D dxy = layoutDXY.get(draggedObject);
                assert dxy != null;
                final double newLayoutX = Math.round(targetOriginX + dxy.getX());
                final double newLayoutY = Math.round(targetOriginY + dxy.getY());
                result.addSubJob(new RelocateNodeJob((FXOMInstance)draggedObject, 
                        newLayoutX, newLayoutY, editorController));
            }
        }
        
        assert result.isExecutable();
        
        return result;
    }
    
    @Override
    public boolean isSelectRequiredAfterDrop() {
        return true;
    }
    
    /*
     * Objects
     */
    @Override
    public int hashCode() {
        int hash = 7;
        hash = 53 * hash + Objects.hashCode(this.targetContainer);
        hash = 53 * hash + (int) (Double.doubleToLongBits(this.sceneX) ^ (Double.doubleToLongBits(this.sceneX) >>> 32));
        hash = 53 * hash + (int) (Double.doubleToLongBits(this.sceneY) ^ (Double.doubleToLongBits(this.sceneY) >>> 32));
        return hash;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final ContainerXYDropTarget other = (ContainerXYDropTarget) obj;
        if (!Objects.equals(this.targetContainer, other.targetContainer)) {
            return false;
        }
        if (Double.doubleToLongBits(this.sceneX) != Double.doubleToLongBits(other.sceneX)) {
            return false;
        }
        if (Double.doubleToLongBits(this.sceneY) != Double.doubleToLongBits(other.sceneY)) {
            return false;
        }
        return true;
    }

    @Override
    public String toString() {
        return "ContainerXYDropTarget{" + "targetContainer=" + targetContainer + ", sceneX=" + sceneX + ", sceneY=" + sceneY + '}'; //NOI18N
    }
    
    
    /*
     * Private
     */
    
    private Point2D computeRelocationDXY(FXOMInstance hitObject, double hitX, double hitY) {
        assert hitObject != null;
        assert hitObject.getSceneGraphObject() instanceof Node;
        
        /*
         * Converts (hitX, hitY) in hitObject parent coordinate space.
         */
        final Node sceneGraphNode = (Node)hitObject.getSceneGraphObject();
        final Point2D currentHit = sceneGraphNode.localToParent(hitX, hitY);
        
        /*
         * Computes drop target location in hitObject parent coordinate space
         */
        final Parent sceneGraphParent = sceneGraphNode.getParent();
        final Point2D newHit = sceneGraphParent.sceneToLocal(sceneX, sceneY, true /* rootScene */);
        
        final double dx = newHit.getX() - currentHit.getX();
        final double dy = newHit.getY() - currentHit.getY();
        return new Point2D(dx, dy);
    }
}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/library/util/ExplorerBase.java<|end_filename|>
/*
 * Copyright (c) 2020, Gluon and/or its affiliates.
 * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.kit.library.util;

import com.oracle.javafx.scenebuilder.kit.editor.EditorPlatform;
import com.oracle.javafx.scenebuilder.kit.library.BuiltinLibrary;
import javafx.fxml.FXMLLoader;
import javafx.scene.Node;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.lang.reflect.Modifier;
import java.nio.charset.Charset;

abstract class ExplorerBase {

    static Object instantiateWithFXMLLoader(Class<?> klass, ClassLoader classLoader) throws IOException {
        Object result;

        final String fxmlText = BuiltinLibrary.makeFxmlText(klass);
        final byte[] fxmlBytes = fxmlText.getBytes(Charset.forName("UTF-8")); //NOI18N

        final FXMLLoader fxmlLoader = new FXMLLoader();
        try {
            fxmlLoader.setClassLoader(classLoader);
            result = fxmlLoader.load(new ByteArrayInputStream(fxmlBytes));
        } catch(IOException x) {
            throw x;
        } catch(RuntimeException|Error x) {
            throw new IOException(x);
        }

        return result;
    }

    String makeClassName(String entryName, String separator) {
        final String result;

        if (! entryName.endsWith(".class")) { //NOI18N
            result = null;
        } else if (entryName.contains("$")) { //NOI18N
            // We skip inner classes for now
            result = null;
        } else {
            final int endIndex = entryName.length()-6; // ".class" -> 6 //NOI18N
            result = entryName.substring(0, endIndex).replace(separator, "."); //NOI18N
        }

        return result;
    }

    JarReportEntry exploreEntry(String entryName, ClassLoader classLoader, String className) {
        JarReportEntry.Status status;
        Throwable entryException;
        Class<?> entryClass = null;

        // Filtering out what starts with com.javafx. is bound to DTL-6378.
        if (className == null || className.startsWith("java.") //NOI18N
                || className.startsWith("javax.") || className.startsWith("javafx.") //NOI18N
                || className.startsWith("com.oracle.javafx.scenebuilder.") //NOI18N
                || className.startsWith("com.javafx.")
                || className.startsWith("module-info")
                || className.startsWith(EditorPlatform.GLUON_PACKAGE)) { //NOI18N
            status = JarReportEntry.Status.IGNORED;
            entryClass = null;
            entryException = null;
        } else {
            try {
                // Some reading explaining why using Class.forName is not appropriate:
                // http://blog.osgi.org/2011/05/what-you-should-know-about-class.html
                // http://blog.bjhargrave.com/2007/09/classforname-caches-defined-class-in.html
                // http://stackoverflow.com/questions/8100376/class-forname-vs-classloader-loadclass-which-to-use-for-dynamic-loading
                entryClass = classLoader.loadClass(className); // Note: static intializers of entryClass are not run, this doesn't seem to be an issue

                if (Modifier.isAbstract(entryClass.getModifiers())
                        || !Node.class.isAssignableFrom(entryClass)) {
                    status = JarReportEntry.Status.IGNORED;
                    entryClass = null;
                    entryException = null;
                } else {
                    instantiateWithFXMLLoader(entryClass, classLoader);
                    status = JarReportEntry.Status.OK;
                    entryException = null;
                }
            } catch (RuntimeException | IOException x) {
                status = JarReportEntry.Status.CANNOT_INSTANTIATE;
                entryException = x;
            } catch (Error | ClassNotFoundException x) {
                status = JarReportEntry.Status.CANNOT_LOAD;
                entryClass = null;
                entryException = x;
            }
        }

        return new JarReportEntry(entryName, status, entryException, entryClass, className);
    }
}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/content/util/DistanceUtils.java<|end_filename|>
/*
 * Copyright (c) 2018, Gluon and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation and Gluon nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */

package com.oracle.javafx.scenebuilder.kit.editor.panel.content.util;

import javafx.geometry.Point2D;
import javafx.scene.shape.Line;

public class DistanceUtils {

    /**
     * Calculates distance from point to line
     * 
     * @param point target point
     * @param line target line
     * @return distance
     */
    public static double getDistFromPointToLine(Point2D point, Line line) {
        double x = point.getX();
        double y = point.getY();
        
        double x0 = line.getStartX();
        double y0 = line.getStartY();
        double x1 = line.getEndX();
        double y1 = line.getEndY();
        
        double dot0 = dot(x - x0, y - y0, x1 - x0, y1 - y0);
        if (dot0 < 0) {
            return dist(x, y, x0, y0);
        }
        
        double dot1 = dot(x - x1, y - y1, x0 - x1, y0 - y1);
        if (dot1 < 0) {
            return dist(x, y, x1, y1);
        }
        
        return Math.abs((y0 - y1) * x + (x1 - x0) * y + (x0 * y1 - x1 * y0)) / dist(x0, y0, x1, y1);
    }
    
    private static double dot(double x1, double y1, double x2, double y2) {
        return x1 * x2 + y1 * y2;
    }
    
    private static double dist(double x1, double y1, double x2, double y2) {
        return Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2));
    }
    
}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/job/gridpane/AddColumnJob.java<|end_filename|>
/*
 * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.kit.editor.job.gridpane;

import com.oracle.javafx.scenebuilder.kit.editor.EditorController;
import com.oracle.javafx.scenebuilder.kit.editor.job.BatchSelectionJob;
import com.oracle.javafx.scenebuilder.kit.editor.job.Job;
import com.oracle.javafx.scenebuilder.kit.editor.job.gridpane.GridPaneJobUtils.Position;
import com.oracle.javafx.scenebuilder.kit.editor.selection.AbstractSelectionGroup;
import com.oracle.javafx.scenebuilder.kit.editor.selection.GridSelectionGroup;
import com.oracle.javafx.scenebuilder.kit.editor.selection.GridSelectionGroup.Type;
import com.oracle.javafx.scenebuilder.kit.editor.selection.ObjectSelectionGroup;
import com.oracle.javafx.scenebuilder.kit.editor.selection.Selection;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMInstance;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMObject;
import com.oracle.javafx.scenebuilder.kit.metadata.util.DesignHierarchyMask;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * Job invoked when adding columns.
 *
 * This job handles multi-selection as follows :
 * - if multiple GridPanes are selected, no column can be selected.
 * We add the new column to each GP, either at first position (add before)
 * or last position (add after).
 * - if multiple columns are selected, a single GridPane is selected.
 * We add new columns for each selected column, either before or after.
 *
 */
public class AddColumnJob extends BatchSelectionJob {

    // Key = target GridPane instance
    // Value = set of target column indexes for this GridPane
    private final Map<FXOMObject, Set<Integer>> targetGridPanes = new HashMap<>();
    private final Position position;

    public AddColumnJob(EditorController editorController, Position position) {
        super(editorController);
        assert position == Position.BEFORE || position == Position.AFTER;
        this.position = position;
    }
    
    @Override
    protected List<Job> makeSubJobs() {
        final List<Job> result = new ArrayList<>();

        if (GridPaneJobUtils.canPerformAdd(getEditorController())) {

            // Populate the target GridPane map
            assert targetGridPanes.isEmpty() == true;
            final List<FXOMObject> objectList
                    = GridPaneJobUtils.getTargetGridPanes(getEditorController());
            for (FXOMObject object : objectList) {
                final Set<Integer> indexList
                        = getTargetColumnIndexes(getEditorController(), object);
                targetGridPanes.put(object, indexList);
            }

            // Add sub jobs
            // First add the new column constraints
            final Job addConstraints = new AddColumnConstraintsJob(
                    getEditorController(), position, targetGridPanes);
            result.add(addConstraints);
            // Then move the column content
            result.addAll(moveColumnContent());
        }
        return result;
    }

    @Override
    protected String makeDescription() {
        return "Add Column " + position.name(); //NOI18N
    }

    @Override
    protected AbstractSelectionGroup getNewSelectionGroup() {
        final AbstractSelectionGroup asg;
        // Update new selection :
        // - if there is more than 1 GridPane, we select the GridPane instances
        // - if there is a single GridPane, we select the added columns
        if (targetGridPanes.size() > 1) {
            Set<FXOMObject> objects = targetGridPanes.keySet();
            asg = new ObjectSelectionGroup(objects, objects.iterator().next(), null);
        } else {
            assert targetGridPanes.size() == 1;
            final FXOMInstance targetGridPane
                    = (FXOMInstance) targetGridPanes.keySet().iterator().next();
            final Set<Integer> targetIndexes = targetGridPanes.get(targetGridPane);
            assert targetIndexes.size() >= 1;
            final Set<Integer> addedIndexes
                    = GridPaneJobUtils.getAddedIndexes(targetIndexes, position);
            
            asg = new GridSelectionGroup(targetGridPane, Type.COLUMN, addedIndexes);
        }
        return asg;
    }

    private List<Job> moveColumnContent() {

        final List<Job> result = new ArrayList<>();

        for (FXOMObject targetGridPane : targetGridPanes.keySet()) {

            final Set<Integer> targetIndexes = targetGridPanes.get(targetGridPane);

            final DesignHierarchyMask mask = new DesignHierarchyMask(targetGridPane);
            final int columnsSize = mask.getColumnsSize();
            final Iterator<Integer> iterator = targetIndexes.iterator();

            int shiftIndex = 0;
            int targetIndex = iterator.next();
            while (targetIndex != -1) {
                // Move the columns content :
                // - from the target index 
                // - to the next target index if any or the last column index otherwise
                int fromIndex, toIndex;

                switch (position) {
                    case BEFORE:
                        // fromIndex included
                        // toIndex excluded
                        fromIndex = targetIndex;
                        if (iterator.hasNext()) {
                            targetIndex = iterator.next();
                            toIndex = targetIndex - 1;
                        } else {
                            targetIndex = -1;
                            toIndex = columnsSize - 1;
                        }
                        break;
                    case AFTER:
                        // fromIndex excluded
                        // toIndex included
                        fromIndex = targetIndex + 1;
                        if (iterator.hasNext()) {
                            targetIndex = iterator.next();
                            toIndex = targetIndex;
                        } else {
                            targetIndex = -1;
                            toIndex = columnsSize - 1;
                        }
                        break;
                    default:
                        assert false;
                        return result;
                }

                // If fromIndex >= columnsSize, we are below the last existing column 
                // => no column content to move
                if (fromIndex < columnsSize) {
                    final int offset = 1 + shiftIndex;
                    final List<Integer> indexes
                            = GridPaneJobUtils.getIndexes(fromIndex, toIndex);
                    final ReIndexColumnContentJob reIndexJob = new ReIndexColumnContentJob(
                            getEditorController(), offset, targetGridPane, indexes);
                    result.add(reIndexJob);
                }

                shiftIndex++;
            }
        }
        return result;
    }

    /**
     * Returns the list of target column indexes for the specified GridPane
     * instance.
     *
     * @return the list of target indexes
     */
    private Set<Integer> getTargetColumnIndexes(
            final EditorController editorController,
            final FXOMObject targetGridPane) {

        final Selection selection = editorController.getSelection();
        final AbstractSelectionGroup asg = selection.getGroup();

        final Set<Integer> result = new LinkedHashSet<>();

        // Selection == GridPane columns
        // => return the list of selected columns
        if (asg instanceof GridSelectionGroup
                && ((GridSelectionGroup) asg).getType() == Type.COLUMN) {
            final GridSelectionGroup gsg = (GridSelectionGroup) asg;
            result.addAll(gsg.getIndexes());
        } //
        // Selection == GridPanes or Selection == GridPane rows
        // => return either the first (BEFORE) or the last (AFTER) column index
        else {
            switch (position) {
                case BEFORE:
                    result.add(0);
                    break;
                case AFTER:
                    final DesignHierarchyMask mask
                            = new DesignHierarchyMask(targetGridPane);
                    final int size = mask.getColumnsSize();
                    result.add(size - 1);
                    break;
                default:
                    assert false;
                    break;
            }
        }

        return result;
    }
}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/content/driver/tring/TabTring.java<|end_filename|>
/*
 * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.kit.editor.panel.content.driver.tring;

import com.oracle.javafx.scenebuilder.kit.editor.panel.content.ContentPanelController;
import com.oracle.javafx.scenebuilder.kit.editor.panel.content.driver.TabOutline;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMInstance;
import javafx.geometry.Bounds;
import javafx.scene.Node;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.scene.paint.Paint;

/**
 *
 * 
 */
public class TabTring extends AbstractTring<Tab> {

    private final TabOutline tabOutline;

    public TabTring(ContentPanelController contentPanelController, FXOMInstance fxomInstance) {
        super(contentPanelController, fxomInstance, Tab.class);
        assert fxomInstance.getSceneGraphObject() instanceof Tab;
        
        tabOutline = new TabOutline(getSceneGraphObject());
        tabOutline.getRingPath().getStyleClass().add(TARGET_RING_CLASS);
        tabOutline.getRingPath().setMouseTransparent(true);
        getRootNode().getChildren().add(tabOutline.getRingPath());
    }
    
    public FXOMInstance getFxomInstance() {
        return (FXOMInstance) getFxomObject();
    }

    /*
     * AbstractPring
     */
    
    @Override
    protected void layoutDecoration() {
        tabOutline.layout(this);
    }
    
    @Override
    public void changeStroke(Paint stroke) {
        tabOutline.getRingPath().setStroke(stroke);
    }
    
    
    /*
     * AbstractDecoration
     */
    
    @Override
    public Bounds getSceneGraphObjectBounds() {
        return getSceneGraphObject().getTabPane().getLayoutBounds();
    }

    @Override
    public Node getSceneGraphObjectProxy() {
        return getSceneGraphObject().getTabPane();
    }

    @Override
    protected void startListeningToSceneGraphObject() {
        final TabPane tabPane = getSceneGraphObject().getTabPane();
        startListeningToLayoutBounds(tabPane);
        startListeningToLocalToSceneTransform(tabPane);
    }

    @Override
    protected void stopListeningToSceneGraphObject() {
        final TabPane tabPane = getSceneGraphObject().getTabPane();
        stopListeningToLayoutBounds(tabPane);
        stopListeningToLocalToSceneTransform(tabPane);
    }
}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/metadata/property/value/SingleValuePropertyMetadata.java<|end_filename|>
/*
 * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.kit.metadata.property.value;


import com.oracle.javafx.scenebuilder.kit.fxom.FXOMDocument;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMProperty;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMInstance;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMNodes;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMObject;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMPropertyC;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMPropertyT;
import com.oracle.javafx.scenebuilder.kit.metadata.property.ValuePropertyMetadata;
import com.oracle.javafx.scenebuilder.kit.metadata.util.InspectorPath;
import com.oracle.javafx.scenebuilder.kit.metadata.util.PrefixedValue;
import com.oracle.javafx.scenebuilder.kit.metadata.util.PropertyName;
import java.util.Objects;

/**
 *
 */
public abstract class SingleValuePropertyMetadata<T> extends ValuePropertyMetadata {
    
    private final Class<T> valueClass;
    private final T defaultValue;

    public SingleValuePropertyMetadata(PropertyName name, Class<T> valueClass, 
            boolean readWrite, T defaultValue, InspectorPath inspectorPath) {
        super(name, readWrite, inspectorPath);
        this.defaultValue = defaultValue;
        this.valueClass = valueClass;
    }
    
    public T getDefaultValue() {
        return defaultValue;
    }
    
    public T getValue(FXOMInstance fxomInstance) {
        final T result;
        
        if (isReadWrite()) {
            final FXOMProperty fxomProperty = fxomInstance.getProperties().get(getName());
            if (fxomProperty == null) {
                // propertyName is not specified in the fxom instance.
                // We return the default value specified in the metadata of the
                // property
                result = defaultValue;
            } else if (fxomProperty instanceof FXOMPropertyT) {
                final FXOMPropertyT fxomPropertyT = (FXOMPropertyT) fxomProperty;
                final PrefixedValue pv = new PrefixedValue(fxomPropertyT.getValue());
                if (pv.isBindingExpression()) {
                    result = getDefaultValue();
                } else {
                    result = makeValueFromProperty(fxomPropertyT);
                }
            } else if (fxomProperty instanceof FXOMPropertyC) {
                final FXOMPropertyC fxomPropertyC = (FXOMPropertyC) fxomProperty;
                assert fxomPropertyC.getValues().isEmpty() == false;
                final FXOMObject firstValue = fxomPropertyC.getValues().get(0);
                if (firstValue instanceof FXOMInstance) {
                    result = makeValueFromFxomInstance((FXOMInstance) firstValue);
                } else {
                    result = getDefaultValue();
                }
            } else {
                assert false;
                result = defaultValue;
            }
        } else {
            result = valueClass.cast(getName().getValue(fxomInstance.getSceneGraphObject()));
        }
        
        return result;
    }

    public void setValue(FXOMInstance fxomInstance, T value) {
        assert isReadWrite();
        
        final FXOMProperty fxomProperty = fxomInstance.getProperties().get(getName());

        if (Objects.equals(value, getDefaultValueObject())) {
            // We must remove the fxom property if any
            if (fxomProperty != null) {
                fxomProperty.removeFromParentInstance();
            }
        } else {
            final FXOMDocument fxomDocument = fxomInstance.getFxomDocument();
            final FXOMProperty newProperty;
            if (canMakeStringFromValue(value)) {
                final String valueString = makeStringFromValue(value);
                newProperty = new FXOMPropertyT(fxomDocument, getName(), valueString);
            } else {
                final FXOMInstance valueInstance = makeFxomInstanceFromValue(value, fxomDocument);
                newProperty = new FXOMPropertyC(fxomDocument, getName(), valueInstance);
            }
            FXOMNodes.updateProperty(fxomInstance, newProperty);
        }
    }
    
    public abstract T makeValueFromString(String string);
    public abstract T makeValueFromFxomInstance(FXOMInstance valueFxomInstance);
    public abstract boolean canMakeStringFromValue(T value);
    public abstract String makeStringFromValue(T value);
    public abstract FXOMInstance makeFxomInstanceFromValue(T value, FXOMDocument fxomDocument);
    
    /* This routine should become abstract and replace makeValueFromString(). */
    public T makeValueFromProperty(FXOMPropertyT fxomProperty) {
        return makeValueFromString(fxomProperty.getValue());
    }
    
    /*
     * ValuePropertyMetadata
     */
    @Override
    public Class<? extends T> getValueClass() {
        return valueClass;
    }

    @Override
    public Object getDefaultValueObject() {
        return defaultValue;
    }

    @Override
    public Object getValueObject(FXOMInstance fxomInstance) {
        return getValue(fxomInstance);
    }

    @Override
    public void setValueObject(FXOMInstance fxomInstance, Object valueObject) {
        setValue(fxomInstance, valueClass.cast(valueObject));
    }
}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/content/driver/tring/VBoxTring.java<|end_filename|>
/*
 * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.kit.editor.panel.content.driver.tring;

import com.oracle.javafx.scenebuilder.kit.editor.panel.content.ContentPanelController;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMInstance;
import java.util.List;
import javafx.geometry.Bounds;
import javafx.geometry.Point2D;
import javafx.scene.Node;
import javafx.scene.layout.VBox;
import javafx.scene.shape.Line;

/**
 *
 * 
 */
public class VBoxTring extends AbstractNodeTring<VBox> {
        
    private final int targetIndex;
    private final Line crackLine = new Line();

    public VBoxTring(ContentPanelController contentPanelController, 
            FXOMInstance fxomInstance, int targetIndex) {
        super(contentPanelController, fxomInstance, VBox.class);
        assert targetIndex >= -1;
        this.targetIndex = targetIndex;
        
        crackLine.getStyleClass().add(TARGET_CRACK_CLASS);
        crackLine.setMouseTransparent(true);
        getRootNode().getChildren().add(0, crackLine);
    }
    
    
    /*
     * AbstractGenericTring
     */
        
    @Override
    protected void layoutDecoration() {
        
        super.layoutDecoration();
        
        final VBox vbox = getSceneGraphObject();
        final int childCount = vbox.getChildren().size();
        
        if (childCount == 0) {
            // No crack line
            crackLine.setVisible(false);
            
        } else {
            // Computes the crack y
            
            final double crackY;
            final List<Node> children = vbox.getChildren();
            if (targetIndex == -1) {
                final Node child = children.get(childCount-1);
                final Bounds cb = child.localToParent(child.getLayoutBounds());
                crackY = cb.getMaxY();
            } else {
                final Node child = children.get(targetIndex);
                final Bounds cb = child.localToParent(child.getLayoutBounds());
                crackY = cb.getMinY();
            }

            // Updates the crack line
            final boolean snapToPixel = true;
            final Bounds b = getSceneGraphObject().getLayoutBounds();
            final Point2D p0 = sceneGraphObjectToDecoration(b.getMinX(), crackY, snapToPixel);
            final Point2D p1 = sceneGraphObjectToDecoration(b.getMaxX(), crackY, snapToPixel);

            crackLine.setVisible(true);
            crackLine.setStartX(p0.getX());
            crackLine.setStartY(p0.getY());
            crackLine.setEndX(p1.getX());
            crackLine.setEndY(p1.getY());
        }
    }
}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/glossary/JavaTokenizer.java<|end_filename|>
/*
 * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.kit.glossary;

/**
 */
// Tokenize file f as follows. We read the entire text of the file into a string. We replace every occurrence of #
// in the string by its unicode equivalent. Then we construct a version of the file where every token is preceded
// by #, so for example
//     for (String s : strings) {
// would become
// #    #for# #(#String# #s# #:# #strings#)# #{
// The idea is that we can apply patterns to this tokenized version without fear that they will match inside
// strings or comments. Since a string or comment is a single token, and since we removed any # characters that
// might be inside strings or comments, we know that if we see for example #for#( then that is definitely an
// occurrence of "for(" that is not inside a string or a comment. //NOI18N
// The concept of a "token" here does not correspond exactly to Java's, both because sequences of whitespace are tokens //NOI18N
// and because the scanning is simplistic. We assume that the code is syntactically correct, and we also split some
// tokens more than Java does, for example #-#1 or #+#=. This extra splitting has no effect on the particular rules
// we will apply. We also do not scan keywords any differently from identifiers; since they are reserved words, if
// we see #for# then that is definitely the "for" keyword. //NOI18N
class JavaTokenizer {
    private JavaTokenizer() {
        assert false;
    }

    private static final String unicodeHash = "\\u" + String.format("%04x", (int) '#');//NOI18N

    public static String tokenize(String text) throws ParseException {
        if (text.contains(unicodeHash)) {
            throw new ParseException("Source file contains the sequence '" + unicodeHash + "'");//NOI18N
            // Why would we bother writing unicode when we can write #?
        }

        final String noHashText = text.replace("#", unicodeHash);//NOI18N
        final int len = noHashText.length();
        final StringBuilder tokens = new StringBuilder();
        int nexti;
        for (int i = 0; i < len; i = nexti) {
            char c = noHashText.charAt(i);
            switch (c) {
                case '/'://NOI18N
                    nexti = scanSlash(noHashText, i);
                    break;
                case '"'://NOI18N
                case '\''://NOI18N
                    try {
                        nexti = scanQuote(noHashText, i, c);
                    } catch (Error e) {
                        System.err.println(tokens.toString());
                        throw e;
                    }
                    break;
                case '\\'://NOI18N
                    throw new ParseException("Backslash outside quotes");//NOI18N
                    // It could be a unicode escape but why would we ever want that outside a string?
                default:
                    if (Character.isWhitespace(c)) {
                        nexti = scanSpace(noHashText, i);
                    } else if (c == '.') {
                        if (i + 1 >= len) {
                            throw new ParseException("Dot at end of file");//NOI18N
                        }
                        if (Character.isDigit(noHashText.charAt(i + 1))) {
                            nexti = scanNumber(noHashText, i + 1);
                        } else {
                            nexti = i + 1;
                        }
                    } else if (c == '.' || Character.isDigit(c)) {//NOI18N
                        nexti = scanNumber(noHashText, i);
                    } else if (Character.isJavaIdentifierStart(c)) {
                        nexti = scanIdentifier(noHashText, i);
                    } else {
                        nexti = i + 1;
                    }
            }
            tokens.append('#').append(noHashText.substring(i, nexti));//NOI18N
        }

        return tokens.toString();
    }

    private static int scanSpace(String s, int i) {
        int len = s.length();
        while (i < len && Character.isWhitespace(s.charAt(i))) {
            i++;
        }
        return i;
    }

    private static int scanNumber(String s, int i) {
        int len = s.length();
        while (i < len) {
            char c = s.charAt(i);
            if (c == 'e' || c == 'E') {//NOI18N
                i++;  // skip possible sign
            }
            if (c == '.' || Character.isDigit(c) || Character.isLetter(c)) {//NOI18N
                i++;
            } else {
                break;
            }
        }
        return i;
    }

    private static int scanIdentifier(String s, int i) {
        int len = s.length();
        while (i < len && Character.isJavaIdentifierPart(s.charAt(i))) {
            i++;
        }
        return i;
    }

    private static int scanSlash(String s, int i) throws ParseException {
        int len = s.length();
        if (i + 1 >= len) {
            return i + 1;
        }
        switch (s.charAt(i + 1)) {
            case '/'://NOI18N
                int newline = s.indexOf('\n', i);//NOI18N
                if (newline < 0) {
                    throw new ParseException("Unterminated // comment");//NOI18N
                }
                return newline + 1;
            case '*'://NOI18N
                int starSlash = s.indexOf("*/", i + 2);//NOI18N
                if (starSlash < 0) {
                    throw new ParseException("Unterminated /* comment");//NOI18N
                }
                return starSlash + 2;
            default:
                return i + 1;
        }
    }

    private static int scanQuote(String s, int i, char quote) throws ParseException {
        assert s.charAt(i) == quote;
        int len = s.length();
        while (++i < len && s.charAt(i) != quote) {
            if (s.charAt(i) == '\\') {//NOI18N
                i++;
            }
        }
        if (i >= len) {
            System.err.println(s);
            throw new ParseException("Unterminated char or string constant");//NOI18N
        }
        return i + 1;
    }

    @SuppressWarnings("serial")//NOI18N
    public static class ParseException extends Exception {
        public ParseException(String msg) {
            super(msg);
        }
    }
}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/content/gesture/DragGesture.java<|end_filename|>
/*
 * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.kit.editor.panel.content.gesture;

import com.oracle.javafx.scenebuilder.kit.editor.drag.DragController;
import com.oracle.javafx.scenebuilder.kit.editor.drag.source.AbstractDragSource;
import com.oracle.javafx.scenebuilder.kit.editor.drag.source.ExternalDragSource;
import com.oracle.javafx.scenebuilder.kit.editor.drag.target.AbstractDropTarget;
import com.oracle.javafx.scenebuilder.kit.editor.drag.target.AccessoryDropTarget;
import com.oracle.javafx.scenebuilder.kit.editor.drag.target.ContainerXYDropTarget;
import com.oracle.javafx.scenebuilder.kit.editor.drag.target.ImageViewDropTarget;
import com.oracle.javafx.scenebuilder.kit.editor.drag.target.RootDropTarget;
import com.oracle.javafx.scenebuilder.kit.editor.panel.content.ContentPanelController;
import com.oracle.javafx.scenebuilder.kit.editor.panel.content.driver.AbstractDriver;
import com.oracle.javafx.scenebuilder.kit.editor.panel.content.driver.BorderPaneDriver;
import com.oracle.javafx.scenebuilder.kit.editor.panel.content.guides.MovingGuideController;
import com.oracle.javafx.scenebuilder.kit.editor.panel.content.util.BoundsUtils;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMDocument;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMInstance;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMObject;
import com.oracle.javafx.scenebuilder.kit.metadata.util.DesignHierarchyMask;
import com.oracle.javafx.scenebuilder.kit.metadata.util.DesignHierarchyMask.Accessory;
import com.oracle.javafx.scenebuilder.kit.util.MathUtils;

import java.util.HashSet;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;

import javafx.event.EventType;
import javafx.geometry.Bounds;
import javafx.geometry.Point2D;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.image.ImageView;
import javafx.scene.input.DragEvent;
import javafx.scene.input.InputEvent;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.BorderPane;
import javafx.stage.Window;

/**
 *
 * 
 */
public class DragGesture extends AbstractGesture {
    
    private static final Logger LOG = Logger.getLogger(DragGesture.class.getName());

    private final double MARGIN = 14.0;
    
    private final DragController dragController;
    private final Set<FXOMObject> pickExcludes = new HashSet<>();
    private DragEvent dragEnteredEvent;
    private DragEvent lastDragEvent;
    private Observer observer;
    private boolean willReceiveDragDone;
    private boolean shouldInvokeEnd;
    private FXOMObject hitParent;
    private DesignHierarchyMask hitParentMask;
    private MovingGuideController movingGuideController;
    private boolean guidesDisabled;
    private Node shadow;

    public DragGesture(ContentPanelController contentPanelController) {
        super(contentPanelController);
        this.dragController = contentPanelController.getEditorController().getDragController();
    }
    
    /*
     * AbstractDragGesture
     */
    
    @Override
    public void start(InputEvent e, Observer observer) {
        assert e != null;
        assert e instanceof DragEvent;
        assert e.getEventType() == DragEvent.DRAG_ENTERED;
        
        final Node glassLayer = contentPanelController.getGlassLayer();
        assert glassLayer.getOnDragEntered()== null;
        assert glassLayer.getOnDragOver()== null;
        assert glassLayer.getOnDragExited()== null;
        assert glassLayer.getOnDragDropped()== null;
        assert glassLayer.getOnDragDone()== null;
        assert glassLayer.getOnKeyPressed()== null;
        
        glassLayer.setOnDragEntered(e1 -> {
            lastDragEvent = e1;
            dragEnteredGlassLayer();
        });
        glassLayer.setOnDragOver(e1 -> {
            lastDragEvent = e1;
            dragOverGlassLayer();
        });
        glassLayer.setOnDragExited(e1 -> {
            lastDragEvent = e1;
            dragExitedGlassLayer();
        });
        glassLayer.setOnDragDropped(e1 -> {
            lastDragEvent = e1;
            dragDroppedOnGlassLayer();
            e1.consume();
            // On Linux, "drag over" is randomly called before "drag done".
            // It's unclear whether it's an FX bug or feature.
            // To make things unambiguous, we clear the "drag over" callback.
            // See DTL-5956.
            glassLayer.setOnDragOver(null);
        });
        glassLayer.setOnDragDone(e1 -> {
            lastDragEvent = e1;
            dragDoneOnGlassLayer();
            e1.getDragboard().clear();
            e1.consume();
        });
        glassLayer.setOnKeyPressed(e1 -> handleKeyPressed(e1));
        
        this.dragEnteredEvent = (DragEvent) e;
        this.lastDragEvent = this.dragEnteredEvent;
        this.observer = observer;
        this.willReceiveDragDone = this.dragEnteredEvent.getGestureSource() == glassLayer;
        this.shouldInvokeEnd = willReceiveDragDone;
        assert this.hitParent == null;
        assert this.hitParentMask == null;
        assert this.shadow == null;
        
        setupMovingGuideController();
        
        dragEnteredGlassLayer();
    }
    
    
    /*
     * Private
     */
    
    private void dragEnteredGlassLayer() {
        if (dragController.getDragSource() == null) { // Drag started externally
            final FXOMDocument fxomDocument
                    = contentPanelController.getEditorController().getFxomDocument();
            final Window ownerWindow
                    = contentPanelController.getPanelRoot().getScene().getWindow();
            final ExternalDragSource dragSource = new ExternalDragSource(
                    lastDragEvent.getDragboard(), fxomDocument, ownerWindow);
            assert dragSource.isAcceptable();
            dragController.begin(dragSource);
            shouldInvokeEnd = true;
        }
        
        // Objects being dragged should be excluded from the pick.
        // We create the exclude list here once.
        pickExcludes.clear();
        pickExcludes.addAll(dragController.getDragSource().getDraggedObjects());
        
        // We show the shadow
        showShadow();
        
        // Now same logic as dragOver
        dragOverGlassLayer();
    }
    
    private void dragOverGlassLayer() {
        /*
         * On Linux, Node.onDragOver() is sometimes called *after*
         * Node.onDragDropped() : see RT-34537.
         * We detect those illegal invocations here and ignore them.
         */
        
        if (lastDragEvent.isDropCompleted()) {
            LOG.log(Level.WARNING, "Ignored dragOver() after dragDropped()"); //NOI18N
        } else {
            dragOverGlassLayerBis();
        }
    }
    
    private void dragOverGlassLayerBis() {
        
        // Let's set what is below the mouse
        final double hitX = lastDragEvent.getSceneX();
        final double hitY = lastDragEvent.getSceneY();
        FXOMObject hitObject = contentPanelController.pick(hitX, hitY, pickExcludes);
        if (hitObject == null) {
            final FXOMDocument fxomDocument
                = contentPanelController.getEditorController().getFxomDocument();
            hitObject = fxomDocument.getFxomRoot();
        }
        
        if (hitObject == null) {
            // FXOM document is empty
            dragOverEmptyDocument();
        } else {
            dragOverHitObject(hitObject);
        }
    }
    
    private void dragOverEmptyDocument() {
        dragController.setDropTarget(new RootDropTarget());
        lastDragEvent.acceptTransferModes(dragController.getAcceptedTransferModes());
        updateShadow(lastDragEvent.getSceneX(), lastDragEvent.getSceneY());
    }
    
    private void dragOverHitObject(FXOMObject hitObject) {
        assert hitObject != null;
                
        final FXOMDocument fxomDocument
                = contentPanelController.getEditorController().getFxomDocument();
        final AbstractDragSource dragSource
                = dragController.getDragSource();
        final DesignHierarchyMask m 
                = new DesignHierarchyMask(hitObject);
        final double hitX 
                = lastDragEvent.getSceneX();
        final double hitY 
                = lastDragEvent.getSceneY();
        
        assert fxomDocument != null;
        assert dragSource != null;

        AbstractDropTarget dropTarget = null;
        FXOMObject newHitParent = null;
        DesignHierarchyMask newHitParentMask = null;
        
        // dragSource is a single ImageView ?
        final boolean hitImageView = hitObject.getSceneGraphObject() instanceof ImageView;
        final boolean externalDragSource = dragSource instanceof ExternalDragSource;

        if (dragSource.isSingleImageViewOnly() && hitImageView && externalDragSource) {
            dropTarget = new ImageViewDropTarget(hitObject);
            newHitParent = hitObject;
            newHitParentMask = m;
        }
        
        // dragSource is a single Tooltip ?
        if (dropTarget == null) {
            if (dragSource.isSingleTooltipOnly()) {
                assert hitObject instanceof FXOMInstance;
                dropTarget = new AccessoryDropTarget((FXOMInstance)hitObject, Accessory.TOOLTIP);
                newHitParent = hitObject;
                newHitParentMask = m;
            }
        }
        
        // dragSource is a single ContextMenu ?
        if (dropTarget == null) {
            if (dragSource.isSingleContextMenuOnly()) {
                assert hitObject instanceof FXOMInstance;
                dropTarget = new AccessoryDropTarget((FXOMInstance)hitObject, Accessory.CONTEXT_MENU);
                newHitParent = hitObject;
                newHitParentMask = m;
            }
        }
        
        // hitObject is BorderPane ?
        if (dropTarget == null) {
            if (hitObject.getSceneGraphObject() instanceof BorderPane) {
                final AbstractDriver driver = contentPanelController.lookupDriver(hitObject);
                assert driver instanceof BorderPaneDriver;
                dropTarget = driver.makeDropTarget(hitObject, hitX, hitY);
                newHitParent = hitObject;
                newHitParentMask = m;
            }
        }
        
        // hitObject has sub-components (ie it is a container)
        if (dropTarget == null) {
            if (m.isAcceptingSubComponent()) {
                final AbstractDriver driver = contentPanelController.lookupDriver(hitObject);
                dropTarget = driver.makeDropTarget(hitObject, hitX, hitY);
                newHitParent = hitObject;
                newHitParentMask = m;
            }
        }
        
        // hitObject accepts Accessory.CONTENT
        if (dropTarget == null) {
            if (m.isAcceptingAccessory(Accessory.CONTENT)) {
                assert hitObject instanceof FXOMInstance;
                dropTarget = new AccessoryDropTarget((FXOMInstance)hitObject, Accessory.CONTENT);
                newHitParent = hitObject;
                newHitParentMask = m;
            }
        }
        
        // hitObject parent is a container ?
        if (dropTarget == null) {
            final FXOMObject hitObjectParent = hitObject.getParentObject();
            if (hitObjectParent != null) {
                final DesignHierarchyMask mp = new DesignHierarchyMask(hitObjectParent);
                if (mp.isAcceptingSubComponent()) {
                    final AbstractDriver driver = contentPanelController.lookupDriver(hitObjectParent);
                    dropTarget = driver.makeDropTarget(hitObjectParent, hitX, hitY);
                    newHitParent = hitObjectParent;
                    newHitParentMask = mp;
                }
            }
        }
                
        // Update movingGuideController
        if (newHitParent != hitParent) {
            hitParent = newHitParent;
            hitParentMask = newHitParentMask;
            if (hitParent == null) {
                assert hitParentMask == null;
                movingGuideController.clearSampleBounds();
            } else {
                assert hitParentMask != null;
                if (hitParentMask.isFreeChildPositioning() && dragSource.isNodeOnly()) {
                    populateMovingGuideController();
                } else {
                    movingGuideController.clearSampleBounds();
                }
            }
        }
        
        final double guidedX, guidedY;
        if (movingGuideController.hasSampleBounds() && (guidesDisabled == false)) {
            updateShadow(hitX, hitY);
            final Bounds shadowBounds = shadow.getLayoutBounds();
            final Bounds shadowBoundsInScene = shadow.localToScene(shadowBounds, true /* rootScene */);
            movingGuideController.match(shadowBoundsInScene);
            
            guidedX = hitX + movingGuideController.getSuggestedDX();
            guidedY = hitY + movingGuideController.getSuggestedDY();
        } else {
            guidedX = hitX;
            guidedY = hitY;
        }

        updateShadow(guidedX, guidedY);
        
        if (!MathUtils.equals(guidedX , hitX) || !MathUtils.equals(guidedY, hitY)) {
            assert dropTarget != null;
            assert dropTarget instanceof ContainerXYDropTarget;
            final AbstractDriver driver = contentPanelController.lookupDriver(dropTarget.getTargetObject());
            dropTarget = driver.makeDropTarget(hitParent, guidedX, guidedY);
            assert dropTarget instanceof ContainerXYDropTarget;
        }
        
        dragController.setDropTarget(dropTarget);
        lastDragEvent.acceptTransferModes(dragController.getAcceptedTransferModes());
        
    }
    
    private void dragExitedGlassLayer() {
        
        dragController.setDropTarget(null);
        hideShadow();
        movingGuideController.clearSampleBounds();

        if (willReceiveDragDone == false) {
            dragDoneOnGlassLayer();
        }
    }
    
    private void dragDroppedOnGlassLayer() {    
        lastDragEvent.setDropCompleted(true);
        dragController.commit();
        contentPanelController.getGlassLayer().requestFocus();
    }
    
    private void dragDoneOnGlassLayer() {
        if (shouldInvokeEnd) {
            dragController.end();
        }
        performTermination();
    }
    
    private void handleKeyPressed(KeyEvent e) {
        if (e.getCode() == KeyCode.ESCAPE) {
            dragExitedGlassLayer();
            if (willReceiveDragDone) {
                // dragDone will not arrive but 
                // we need to execute the corresponding logic
                dragDoneOnGlassLayer();
            }
        } else if (e.getCode() == KeyCode.ALT) {
            final EventType<KeyEvent> eventType = e.getEventType();
            if (eventType == KeyEvent.KEY_PRESSED) {
                guidesDisabled = true;
            } else if (eventType == KeyEvent.KEY_RELEASED) {
                guidesDisabled = false;
            }
            dragOverGlassLayer();
        }
    }
    
    
    private void performTermination() {
        final Node glassLayer = contentPanelController.getGlassLayer();
        glassLayer.setOnDragEntered(null);
        glassLayer.setOnDragOver(null);
        glassLayer.setOnDragExited(null);
        glassLayer.setOnDragDropped(null);
        glassLayer.setOnDragDone(null);
        glassLayer.setOnKeyPressed(null);
        
        dismantleMovingGuideController();
        
        observer.gestureDidTerminate(this);
        observer = null;
        
        dragEnteredEvent = null;
        lastDragEvent = null;
        shouldInvokeEnd = false;
        hitParent = null;
        hitParentMask = null;
        assert shadow == null; // Because dragExitedGlassLayer() called hideShadow()
    }
    
    /*
     * Shadow
     */
    
    private void showShadow() {
        assert shadow == null;
        
        shadow = dragController.getDragSource().makeShadow();
        shadow.setMouseTransparent(true);
        contentPanelController.getRudderLayer().getChildren().add(shadow);
        
        updateShadow(0.0, 0.0);
    }
    
    private void updateShadow(double hitX, double hitY) {
        assert shadow != null;
        
        final Group rudderLayer = contentPanelController.getRudderLayer();
        final Point2D p = rudderLayer.sceneToLocal(hitX, hitY, true /* rootScene */);
        shadow.setLayoutX(p.getX());
        shadow.setLayoutY(p.getY());
    }
    
    private void hideShadow() {
        assert shadow != null;
        contentPanelController.getRudderLayer().getChildren().remove(shadow);
        shadow = null;
    }
    
    /*
     * MovingGuideController
     */
    
    private void setupMovingGuideController() {
        final Bounds scope = contentPanelController.getWorkspacePane().getLayoutBounds();
        final Bounds scopeInScene = contentPanelController.getWorkspacePane().localToScene(scope, true /* rootScene */);
        this.movingGuideController = new MovingGuideController(
                contentPanelController.getGuidesColor(), scopeInScene);
        final Group rudderLayer = contentPanelController.getRudderLayer();
        final Group guideGroup = movingGuideController.getGuideGroup();
        assert guideGroup.isMouseTransparent();
        rudderLayer.getChildren().add(guideGroup);
    }
    
    
    private void populateMovingGuideController() {
        assert hitParentMask != null;
        assert hitParentMask.isFreeChildPositioning(); // (1)
        
        movingGuideController.clearSampleBounds();
        
        // Adds N, S, E, W and center lines for each child of the hitParent
        for (int i = 0, c = hitParentMask.getSubComponentCount(); i < c; i++) {
            final FXOMObject child = hitParentMask.getSubComponentAtIndex(i);
            final boolean isNode = child.getSceneGraphObject() instanceof Node;
            if ((pickExcludes.contains(child) == false) && isNode) {
                final Node childNode = (Node) child.getSceneGraphObject();
                movingGuideController.addSampleBounds(childNode);
            }
        }
        
        // Adds N, S, E, W and center lines of the hitParent itself
        assert hitParent.getSceneGraphObject() instanceof Node; // Because (1)
        final Node hitParentNode = (Node) hitParent.getSceneGraphObject();
        movingGuideController.addSampleBounds(hitParentNode);
        
        // If bounds of hitParent are larger enough then adds the margin boundaries
        final Bounds hitParentBounds = hitParentNode.getLayoutBounds();
        final Bounds insetBounds = BoundsUtils.inset(hitParentBounds, MARGIN, MARGIN);
        if (insetBounds.isEmpty() == false) {
            final Bounds insetBoundsInScene = hitParentNode.localToScene(insetBounds, true /* rootScene */);
            movingGuideController.addSampleBounds(insetBoundsInScene, false /* addMiddle */);
        }
    }    
    
    
    private void dismantleMovingGuideController() {
        assert movingGuideController != null;
        final Group guideGroup = movingGuideController.getGuideGroup();
        final Group rudderLayer = contentPanelController.getRudderLayer();
        assert rudderLayer.getChildren().contains(guideGroup);
        rudderLayer.getChildren().remove(guideGroup);
        movingGuideController = null;
    }
}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/inspector/editors/ToggleGroupEditor.java<|end_filename|>
/*
 * Copyright (c) 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.kit.editor.panel.inspector.editors;

import com.oracle.javafx.scenebuilder.kit.metadata.property.ValuePropertyMetadata;
import com.oracle.javafx.scenebuilder.kit.util.JavaLanguage;
import java.util.List;
import java.util.Set;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;

/**
 * Toggle Group editor.
 *
 *
 */
public class ToggleGroupEditor extends AutoSuggestEditor {

    List<String> suggestedTgs;

    public ToggleGroupEditor(ValuePropertyMetadata propMeta, Set<Class<?>> selectedClasses, List<String> suggestedTgs) {
        super(propMeta, selectedClasses, suggestedTgs);
        initialize(suggestedTgs);
    }
    
    private void initialize(List<String> suggestedTgs) {
        this.suggestedTgs = suggestedTgs;

        // text field events handling
        EventHandler<ActionEvent> onActionListener = event -> {
            if (isHandlingError()) {
                // Event received because of focus lost due to error dialog
                return;
            }
            String value = textField.getText();
            if (value != null && !value.isEmpty()) {

                if (!JavaLanguage.isIdentifier(value)) {
//                        System.err.println(I18N.getString("inspector.fxml.invalid.id", value));
                    handleInvalidValue(value);
                    return;
                }
            }
            userUpdateValueProperty((value == null || value.isEmpty()) ? null : value);
            textField.selectAll();
        };
        setTextEditorBehavior(this, textField, onActionListener);
    }

    @Override
    public void reset(ValuePropertyMetadata propMeta, Set<Class<?>> selectedClasses, List<String> suggestedTgs) {
        super.reset(propMeta, selectedClasses, suggestedTgs);
    }
}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/util/AbstractPanelController.java<|end_filename|>
/*
 * Copyright (c) 2017, Gluon and/or its affiliates.
 * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.kit.editor.panel.util;

import com.oracle.javafx.scenebuilder.kit.editor.EditorController;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMDocument;

import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import javafx.beans.value.ChangeListener;
import javafx.scene.Parent;

/**
 * AbstractPanelController is the abstract base class for all the panel 
 * controllers of Scene Builder Kit.
 * <p>
 * At instantiation time, each panel controller is passed a reference to its 
 * editor controller which is hold in <code>editorController</code>.
 * <p>
 * Subclasses must provide three methods:
 * <ul>
 * <li><code>makePanel</code> must create the FX components
 * which compose the panel
 * <li><code>fxomDocumentDidChange</code> must keep the panel up to date
 * after the editor controller has changed the base document
 * <li><code>editorSelectionDidChange</code> must keep the panel up to date
 * after the editor controller has changed the selected objects.
 * </ul>
 * 
 * 
 */
public abstract class AbstractPanelController {
    
    private static final Logger LOG = Logger.getLogger(AbstractPanelController.class.getName());
    
    private final EditorController editorController;
    private Parent panelRoot;
    
    /**
     * Base constructor for invocation by the subclasses.
     * Subclass implementations should make sure that this constructor can be
     * invoked outside of the JavaFX thread.
     * 
     * @param c the editor controller (should not be null).
     */
    protected AbstractPanelController(EditorController c) {
        assert c != null;
        this.editorController = c;
        startListeningToEditorSelection();
        startListeningToJobManagerRevision();
        editorController.fxomDocumentProperty().addListener((ChangeListener<FXOMDocument>) (ov, od, nd) -> {
            assert editorController.getFxomDocument() == nd;
            if (od != null) {
                od.sceneGraphRevisionProperty().removeListener(fxomDocumentRevisionListener);
                od.cssRevisionProperty().removeListener(cssRevisionListener);
            }
            try {
                fxomDocumentDidChange(od);
            } catch(RuntimeException x) {
                LOG.log(Level.SEVERE, "Bug", x); //NOI18N
            }
            if (nd != null) {
                nd.sceneGraphRevisionProperty().addListener(fxomDocumentRevisionListener);
                nd.cssRevisionProperty().addListener(cssRevisionListener);
            }
        });
        if (editorController.getFxomDocument() != null) {
            editorController.getFxomDocument().sceneGraphRevisionProperty().addListener(fxomDocumentRevisionListener);
            editorController.getFxomDocument().cssRevisionProperty().addListener(cssRevisionListener);
        }
        editorController.toolStylesheetProperty().addListener((ChangeListener<String>) (ov, od, nd) -> toolStylesheetDidChange(od));
    }
    
    /**
     * Returns the editor controller associated to this panel controller.
     * 
     * @return the editor controller (never null).
     */
    public EditorController getEditorController() {
        return editorController;
    }
    
    /**
     * Returns the root FX object of this panel.
     * When called the first time, this method invokes {@link #makePanel()}
     * to build the FX components of the panel.
     * 
     * @return the root object of the panel (never null)
     */
    public Parent getPanelRoot() {
        if (panelRoot == null) {
            makePanel();
            assert panelRoot != null;
            
            // Installs the stylesheet from the editor controller
            final List<String> stylesheets = panelRoot.getStylesheets();
            if (stylesheets.contains(EditorController.getBuiltinToolStylesheet())) {
                toolStylesheetDidChange(EditorController.getBuiltinToolStylesheet());
            } else {
                toolStylesheetDidChange(null);
            }
        }
        
        return panelRoot;
    }
    
    /*
     * To be implemented by subclasses
     */
    
    /**
     * Creates the FX object composing the panel.
     * This routine is called by {@link AbstractPanelController#getPanelRoot}.
     * It *must* invoke {@link AbstractPanelController#setPanelRoot}.
     */
    protected abstract void makePanel();
    
    /**
     * Updates the panel after the editor controller has change
     * the base document. Subclass can use {@link EditorController#getFxomDocument() } 
     * to retrieve the newly set document (possibly null).
     * 
     * @param oldDocument the previous document (possibly null).
     */
    protected abstract void fxomDocumentDidChange(FXOMDocument oldDocument);
    
    /**
     * Updates the panel after the revision of the scene graph has changed.
     * Revision is incremented each time the fxom document rebuilds the
     * scene graph.
     */
    protected abstract void sceneGraphRevisionDidChange();
    
    /**
     * Updates the panel after the css revision has changed.
     * Revision is incremented each time the fxom document forces FX to
     * reload its stylesheets.
     */
    protected abstract void cssRevisionDidChange();
    
    /**
     * Updates the panel after the revision of job manager has changed.
     * Revision is incremented each time a job is executed, undone or redone.
     */
    protected abstract void jobManagerRevisionDidChange();
    
    /**
     * Updates the panel after the editor controller has changed the selected
     * objects. Subclass can use {@link EditorController#getSelection()} to
     * retrieve the currently selected objects.
     */
    protected abstract void editorSelectionDidChange();
    
    
    /*
     * For subclasses
     */
    
    /**
     * Set the root of this panel controller.
     * This routine must be invoked by subclass's makePanel() routine.
     * 
     * @param panelRoot the root panel (non null).
     */
    protected  final void setPanelRoot(Parent panelRoot) {
        assert panelRoot != null;
        this.panelRoot = panelRoot;
    }
    
    private final ChangeListener<Number> fxomDocumentRevisionListener
            = (observable, oldValue, newValue) -> {
        try {
            sceneGraphRevisionDidChange();
        } catch(RuntimeException x) {
            LOG.log(Level.SEVERE, "Bug", x); //NOI18N
        }
    };
    
    private final ChangeListener<Number> cssRevisionListener
            = (observable, oldValue, newValue) -> {
        try {
            cssRevisionDidChange();
        } catch(RuntimeException x) {
            LOG.log(Level.SEVERE, "Bug", x); //NOI18N
        }
    };
    
    private final ChangeListener<Number> jobManagerRevisionListener
            = (observable, oldValue, newValue) -> {
        try {
            jobManagerRevisionDidChange();
        } catch(RuntimeException x) {
            LOG.log(Level.SEVERE, "Bug", x); //NOI18N
        }
    };
    
    private final ChangeListener<Number> editorSelectionListener = 
        (observable, oldValue, newValue) -> {
        try {
            editorSelectionDidChange();
        } catch(RuntimeException x) {
            LOG.log(Level.SEVERE, "Bug", x); //NOI18N
        }
    };
    
    /**
     * Setup a listener which invokes {@link #editorSelectionDidChange} each
     * time the editor controller changes the selected objects.
     * This routine is automatically called when the panel controller is 
     * instantiated. Subclasses may invoke it after temporarily disabling 
     * selection listening with {@link AbstractPanelController#stopListeningToEditorSelection}.
     */
    protected final void startListeningToEditorSelection() {
        editorController.getSelection().revisionProperty().addListener(editorSelectionListener);
    }
    
    /**
     * Removes the listener which invokes {@link #editorSelectionDidChange} each
     * time the editor controller changes the selected objects.
     * Subclasses may invoke this routine to temporarily stop listening to 
     * the selection changes from the editor controller. Use
     * {@link AbstractPanelController#startListeningToEditorSelection} to 
     * re-enable selection listening.
     */
    protected final void stopListeningToEditorSelection() {
        editorController.getSelection().revisionProperty().removeListener(editorSelectionListener);
    }
    
    
    /**
     * Setup a listener which invokes {@link #jobManagerRevisionDidChange} each
     * time the job manager has executed, undone or redone a job.
     * This routine is automatically called when the panel controller is 
     * instantiated. Subclasses may invoke it after temporarily disabling 
     * job manager listening with {@link AbstractPanelController#stopListeningToJobManagerRevision}.
     */
    protected final void startListeningToJobManagerRevision() {
        editorController.getJobManager().revisionProperty().addListener(jobManagerRevisionListener);
    }

    
    /**
     * Removes the listener which invokes {@link #jobManagerRevisionDidChange} each
     * time the job manager has executed, undone or redone a job.
     * Subclasses may invoke this routine to temporarily stop listening to 
     * the job manager from the editor controller. Use
     * {@link AbstractPanelController#startListeningToJobManagerRevision} to 
     * re-enable job manager listening.
     */
    protected final void stopListeningToJobManagerRevision() {
        editorController.getJobManager().revisionProperty().removeListener(jobManagerRevisionListener);
    }
    
    
    /**
     * Replaces oldStylesheet by the tool style sheet assigned to the editor
     * controller. This methods {@link EditorController#getToolStylesheet}.
     * 
     * @param oldStylesheet null or the style sheet to be replaced
     */
    protected void toolStylesheetDidChange(String oldStylesheet) {
        /*
         * Tool style sheet has changed in editor controller.
         * If the panel has been loaded, then we replace the old sheet
         * by the new one in the stylesheets property of its root object.
         */
        if (panelRoot != null) {
            final List<String> stylesheets = panelRoot.getStylesheets();
            if (oldStylesheet != null) {
                stylesheets.remove(oldStylesheet);
            }
            stylesheets.add(editorController.getToolStylesheet());
        }
    }
    
    
}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/fxom/FXOMLoader.java<|end_filename|>
/*
 * Copyright (c) 2017 Gluon and/or its affiliates.
 * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.kit.fxom;

import com.oracle.javafx.scenebuilder.kit.i18n.I18N;
import com.oracle.javafx.scenebuilder.kit.editor.panel.util.dialog.ErrorDialog;
import com.oracle.javafx.scenebuilder.kit.metadata.util.PropertyName;
import com.oracle.javafx.scenebuilder.kit.util.Deprecation;
import javafx.fxml.LoadListener;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.stage.Window;

import javax.xml.stream.XMLStreamException;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.Collections;


/**
 *
 *
 */
class FXOMLoader implements LoadListener {

    private final FXOMDocument document;
    private TransientNode currentTransientNode;
    private GlueCursor glueCursor;
    
    /*
     * FXOMLoader
     */

    public FXOMLoader(FXOMDocument document) {
        assert document != null;
        assert document.getGlue().getRootElement() != null;
        this.document = document;
    }

    public void load(String fxmlText) throws java.io.IOException {
        assert fxmlText != null;

        final ClassLoader classLoader;
        if (document.getClassLoader() != null) {
            classLoader = document.getClassLoader();
        } else {
            classLoader = FXMLLoader.getDefaultClassLoader();
        }

        FXMLLoader fxmlLoader = new FXMLLoader();
        fxmlLoader.setLocation(document.getLocation());
        fxmlLoader.setResources(new ResourceKeyCollector(document.getResources()));
        fxmlLoader.setClassLoader(new TransientClassLoader(classLoader));
        fxmlLoader.setLoadListener(this);
        Deprecation.setStaticLoad(fxmlLoader, true);

        final Charset utf8 = Charset.forName("UTF-8");
        try (final InputStream is = new ByteArrayInputStream(fxmlText.getBytes(utf8))) {
            glueCursor = new GlueCursor(document.getGlue());
            currentTransientNode = null;
            assert is.markSupported();
            is.reset();
            setSceneGraphRoot(fxmlLoader.load(is));
        } catch (RuntimeException | IOException x) {
            if (x.getCause().getClass() == XMLStreamException.class) {
                handleUnsupportedCharset(x);
            } else
                throw new IOException(x);
        }
    }

    private void handleUnsupportedCharset(Exception x) {
        final ErrorDialog errorDialog = new ErrorDialog(null);
        errorDialog.setMessage(I18N.getString("alert.open.failure.charset.not.found"));
        errorDialog.setDetails(I18N.getString("alert.open.failure.charset.not.found.details"));
        errorDialog.setDebugInfo(x.getCause().toString());
        errorDialog.setTitle(I18N.getString("alert.title.open"));
        errorDialog.showAndWait();
    }

    private void setSceneGraphRoot(Object sceneGraphRoot) {
        document.setSceneGraphRoot(sceneGraphRoot);
        document.setDisplayNode(null);
        document.setDisplayStylesheets(Collections.emptyList());

        if (sceneGraphRoot instanceof Scene) {
            Scene scene = (Scene) sceneGraphRoot;
            document.setDisplayNode(scene.getRoot());
            document.setDisplayStylesheets(scene.getStylesheets());
            scene.setRoot(new Pane()); // ensure displayNode is only part of one scene
        } else if (sceneGraphRoot instanceof Window) {
            Window window = (Window) sceneGraphRoot;
            if (window.getScene() != null) {
                document.setDisplayNode(window.getScene().getRoot());
                document.setDisplayStylesheets(window.getScene().getStylesheets());
                window.getScene().setRoot(new Pane()); // ensure displayNode is only part of one scene
            }
        }
    }

    public FXOMDocument getDocument() {
        return document;
    }
    
    
    /*
     * LoadListener
     */

    @Override
    public void readImportProcessingInstruction(String data) {
    }

    @Override
    public void readLanguageProcessingInstruction(String data) {
    }

    @Override
    public void readComment(String string) {
    }

    @Override
    public void beginInstanceDeclarationElement(Class<?> declaredClass) {
        assert declaredClass != null;
        assert glueCursor.getCurrentElement().getTagName().equals(PropertyName.makeClassFullName(declaredClass)) ||
               glueCursor.getCurrentElement().getTagName().equals(declaredClass.getCanonicalName());

        final TransientObject transientInstance
                = new TransientObject(currentTransientNode,
                declaredClass, glueCursor.getCurrentElement());

        currentTransientNode = transientInstance;
        glueCursor.moveToNextElement();
    }

    @Override
    public void beginUnknownTypeElement(String unknownClassName) {
        assert unknownClassName != null;
        assert glueCursor.getCurrentElement().getTagName().equals(unknownClassName);

        final TransientObject transientInstance
                = new TransientObject(currentTransientNode,
                unknownClassName, glueCursor.getCurrentElement());

        currentTransientNode = transientInstance;
        glueCursor.moveToNextElement();
    }

    @Override
    public void beginIncludeElement() {
        assert glueCursor.getCurrentElement().getTagName().equals("fx:include");

        final TransientIntrinsic transientIntrinsic
                = new TransientIntrinsic(currentTransientNode,
                FXOMIntrinsic.Type.FX_INCLUDE, glueCursor.getCurrentElement());

        currentTransientNode = transientIntrinsic;
        glueCursor.moveToNextElement();
    }

    @Override
    public void beginReferenceElement() {
        assert glueCursor.getCurrentElement().getTagName().equals("fx:reference");

        final TransientIntrinsic transientIntrinsic
                = new TransientIntrinsic(currentTransientNode,
                FXOMIntrinsic.Type.FX_REFERENCE, glueCursor.getCurrentElement());

        currentTransientNode = transientIntrinsic;
        glueCursor.moveToNextElement();
    }

    @Override
    public void beginCopyElement() {
        assert glueCursor.getCurrentElement().getTagName().equals("fx:copy");

        final TransientIntrinsic transientIntrinsic
                = new TransientIntrinsic(currentTransientNode,
                FXOMIntrinsic.Type.FX_COPY, glueCursor.getCurrentElement());

        currentTransientNode = transientIntrinsic;
        glueCursor.moveToNextElement();
    }

    @Override
    public void beginRootElement() {
        assert glueCursor.getCurrentElement().getTagName().equals("fx:root");

        final TransientObject transientInstance
                = new TransientObject(currentTransientNode,
                glueCursor.getCurrentElement());

        currentTransientNode = transientInstance;
        glueCursor.moveToNextElement();
    }

    @Override
    public void beginPropertyElement(String name, Class<?> staticClass) {
        assert name != null;

        final TransientProperty transientProperty
                = new TransientProperty(currentTransientNode,
                    new PropertyName(name, staticClass),
                    glueCursor.getCurrentElement());

        currentTransientNode = transientProperty;
        glueCursor.moveToNextElement();
    }

    @Override
    public void beginUnknownStaticPropertyElement(String string) {
        currentTransientNode = new TransientIgnored(currentTransientNode);
        glueCursor.moveToNextElement();
    }

    @Override
    public void beginScriptElement() {
        currentTransientNode = new TransientIgnored(currentTransientNode);
        glueCursor.moveToNextElement();
    }

    @Override
    public void beginDefineElement() {
        currentTransientNode = new TransientIgnored(currentTransientNode);
        glueCursor.moveToNextElement();
    }

    @Override
    public void readInternalAttribute(String attrName, String attrValue) {
        assert currentTransientNode instanceof TransientObject ||
               currentTransientNode instanceof TransientIntrinsic;

        if (attrName.equals("type")) {
            assert currentTransientNode instanceof TransientObject;
            final TransientObject transientObject = (TransientObject) currentTransientNode;
            transientObject.setFxRootType(attrValue);
        }
    }

    @Override
    public void readPropertyAttribute(String name, Class<?> staticClass, String fxmlValue) {
        assert currentTransientNode instanceof TransientObject
                || currentTransientNode instanceof TransientIntrinsic
                || currentTransientNode instanceof TransientProperty;

        assert name != null;

        final PropertyName pname = new PropertyName(name, staticClass);
        final FXOMPropertyT fxomProperty = new FXOMPropertyT(document, pname, null, null, fxmlValue);

        if (currentTransientNode instanceof TransientObject) {
            final TransientObject transientInstance = (TransientObject) currentTransientNode;
            transientInstance.getProperties().add(fxomProperty);
        } else if (currentTransientNode instanceof TransientProperty) {
            final TransientProperty transientProperty = (TransientProperty) currentTransientNode;
            transientProperty.getCollectedProperties().add(fxomProperty);
        } else if(currentTransientNode instanceof  TransientIntrinsic) {
            final TransientIntrinsic transientIntrinsic = (TransientIntrinsic) currentTransientNode;
            transientIntrinsic.getProperties().add(fxomProperty);
        }
    }

    @Override
    public void readUnknownStaticPropertyAttribute(String string, String string1) {
        // TODO(elp) : implement FXOMLoader.readUnknownStaticPropertyAttribute.
    }

    @Override
    public void readEventHandlerAttribute(String name, String hashStatement) {
        // Same as readPropertyAttribute() 
        readPropertyAttribute(name, null, hashStatement);
    }

    @Override
    public void endElement(Object sceneGraphObject) {

        currentTransientNode.setSceneGraphObject(sceneGraphObject);

        if (currentTransientNode instanceof TransientObject) {
            final TransientObject currentInstance = (TransientObject) currentTransientNode;
            final FXOMObject currentFxomObject = currentInstance.makeFxomObject(document);
            final TransientNode currentParent = currentInstance.getParentNode();
            if (currentParent instanceof TransientProperty) {
                final TransientProperty parentProperty = (TransientProperty) currentParent;
                parentProperty.getValues().add(currentFxomObject);
            } else if (currentParent instanceof TransientObject) {
                final TransientObject parentInstance = (TransientObject) currentParent;
                parentInstance.getCollectedItems().add(currentFxomObject);
            } else if (currentParent instanceof TransientIgnored) {
                // currentObject is an object inside an fx:define section
                // Nothing to do for now
            } else {
                assert currentParent == null;
                document.updateRoots(currentFxomObject, currentFxomObject.getSceneGraphObject());
            }

        } else if (currentTransientNode instanceof TransientIntrinsic) {
            final TransientIntrinsic currentIntrinsic = (TransientIntrinsic) currentTransientNode;
            final FXOMIntrinsic currentFxomIntrinsic = currentIntrinsic.makeFxomIntrinsic(document);
            final TransientNode currentParent = currentIntrinsic.getParentNode();

            if (currentParent instanceof TransientProperty) {
                final TransientProperty parentProperty = (TransientProperty) currentParent;
                parentProperty.getValues().add(currentFxomIntrinsic);
            } else if (currentParent instanceof TransientObject) {
                final TransientObject parentInstance = (TransientObject) currentParent;
                parentInstance.getCollectedItems().add(currentFxomIntrinsic);
            } else if (currentParent instanceof TransientIgnored) {
                // currentObject is an object inside an fx:define section
                // Nothing to do for now
            } else {
                assert currentParent == null;
                document.updateRoots(currentFxomIntrinsic, currentFxomIntrinsic.getSceneGraphObject());
            }
        } else if (currentTransientNode instanceof TransientProperty) {
            final TransientProperty currentProperty = (TransientProperty) currentTransientNode;
            final TransientNode currentParent = currentProperty.getParentNode();
            final FXOMProperty currentFxomProperty = currentProperty.makeFxomProperty(document);
            assert currentParent instanceof TransientObject;
            if (currentParent instanceof TransientObject){
            final TransientObject parentObject = (TransientObject) currentParent;
            parentObject.getProperties().add(currentFxomProperty);
        }
        else if(currentParent instanceof TransientIntrinsic) {
                final TransientIntrinsic transientIntrinsic = (TransientIntrinsic) currentParent;
                transientIntrinsic.getProperties().add(currentFxomProperty);
            }

            // We ignore sceneGraphObject
        } else {
                assert currentTransientNode instanceof TransientIgnored;
                // Nothing to do in this case
        }

        currentTransientNode = currentTransientNode.getParentNode();
    }
}


<|start_filename|>app/src/main/java/com/oracle/javafx/scenebuilder/app/CssPanelDelegate.java<|end_filename|>
/*
 * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */

package com.oracle.javafx.scenebuilder.app;

import com.oracle.javafx.scenebuilder.kit.editor.panel.css.CssPanelController;
import com.oracle.javafx.scenebuilder.kit.editor.panel.inspector.InspectorPanelController;
import com.oracle.javafx.scenebuilder.kit.editor.panel.inspector.InspectorPanelController.SectionId;
import com.oracle.javafx.scenebuilder.kit.metadata.property.ValuePropertyMetadata;
import javafx.application.Platform;

/**
 * Implements the interface with the css panel.
 */
public class CssPanelDelegate extends CssPanelController.Delegate {

    private final InspectorPanelController inspectorController;
    private final DocumentWindowController documentWindowController;

    public CssPanelDelegate(InspectorPanelController inspectorController, DocumentWindowController documentWindowController) {
        this.inspectorController = inspectorController;
        this.documentWindowController = documentWindowController;
    }

    @Override
    public void revealInspectorEditor(ValuePropertyMetadata propMeta) {
        if (inspectorController == null || documentWindowController == null
                || propMeta == null) {
            return;
        }

        // Show the inspector if it is hidden
        if (!documentWindowController.isRightPanelVisible()) {
            documentWindowController.performControlAction(DocumentWindowController.DocumentControlAction.TOGGLE_RIGHT_PANEL);
        }
        // Expand the inspector section
        String inspectorSection = propMeta.getInspectorPath().getSectionTag();
        if (inspectorSection.equalsIgnoreCase("properties")) { //NOI18N
            inspectorController.setExpandedSection(SectionId.PROPERTIES);
        } else if (inspectorSection.equalsIgnoreCase("layout")) {//NOI18N
            inspectorController.setExpandedSection(SectionId.LAYOUT);
        } else if (inspectorSection.equalsIgnoreCase("code")) {//NOI18N
            inspectorController.setExpandedSection(SectionId.CODE);
        }

        // Need to delay the focus to the editor, so that the section is actually expanded first.
        Platform.runLater(() -> Platform.runLater(() -> inspectorController.setFocusToEditor(propMeta.getName())));
    }

}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/content/util/CardinalPoint.java<|end_filename|>
/*
 * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.kit.editor.panel.content.util;

import javafx.geometry.BoundingBox;
import javafx.geometry.Bounds;
import javafx.geometry.Point2D;

/**
 *
 * 
 */
public enum CardinalPoint {
    N, NE, E, SE, S, SW, W, NW;
    
    
    public CardinalPoint getOpposite() {
        final CardinalPoint result;
        
        switch(this) {
            case N:
                result = S;
                break;
            case NE:
                result = SW;
                break;
            case E:
                result = W;
                break;
            case SE:
                result = NW;
                break;
            case S:
                result = N;
                break;
            case SW:
                result = NE;
                break;
            case W:
                result = E;
                break;
            case NW:
                result = SE;
                break;
            default:
                assert false : "unexpected cardinal point:" + this;
                result = N;
                break;
        }
        
        return result;
    }
    
    
    public Point2D getPosition(Bounds bounds) {
        final double x, y;
        
        switch(this) {
            case N:
                x = (bounds.getMinX() + bounds.getMaxX()) / 2.0;
                y = bounds.getMinY();
                break;
            case NE:
                x = bounds.getMaxX();
                y = bounds.getMinY();
                break;
            case E:
                x = bounds.getMaxX();
                y = (bounds.getMinY() + bounds.getMaxY()) / 2.0;
                break;
            case SE:
                x = bounds.getMaxX();
                y = bounds.getMaxY();
                break;
            case S:
                x = (bounds.getMinX() + bounds.getMaxX()) / 2.0;
                y = bounds.getMaxY();
                break;
            case SW:
                x = bounds.getMinX();
                y = bounds.getMaxY();
                break;
            case W:
                x = bounds.getMinX();
                y = (bounds.getMinY() + bounds.getMaxY()) / 2.0;
                break;
            case NW:
                x = bounds.getMinX();
                y = bounds.getMinY();
                break;
            default:
                assert false : "unexpected cardinal point:" + this;
                x = bounds.getMinX();
                y = bounds.getMinY();
                break;
        }
        
        return new Point2D(x, y);
    }
    
    
    public Bounds getResizedBounds(Bounds currentBounds, double dx, double dy) {
        /*
         * 
         *        NW                N                NE
         *         *----------------*----------------*
         *         |                                 |
         *         |                                 |
         *         |                                 |
         *       W *                                 * E
         *         |                                 |
         *         |                                 |
         *         |                                 |
         *         *----------------*----------------*
         *        SW                S                SE
         * 
         * 
         */

        // x axis
        final double minX = currentBounds.getMinX();
        final double maxX = currentBounds.getMaxX();
        final double newMinX, newMaxX;
        switch(this) {
            case NW:
            case  W:
            case SW:
                newMinX = Math.min(minX + dx, maxX);
                newMaxX = maxX;
                break;
            case NE:
            case  E:
            case SE:
                newMinX = minX;
                newMaxX = Math.max(maxX + dx, minX);
                break;
            case N:
            case S:
                newMinX = minX;
                newMaxX = maxX;
                break;
            default:
                // Emergency code
                assert false : "unexpected value=" + this;
                newMinX = minX;
                newMaxX = maxX;
                break;
        }
        
        // y axis
        final double minY = currentBounds.getMinY();
        final double maxY = currentBounds.getMaxY();
        final double newMinY, newMaxY;
        switch(this) {
            case NW:
            case N:
            case NE:
                newMinY = Math.min(minY + dy, maxY);
                newMaxY = maxY;
                break;
            case SE:
            case S:
            case SW:
                newMinY = minY;
                newMaxY = Math.max(maxY + dy, minY);
                break;
            case  E:
            case  W:
                newMinY = minY;
                newMaxY = maxY;
                break;
            default:
                // Emergency code
                assert false : "unexpected value=" + this;
                newMinY = minY;
                newMaxY = maxY;
                break;
        }

        return new BoundingBox(newMinX, newMinY, newMaxX - newMinX, newMaxY - newMinY);
    }
    
    
    public Point2D clampVector(double dx, double dy) {
        final double resultDX, resultDY;
        
        switch(this) {
            case N:
            case S:
                resultDX = 0.0;
                resultDY = dy;
                break;
            case E:
            case W:
                resultDX = dx;
                resultDY = 0.0;
                break;
            default:
                resultDX = dx;
                resultDY = dy;
                break;
        }
        
        return new Point2D(resultDX, resultDY);
    }
    
    
    public Bounds snapBounds(Bounds bounds, double ratio) {
        /*
         * 
         *        NW                N                NE
         *         *----------------*----------------*
         *         |                                 |
         *         |                                 |
         *         |                                 |
         *       W *                                 * E
         *         |                                 |
         *         |                                 |
         *         |                                 |
         *         *----------------*----------------*
         *        SW                S                SE
         * 
         * 
         */
        
        final double minX = bounds.getMinX();
        final double minY = bounds.getMinY();
        final double maxX = bounds.getMaxX();
        final double maxY = bounds.getMaxY();
        final double snapWidth = bounds.getHeight() / ratio;
        final double snapDX = snapWidth - bounds.getWidth();
        final double snapHeight = bounds.getWidth() * ratio;
        final double snapDY = snapHeight - bounds.getHeight();
        final double newMinX, newMinY, newMaxX, newMaxY;

        // x axis
        switch(this) {
            case N:
            case S:
                newMinX = minX - snapDX / 2.0;
                newMaxX = maxX + snapDX / 2.0;
                break;
            case E:
            case W:
                newMinX = minX;
                newMaxX = maxX;
                break;
            case NW:
            case SW:
                if (Math.abs(snapDX) >= Math.abs(snapDY)) {
                    newMinX = minX - snapDX;
                } else {
                    newMinX = minX;
                }
                newMaxX = maxX;
                break;
            case NE:
            case SE:
                newMinX = minX;
                if (Math.abs(snapDX) >= Math.abs(snapDY)) {
                    newMaxX = maxX + snapDX;
                } else {
                    newMaxX = maxX;
                }
                break;
            default:
                // Emergency code
                assert false : "Unexpected " + this;
                newMinX = minX;
                newMaxX = maxX;
                break;
        }

        // y axis
        switch(this) {
            case N:
            case S:
                newMinY = minY;
                newMaxY = maxY;
                break;
            case E:
            case W:
                newMinY = minY - snapDY / 2.0;
                newMaxY = maxY + snapDY / 2.0;
                break;
            case NW:
            case NE:
                if (Math.abs(snapDY) > Math.abs(snapDX)) {
                    newMinY = minY - snapDY;
                } else {
                    newMinY = minY;
                }
                newMaxY = maxY;
                break;
            case SW:
            case SE:
                newMinY = minY;
                if (Math.abs(snapDY) > Math.abs(snapDX)) {
                    newMaxY = maxY + snapDY;
                } else {
                    newMaxY = maxY;
                }
                break;
            default:
                // Emergency code
                assert false : "Unexpected " + this;
                newMinY = minY;
                newMaxY = maxY;
                break;
        }

        return new BoundingBox(newMinX, newMinY, newMaxX - newMinX, newMaxY - newMinY);
    }
}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/ResourceUtils.java<|end_filename|>
/*
 * Copyright (c) 2017 Gluon and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation and Gluon nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */

package com.oracle.javafx.scenebuilder.kit;

import javafx.collections.ObservableList;
import javafx.scene.Parent;
import javafx.scene.Scene;

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

public class ResourceUtils {

    public static final String BASE = ResourceUtils.class.getResource("css/Base.css").toExternalForm();
    public static final String THEME_DARK_STYLESHEET = ResourceUtils.class.getResource("css/ThemeDark.css").toExternalForm();
    public static final String THEME_DEFAULT_STYLESHEET = ResourceUtils.class.getResource("css/ThemeDefault.css").toExternalForm();
    private static List<String> imageExtensions;
    private static List<String> audioExtensions;
    private static List<String> videoExtensions;
    private static List<String> mediaExtensions;

    public static String getToolStylesheet(ToolTheme theme) {
        switch(theme) {
            case DARK:
                return THEME_DARK_STYLESHEET;
            case DEFAULT:
                return THEME_DEFAULT_STYLESHEET;
        }
        return null;
    }

    public static void setToolTheme(ToolTheme theme, Scene scene) {
        setToolTheme(theme, scene.getStylesheets());
    }

    public static void setToolTheme(ToolTheme theme, Parent parent) {
        setToolTheme(theme, parent.getStylesheets());
    }

    private static void setToolTheme(ToolTheme theme, ObservableList<String> stylesheets) {
        if (!stylesheets.contains(BASE)) {
            stylesheets.add(BASE);
        }
        String themeStylesheet = getToolStylesheet(theme);
        if (!stylesheets.contains(themeStylesheet)) {
            stylesheets.add(themeStylesheet);
        }
    }

    public static List<String> getSupportedImageExtensions() {
        if (imageExtensions == null) {
            imageExtensions = new ArrayList<>();
            imageExtensions.add("*.jpg"); //NOI18N
            imageExtensions.add("*.jpeg"); //NOI18N
            imageExtensions.add("*.png"); //NOI18N
            imageExtensions.add("*.gif"); //NOI18N
            imageExtensions = Collections.unmodifiableList(imageExtensions);
        }
        return imageExtensions;
    }

    public static List<String> getSupportedAudioExtensions() {
        if (audioExtensions == null) {
            audioExtensions = new ArrayList<>();
            audioExtensions.add("*.aif"); //NOI18N
            audioExtensions.add("*.aiff"); //NOI18N
            audioExtensions.add("*.mp3"); //NOI18N
            audioExtensions.add("*.m4a"); //NOI18N
            audioExtensions.add("*.wav"); //NOI18N
            audioExtensions.add("*.m3u"); //NOI18N
            audioExtensions.add("*.m3u8"); //NOI18N
            audioExtensions = Collections.unmodifiableList(audioExtensions);
        }
        return audioExtensions;
    }

    public static List<String> getSupportedVideoExtensions() {
        if (videoExtensions == null) {
            videoExtensions = new ArrayList<>();
            videoExtensions.add("*.flv"); //NOI18N
            videoExtensions.add("*.fxm"); //NOI18N
            videoExtensions.add("*.mp4"); //NOI18N
            videoExtensions.add("*.m4v"); //NOI18N
            videoExtensions = Collections.unmodifiableList(videoExtensions);
        }
        return videoExtensions;
    }

    public static List<String> getSupportedMediaExtensions() {
        if (mediaExtensions == null) {
            mediaExtensions = new ArrayList<>();
            mediaExtensions.addAll(getSupportedImageExtensions());
            mediaExtensions.addAll(getSupportedAudioExtensions());
            mediaExtensions.addAll(getSupportedVideoExtensions());
            mediaExtensions = Collections.unmodifiableList(mediaExtensions);
        }
        return mediaExtensions;
    }
}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/content/driver/gridpane/GridPaneHandles.java<|end_filename|>
/*
 * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.kit.editor.panel.content.driver.gridpane;

import com.oracle.javafx.scenebuilder.kit.editor.panel.content.ContentPanelController;
import com.oracle.javafx.scenebuilder.kit.editor.panel.content.driver.handles.AbstractHandles;
import com.oracle.javafx.scenebuilder.kit.editor.panel.content.driver.handles.AbstractNodeHandles;
import com.oracle.javafx.scenebuilder.kit.editor.panel.content.gesture.AbstractGesture;
import com.oracle.javafx.scenebuilder.kit.editor.panel.content.gesture.mouse.ResizeColumnGesture;
import com.oracle.javafx.scenebuilder.kit.editor.panel.content.gesture.mouse.ResizeRowGesture;
import com.oracle.javafx.scenebuilder.kit.editor.panel.content.gesture.mouse.SelectAndMoveInGridGesture;
import com.oracle.javafx.scenebuilder.kit.editor.selection.GridSelectionGroup;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMInstance;
import java.util.Collections;
import javafx.scene.Node;
import javafx.scene.layout.GridPane;

/**
 *
 */
public class GridPaneHandles extends AbstractNodeHandles<GridPane> {
    
    private final GridPaneMosaic mosaic 
            = new GridPaneMosaic("handles", //NOI18N
                    true /* shouldShowTray */,
                    true /* shouldCreateSensors */ );
    
    public GridPaneHandles(ContentPanelController contentPanelController,
            FXOMInstance fxomInstance) {
        super(contentPanelController, fxomInstance, GridPane.class);
        
        getRootNode().getChildren().add(0, mosaic.getTopGroup()); // Below handles
    }
    
    public void updateColumnRowSelection(GridSelectionGroup gsg) {
        
        if (gsg == null) {
            mosaic.setSelectedColumnIndexes(Collections.emptySet());
            mosaic.setSelectedRowIndexes(Collections.emptySet());
        } else {
            switch(gsg.getType()) {
                case COLUMN:
                    mosaic.setSelectedColumnIndexes(gsg.getIndexes());
                    mosaic.setSelectedRowIndexes(Collections.emptySet());
                    break;
                case ROW:
                    mosaic.setSelectedColumnIndexes(Collections.emptySet());
                    mosaic.setSelectedRowIndexes(gsg.getIndexes());
                    break;
                default:
                    assert false;
                    break;
            }
        }
    }
    
    
    /*
     * AbstractNodeHandles
     */
    @Override
    public void layoutDecoration() {
        super.layoutDecoration();
                
        if (mosaic.getGridPane() != getSceneGraphObject()) {
            mosaic.setGridPane(getSceneGraphObject());
        } else {
            mosaic.update();
        }
        
        // Mosaic update may have created new trays and new sensors. 
        // Attach this handles to them.
        for (Node node : this.mosaic.getNorthTrayNodes()) {
            attachHandles(node);
        }
        for (Node node : this.mosaic.getSouthTrayNodes()) {
            attachHandles(node);
        }
        for (Node node : this.mosaic.getEastTrayNodes()) {
            attachHandles(node);
        }
        for (Node node : this.mosaic.getWestTrayNodes()) {
            attachHandles(node);
        }
        for (Node node : this.mosaic.getHgapSensorNodes()) {
            attachHandles(node);
        }
        for (Node node : this.mosaic.getVgapSensorNodes()) {
            attachHandles(node);
        }
        
        // Update mosaic transform
        mosaic.getTopGroup().getTransforms().clear();
        mosaic.getTopGroup().getTransforms().add(getSceneGraphObjectToDecorationTransform());
    }

    @Override
    public AbstractGesture findGesture(Node node) {
        AbstractGesture result = findGestureInTrays(node);
        if (result == null) {
            result = findGestureInSensors(node);
        }
        
        return result;
    }
    
    
    private AbstractGesture findGestureInTrays(Node node) {
        final GridSelectionGroup.Type feature;
        
        int trayIndex = mosaic.getNorthTrayNodes().indexOf(node);
        if (trayIndex != -1) {
            feature = GridSelectionGroup.Type.COLUMN;
        } else {
            trayIndex = mosaic.getSouthTrayNodes().indexOf(node);
            if (trayIndex != -1) {
                feature = GridSelectionGroup.Type.COLUMN;
            } else {
                trayIndex = mosaic.getWestTrayNodes().indexOf(node);
                if (trayIndex != -1) {
                    feature = GridSelectionGroup.Type.ROW;
                } else {
                    trayIndex = mosaic.getEastTrayNodes().indexOf(node);
                    feature = GridSelectionGroup.Type.ROW;
                }
            }
        }
        
        final AbstractGesture result;
        if (trayIndex == -1) {
            result = super.findGesture(node);
        } else {
            result = new SelectAndMoveInGridGesture(getContentPanelController(),
                    getFxomInstance(), feature, trayIndex);
        }
        
        return result;
    }
    
    
    private AbstractGesture findGestureInSensors(Node node) {
        final AbstractGesture result;
        
        int sensorIndex = mosaic.getHgapSensorNodes().indexOf(node);
        if (sensorIndex != -1) {
            result = new ResizeColumnGesture(this, sensorIndex);
        } else {
            sensorIndex = mosaic.getVgapSensorNodes().indexOf(node);
            if (sensorIndex != -1) {
                result = new ResizeRowGesture(this, sensorIndex);
            } else {
                result = super.findGesture(node);
            }
        }
        
        return result;
    }

    
    /*
     * Private
     */
    
    /* 
     * Wrapper to avoid the 'leaking this in constructor' warning emitted by NB.
     */
    private void attachHandles(Node node) {
        if (AbstractHandles.lookupHandles(node) == null) {
            attachHandles(node, this);
        }
    }
}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/inspector/editors/InlineListEditor.java<|end_filename|>
/*
 * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.kit.editor.panel.inspector.editors;

import com.oracle.javafx.scenebuilder.kit.metadata.property.ValuePropertyMetadata;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import javafx.event.EventHandler;
import javafx.scene.Node;
import javafx.scene.layout.VBox;

/**
 * Generic class for property editors based on a list of inline items.
 * 
 * 
 */
public abstract class InlineListEditor extends PropertyEditor implements EditorItemDelegate {
    private final VBox vbox = new VBox(1);
    private final List<EditorItem> editorItems = new ArrayList<>();

    public InlineListEditor(ValuePropertyMetadata propMeta, Set<Class<?>> selectedClasses) {
        super(propMeta, selectedClasses);
        setLayoutFormat(PropertyEditor.LayoutFormat.DOUBLE_LINE);
    }

    @Override
    public Node getValueEditor() {
        return super.handleGenericModes(vbox);
    }

    @Override
    public void reset(ValuePropertyMetadata propMeta, Set<Class<?>> selectedClasses) {
        reset(propMeta, selectedClasses, true);
    }
    
    public void reset(ValuePropertyMetadata propMeta, Set<Class<?>> selectedClasses, boolean removeAll) {
        super.reset(propMeta, selectedClasses);
        setLayoutFormat(PropertyEditor.LayoutFormat.DOUBLE_LINE);
        reset(removeAll);
    }

    @Override
    protected void valueIsIndeterminate() {
        // Set all the items as undeterminate, whathever is the value
        for (EditorItem editorItem : editorItems) {
            editorItem.setValueAsIndeterminate();
        }
    }

    @Override
    public void commit(EditorItem source) {
//        System.out.println("COMMIT");
        userUpdateValueProperty(getValue());
    }

    @Override
    public void editing(boolean editing, EventHandler<?> editingHandler) {
        editingProperty().setValue(editing);
        setCommitListener(editingHandler);
    }

    @Override
    public void add(EditorItem previousItem, EditorItem newItem) {
//        System.out.println("ADD");
        addItem(previousItem, newItem);
        userUpdateValueProperty(getValue());
    }

    @Override
    public void remove(EditorItem source) {
        // By default, keep 1 item
        remove(source, false);
    }
    
    public void remove(EditorItem source, boolean removeAll) {
//        System.out.println("REMOVE");
        removeItem(source, removeAll);
        userUpdateValueProperty(getValue());
    }

    @Override
    public void up(EditorItem source) {
//        System.out.println("UP");
        upItem(source);
        userUpdateValueProperty(getValue());
    }

    @Override
    public void down(EditorItem source) {
//        System.out.println("DOWN");
        downItem(source);
        userUpdateValueProperty(getValue());
    }

    final protected List<EditorItem> getEditorItems() {
        return editorItems;
    }
    
    final protected EditorItem addItem(EditorItem newItem) {
        return addItem(null, newItem);
    }

    final protected EditorItem addItem(EditorItem previousItem, EditorItem newItem) {
        int index = -1;
        if (previousItem != null) {
            // Add the new item under the source EditorItem
            index = editorItems.indexOf(previousItem) + 1;
        }
        if (index != -1) {
            editorItems.add(index, newItem);
            vbox.getChildren().add(index, newItem.getNode());
        } else {
            editorItems.add(newItem);
            vbox.getChildren().add(newItem.getNode());
        }
        updateMenuItems();
        return newItem;
    }

    protected void removeItem(EditorItem editorItem) {
        // By default, keep 1 item
        removeItem(editorItem, false);
    }
    
    protected void removeItem(EditorItem editorItem, boolean removeAll) {
        if (!removeAll && editorItems.size() == 1) {
            // Do not remove last item, but reset it
            editorItem.reset();
            if (editorItem.getRemoveMenuItem() != null) {
                editorItem.getRemoveMenuItem().setDisable(true);
            }
            return;
        }
        editorItems.remove(editorItem);
        vbox.getChildren().remove(editorItem.getNode());
        updateMenuItems();
    }

    protected void upItem(EditorItem editorItem) {
        int indexItem = editorItems.indexOf(editorItem);
        // item should be in the list, and not the 1st item
        assert (indexItem != -1) && (indexItem != 0);
        assert vbox.getChildren().indexOf(editorItem.getNode()) == indexItem;
        Collections.swap(editorItems, indexItem, indexItem - 1);
        EditorUtils.swap(vbox.getChildren(), indexItem, indexItem - 1);
        vbox.requestLayout();
        updateMenuItems();
    }

    protected void downItem(EditorItem editorItem) {
        int indexItem = editorItems.indexOf(editorItem);
        // item should be in the list, and not the last item
        assert (indexItem != -1) && (indexItem != editorItems.size() - 1);
        assert vbox.getChildren().indexOf(editorItem.getNode()) == indexItem;
        Collections.swap(editorItems, indexItem, indexItem + 1);
        EditorUtils.swap(vbox.getChildren(), indexItem, indexItem + 1);
        updateMenuItems();
    }

    protected void reset() {
        // By default, keep 1 item
        reset(false);
    }
    
    protected void reset(boolean removeAll) {
        List<EditorItem> items = new ArrayList<>(editorItems);
        for (EditorItem editorItem : items) {
            removeItem(editorItem, removeAll);
        }
        updateMenuItems();
    }

    private void updateMenuItems() {
        for (int ii = 0; ii < editorItems.size(); ii++) {
            EditorItem item = editorItems.get(ii);
            if (item.getMoveUpMenuItem() == null || item.getMoveDownMenuItem() == null) {
                continue;
            }
            if (ii == 0) {
                // first item
                item.getMoveUpMenuItem().setDisable(true);
            } else {
                item.getMoveUpMenuItem().setDisable(false);
            }
            if (ii == (editorItems.size() - 1)) {
                // last item
                item.getMoveDownMenuItem().setDisable(true);
            } else {
                item.getMoveDownMenuItem().setDisable(false);
            }
        }
    }

}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/util/control/paintpicker/gradientpicker/GradientPickerStop.java<|end_filename|>
/*
 * Copyright (c) 2016, Gluon and/or its affiliates.
 * Copyright (c) 2012, 2015, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.kit.util.control.paintpicker.gradientpicker;

import com.oracle.javafx.scenebuilder.kit.util.control.paintpicker.PaintPicker.Mode;
import com.oracle.javafx.scenebuilder.kit.util.control.paintpicker.PaintPickerController;
import com.oracle.javafx.scenebuilder.kit.util.control.paintpicker.colorpicker.ColorPicker;

import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.geometry.Side;
import javafx.scene.image.ImageView;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;

import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;

import javafx.beans.value.ChangeListener;
import javafx.event.ActionEvent;
import javafx.scene.control.Button;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.CustomMenuItem;
import javafx.scene.control.TextField;
import javafx.scene.paint.Paint;

/**
 * Controller class for the gradient editor stop.
 */
public class GradientPickerStop extends VBox {

    @FXML
    private Rectangle chip_rect;
    @FXML
    private ImageView indicator_image;
    @FXML
    private TextField offset_textfield;
    @FXML
    private ContextMenu context_menu;
    @FXML
    private CustomMenuItem custom_menu_item;
    @FXML
    private Button stop_button;

    private final double min;
    private final double max;
    private double offset;
    private Color color;
    private boolean isSelected;
    private double origX;
    private double startDragX;
    private double thumbWidth;
    private final double edgeMargin = 2.0;
    private final GradientPicker gradientPicker;

    /*
     * Clamp value to be between min and max.
     */
    private static double clamp(double min, double value, double max) {
        if (value < min) return min;
        if (value > max) return max;
        return value;
    }

    public GradientPickerStop(GradientPicker ge, double mini, double maxi, double val, Color c) {
        gradientPicker = ge;
        min = mini;
        max = maxi;
        offset = val;
        color = c;
        initialize();
    }

    public void setOffset(double val) {
        offset = clamp(min, val, max);
        valueToPixels();
    }

    public double getOffset() {
        return offset;
    }

    public void setColor(Color c) {
        color = c;
        chip_rect.setFill(c);
    }

    public Color getColor() {
        return color;
    }

    public void setSelected(boolean selected) {
        isSelected = selected;
        if (selected) {
            indicator_image.setVisible(true);
        } else {
            indicator_image.setVisible(false);
        }
    }

    public boolean isSelected() {
        return isSelected;
    }

    private void initialize() {

        final FXMLLoader loader = new FXMLLoader();
        loader.setLocation(GradientPickerStop.class.getResource("GradientPickerStop.fxml")); //NOI18N
        loader.setController(this);
        loader.setRoot(this);
        try {
            loader.load();
        } catch (IOException ex) {
            Logger.getLogger(GradientPicker.class.getName()).log(Level.SEVERE, null, ex);
        }

        assert offset_textfield != null;
        assert chip_rect != null;

        offset_textfield.setText("" + offset); //NOI18N

        chip_rect.setFill(color);
        gradientPicker.setSelectedStop(this);
        
        stop_button.setOnAction((ActionEvent event) -> {
            event.consume();
        });

        // when we detect a width change, we know node layout is resolved so we position stop in track
        widthProperty().addListener((ChangeListener<Number>) (ov, oldValue, newValue) -> {
            if (newValue.doubleValue() > 0) {
                thumbWidth = newValue.doubleValue();
                valueToPixels();
            }
        });
    }
    
    @FXML
    void stopAction(ActionEvent event) {
        double val = Double.valueOf(offset_textfield.getText());
        setOffset(val);
        showHUD();
        // Called when moving a gradient stop :
        // - update gradient preview accordingly
        // - update model
        final PaintPickerController paintPicker
                = gradientPicker.getPaintPickerController();
        final Mode mode = paintPicker.getMode();
        final Paint value = gradientPicker.getValue(mode);
        gradientPicker.updatePreview(value);
        // Update model
        paintPicker.setPaintProperty(value);
    }

    @FXML
    void thumbKeyPressed(KeyEvent e) {
        if (e.getCode() == KeyCode.BACK_SPACE || e.getCode() == KeyCode.DELETE) {
            gradientPicker.removeStop(this);
            // Called when removing a gradient stop :
            // - update gradient preview accordingly
            // - update model
            final PaintPickerController paintPicker
                    = gradientPicker.getPaintPickerController();
            final Mode mode = paintPicker.getMode();
            final Paint value = gradientPicker.getValue(mode);
            gradientPicker.updatePreview(value);
            // Update model
            paintPicker.setPaintProperty(value);
            e.consume();
        }
    }

    @FXML
    void thumbMousePressed(MouseEvent event) {
        gradientPicker.setSelectedStop(this);
        startDragX = event.getSceneX();
        origX = getLayoutX();
        toFront(); // make sure this stop is in highest z-order
//        showHUD();
        pixelsToValue();
        // Called when selecting a gradient stop :
        // - update color preview accordingly
        // - do not update the model
        final PaintPickerController paintPicker
                = gradientPicker.getPaintPickerController();
        final ColorPicker colorPicker = paintPicker.getColorPicker();
        colorPicker.updateUI(color);
        stop_button.requestFocus();
    }

    @FXML
    void thumbMouseReleased() {
        pixelsToValue();
    }

    @FXML
    void thumbMouseDragged(MouseEvent event) {
        double dragValue = event.getSceneX() - startDragX;
        double deltaX = origX + dragValue;
        double trackWidth = getParent().getBoundsInLocal().getWidth();
        final Double newX = clamp(edgeMargin, deltaX, (trackWidth - (getWidth() + edgeMargin)));
        setLayoutX(newX);
//        showHUD();
        pixelsToValue();
        // Called when moving a gradient stop :
        // - update gradient preview accordingly
        // - update model
        final PaintPickerController paintPicker
                = gradientPicker.getPaintPickerController();
        final Mode mode = paintPicker.getMode();
        final Paint value = gradientPicker.getValue(mode);
        gradientPicker.updatePreview(value);
        // Update model
        paintPicker.setPaintProperty(value);
    }

    private void showHUD() {
        offset_textfield.setText(Double.toString(offset));
        context_menu.show(this, Side.BOTTOM, 0, 5); // better way to center?
    }

    private void valueToPixels() {
        double stopValue = clamp(min, offset, max);
        double availablePixels = getParent().getLayoutBounds().getWidth() - (thumbWidth + edgeMargin);
        double range = max - min;
        double pixelPosition = ((availablePixels / range) * stopValue);
        setLayoutX(pixelPosition);
    }

    private void pixelsToValue() {
        double range = max - min;
        double availablePixels = getParent().getLayoutBounds().getWidth() - (thumbWidth + edgeMargin);
        setOffset(min + (getLayoutX() * (range / availablePixels)));
    }
}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/content/driver/rudder/ResizeRudder.java<|end_filename|>
/*
 * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.kit.editor.panel.content.driver.rudder;

import javafx.geometry.Bounds;
import javafx.geometry.Point2D;
import javafx.scene.Node;
import javafx.scene.shape.Line;

import com.oracle.javafx.scenebuilder.kit.editor.panel.content.ContentPanelController;
import com.oracle.javafx.scenebuilder.kit.editor.panel.content.util.LineEquation;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMObject;

/**
 *
 * 
 */
public class ResizeRudder extends AbstractRudder<Node> {
    
    private final Line diagonalLine = new Line();

    public ResizeRudder(ContentPanelController contentPanelController, 
            FXOMObject fxomObject) {
        super(contentPanelController, fxomObject, Node.class);
        
        diagonalLine.setMouseTransparent(true);
        diagonalLine.getStyleClass().add("resize-rudder"); //NOI18N
        getRootNode().getChildren().add(diagonalLine);
    }
    
    
    /*
     * AbstractRudder
     */
    @Override
    public Bounds getSceneGraphObjectBounds() {
        return getSceneGraphObject().getLayoutBounds();
    }

    @Override
    public Node getSceneGraphObjectProxy() {
        return getSceneGraphObject();
    }

    @Override
    protected void startListeningToSceneGraphObject() {
        startListeningToLayoutBounds(getSceneGraphObject());
        startListeningToLocalToSceneTransform(getSceneGraphObject());
    }

    @Override
    protected void stopListeningToSceneGraphObject() {
        stopListeningToLayoutBounds(getSceneGraphObject());
        stopListeningToLocalToSceneTransform(getSceneGraphObject());
    }

    @Override
    protected void layoutDecoration() {
        final Bounds b = getSceneGraphObjectBounds();
        
        final boolean snapToPixel = true;
        final Point2D p0 = sceneGraphObjectToDecoration(b.getMinX(), b.getMinY(), snapToPixel);
        final Point2D p1 = sceneGraphObjectToDecoration(b.getMaxX(), b.getMaxY(), snapToPixel);
        
        final LineEquation eq = new LineEquation(p0.getX(), p0.getY(), p1.getX(), p1.getY());
        final double outset = 0.1;
        final Point2D d0 = eq.pointAtP(0.0 - outset);
        final Point2D d1 = eq.pointAtP(1.0 + outset);
        
        diagonalLine.setStartX(d0.getX());
        diagonalLine.setStartY(d0.getY());
        diagonalLine.setEndX(d1.getX());
        diagonalLine.setEndY(d1.getY());
    }


}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/fxom/FxIdCollector.java<|end_filename|>
/*
 * Copyright (c) 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */

package com.oracle.javafx.scenebuilder.kit.fxom;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 *
 */
class FxIdCollector {
    
    private final Set<String> fxIds = new HashSet<>();
    private Map<String, Integer> nextIndexes ; // Created lazily
    
    public FxIdCollector(Set<String> fxIds) {
        assert fxIds != null;
        this.fxIds.addAll(fxIds);
    }
    
    public FxIdCollector(FXOMDocument fxomDocument) {
        this(fxomDocument.collectFxIds().keySet());
    }
    
    public String importFxId(String sourceFxId) {
        assert sourceFxId != null;
        
        final String result;
        if (fxIds.contains(sourceFxId)) {
            if (nextIndexes == null) {
                createNextIndexes();
                assert nextIndexes != null;
            }
            final PrefixSuffix pf = new PrefixSuffix(sourceFxId);
            final Integer nextIndex = nextIndexes.get(pf.getPrefix());
            assert nextIndex != null;
            result = pf.getPrefix() + nextIndex;
        } else {
            result = sourceFxId;
        }
        
        fxIds.add(result);
        if (nextIndexes != null) {
            updateNextIndexes(result);
        }
        
        return result;
    }
    
    
    /*
     * Private
     */
    
    private void createNextIndexes() {
        nextIndexes = new HashMap<>();
        
        for (String fxId : fxIds) {
            updateNextIndexes(fxId);
        }
    }
    
    
    private void updateNextIndexes(String fxId) {
        assert nextIndexes != null;
        
        final PrefixSuffix pf = new PrefixSuffix(fxId);
        final Integer nextIndex = nextIndexes.get(pf.getPrefix());
        if ((nextIndex == null) || (pf.getSuffix() >= nextIndex)) {
            nextIndexes.put(pf.getPrefix(), pf.getSuffix()+1);
        } 
    }
    
    
    
    private static class PrefixSuffix {
        private final String prefix;
        private final int suffix;
        
        public PrefixSuffix(String fxId) {
            assert fxId != null;
            assert fxId.isEmpty() == false;
            
            int endIndex = fxId.length();
            while ((endIndex >= 1) && Character.isDigit(fxId.charAt(endIndex-1))) {
                endIndex--;
            }
            if (endIndex < fxId.length()) {
                this.prefix = fxId.substring(0, endIndex);
                this.suffix = Integer.parseInt(fxId.substring(endIndex));
            } else {
                this.prefix = fxId;
                this.suffix = -1;
            }
        }

        public String getPrefix() {
            return prefix;
        }

        public int getSuffix() {
            return suffix;
        }
        
        @Override
        public String toString() {
            return (suffix == -1) ? prefix : prefix+suffix;
        }
    }
}


<|start_filename|>app/src/main/java/com/oracle/javafx/scenebuilder/app/menubar/MenuBarController.java<|end_filename|>
/*
 * Copyright (c) 2016, 2017 Gluon and/or its affiliates.
 * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.app.menubar;

import com.oracle.javafx.scenebuilder.app.DocumentWindowController;
import com.oracle.javafx.scenebuilder.app.DocumentWindowController.DocumentControlAction;
import com.oracle.javafx.scenebuilder.app.DocumentWindowController.DocumentEditAction;
import com.oracle.javafx.scenebuilder.app.SceneBuilderApp;
import com.oracle.javafx.scenebuilder.app.SceneBuilderApp.ApplicationControlAction;
import com.oracle.javafx.scenebuilder.app.i18n.I18N;
import com.oracle.javafx.scenebuilder.app.preferences.PreferencesController;
import com.oracle.javafx.scenebuilder.app.preferences.PreferencesRecordGlobal;
import com.oracle.javafx.scenebuilder.kit.editor.EditorController;
import com.oracle.javafx.scenebuilder.kit.editor.EditorController.ControlAction;
import com.oracle.javafx.scenebuilder.kit.editor.EditorController.EditAction;
import com.oracle.javafx.scenebuilder.kit.editor.EditorController.Size;
import com.oracle.javafx.scenebuilder.kit.editor.EditorPlatform;
import com.oracle.javafx.scenebuilder.kit.editor.panel.content.ContentPanelController;
import com.oracle.javafx.scenebuilder.kit.library.BuiltinLibrary;
import com.oracle.javafx.scenebuilder.kit.library.BuiltinSectionComparator;
import com.oracle.javafx.scenebuilder.kit.library.LibraryItem;
import com.oracle.javafx.scenebuilder.kit.library.LibraryItemNameComparator;
import com.oracle.javafx.scenebuilder.kit.library.user.UserLibrary;
import com.oracle.javafx.scenebuilder.kit.util.MathUtils;
import com.oracle.javafx.scenebuilder.kit.util.control.effectpicker.EffectPicker;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;

import javafx.beans.value.ChangeListener;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.Event;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.control.CheckMenuItem;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.Menu;
import javafx.scene.control.MenuBar;
import javafx.scene.control.MenuItem;
import javafx.scene.control.RadioMenuItem;
import javafx.scene.control.SeparatorMenuItem;
import javafx.scene.effect.Effect;
import javafx.scene.input.KeyCharacterCombination;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyCodeCombination;
import javafx.scene.input.KeyCombination;
import javafx.scene.layout.StackPane;

/**
 *
 */
public class MenuBarController {
    
    private static MenuBarController systemMenuBarController; // For Mac only

    private Menu insertCustomMenu;
    private final DocumentWindowController documentWindowController;
    // This member is null when this MenuBarController is used for
    // managing the menu bar passed to MenuBarSkin.setDefaultSystemMenu().

    private DebugMenuController debugMenuController; // Initialized lazily

    @FXML
    private MenuBar menuBar;
    @FXML
    private Menu insertMenu;
    @FXML
    private Menu addEffectMenu;
    @FXML
    private Menu fileMenu; // Useless as soon as Preferences menu item is implemented
    @FXML
    private Menu previewMenu;
    @FXML
    private Menu windowMenu;

    // File
    @FXML
    private MenuItem newMenuItem;
    @FXML
    private MenuItem newTemplateMenuItem;
    @FXML
    private MenuItem openMenuItem;
    @FXML
    private Menu openRecentMenu;
    @FXML
    private MenuItem saveMenuItem;
    @FXML
    private MenuItem saveAsMenuItem;
    @FXML
    private MenuItem revertMenuItem;
    @FXML
    private MenuItem closeMenuItem;
    @FXML
    private MenuItem revealMenuItem;
    @FXML
    private MenuItem importFxmlMenuItem;
    @FXML
    private MenuItem importMediaMenuItem;
    @FXML
    private MenuItem includeFileMenuItem;
    @FXML
    private MenuItem editIncludedFileMenuItem;
    @FXML
    private MenuItem revealIncludedFileMenuItem;
    @FXML
    private MenuItem showPreferencesMenuItem;
    @FXML
    private MenuItem exitMenuItem;

    // Edit
    @FXML
    private MenuItem undoMenuItem;
    @FXML
    private MenuItem redoMenuItem;
    @FXML
    private MenuItem copyMenuItem;
    @FXML
    private MenuItem cutMenuItem;
    @FXML
    private MenuItem pasteMenuItem;
    @FXML
    private MenuItem pasteIntoMenuItem;
    @FXML
    private MenuItem duplicateMenuItem;
    @FXML
    private MenuItem deleteMenuItem;
    @FXML
    private MenuItem selectAllMenuItem;
    @FXML
    private MenuItem selectNoneMenuItem;
    @FXML
    private MenuItem selectParentMenuItem;
    @FXML
    private MenuItem selectNextMenuItem;
    @FXML
    private MenuItem selectPreviousMenuItem;
    @FXML
    private MenuItem trimMenuItem;

    // View
    @FXML
    private MenuItem gotoContentMenuItem;
    @FXML
    private MenuItem gotoPropertiesMenuItem;
    @FXML
    private MenuItem gotoLayoutMenuItem;
    @FXML
    private MenuItem gotoCodeMenuItem;
    @FXML
    private MenuItem toggleLibraryPanelMenuItem;
    @FXML
    private MenuItem toggleHierarchyPanelMenuItem;
    @FXML
    private MenuItem toggleCSSPanelMenuItem;
    @FXML
    private MenuItem toggleLeftPanelMenuItem;
    @FXML
    private MenuItem toggleRightPanelMenuItem;
    @FXML
    private MenuItem toggleOutlinesMenuItem;
    @FXML
    private MenuItem toggleSampleDataMenuItem;
    @FXML
    private MenuItem toggleAlignmentGuidesMenuItem;
    @FXML
    private MenuItem showSampleControllerMenuItem;
    @FXML
    private Menu zoomMenu;

    // Modify
    @FXML
    private MenuItem fitToParentMenuItem;
    @FXML
    private MenuItem useComputedSizesMenuItem;
    @FXML
    private MenuItem addContextMenuMenuItem;
    @FXML
    private MenuItem addTooltipMenuItem;
    @FXML
    private MenuItem moveRowAboveMenuItem;
    @FXML
    private MenuItem moveRowBelowMenuItem;
    @FXML
    private MenuItem moveColumnBeforeMenuItem;
    @FXML
    private MenuItem moveColumnAfterMenuItem;
    @FXML
    private MenuItem addRowAboveMenuItem;
    @FXML
    private MenuItem addRowBelowMenuItem;
    @FXML
    private MenuItem addColumnBeforeMenuItem;
    @FXML
    private MenuItem addColumnAfterMenuItem;
    @FXML
    private MenuItem increaseRowSpanMenuItem;
    @FXML
    private MenuItem decreaseRowSpanMenuItem;
    @FXML
    private MenuItem increaseColumnSpanMenuItem;
    @FXML
    private MenuItem decreaseColumnSpanMenuItem;
    @FXML
    private MenuItem phoneSetSizeMenuItem;
    @FXML
    private MenuItem tabletSetSizeMenuItem;
    @FXML
    private RadioMenuItem qvgaSetSizeMenuItem;
    @FXML
    private RadioMenuItem vgaSetSizeMenuItem;
    @FXML
    private RadioMenuItem touchSetSizeMenuItem;
    @FXML
    private RadioMenuItem hdSetSizeMenuItem;

    // Arrange
    @FXML
    private MenuItem bringToFrontMenuItem;
    @FXML
    private MenuItem sendToBackMenuItem;
    @FXML
    private MenuItem bringForwardMenuItem;
    @FXML
    private MenuItem sendBackwardMenuItem;
    @FXML
    private MenuItem wrapInAnchorPaneMenuItem;
    @FXML
    private MenuItem wrapInBorderPaneMenuItem;
    @FXML
    private MenuItem wrapInButtonBarMenuItem;
    @FXML
    private MenuItem wrapInDialogPaneMenuItem;
    @FXML
    private MenuItem wrapInFlowPaneMenuItem;
    @FXML
    private MenuItem wrapInGridPaneMenuItem;
    @FXML
    private MenuItem wrapInHBoxMenuItem;
    @FXML
    private MenuItem wrapInPaneMenuItem;
    @FXML
    private MenuItem wrapInScrollPaneMenuItem;
    @FXML
    private MenuItem wrapInSplitPaneMenuItem;
    @FXML
    private MenuItem wrapInStackPaneMenuItem;
    @FXML
    private MenuItem wrapInTabPaneMenuItem;
    @FXML
    private MenuItem wrapInTextFlowMenuItem;
    @FXML
    private MenuItem wrapInTilePaneMenuItem;
    @FXML
    private MenuItem wrapInTitledPaneMenuItem;
    @FXML
    private MenuItem wrapInToolBarMenuItem;
    @FXML
    private MenuItem wrapInVBoxMenuItem;
    @FXML
    private MenuItem wrapInGroupMenuItem;
    @FXML
    private MenuItem wrapInSceneMenuItem;
    @FXML
    private MenuItem wrapInStageMenuItem;
    @FXML
    private MenuItem unwrapMenuItem;

    // Preview
    @FXML
    private MenuItem showPreviewInWindowMenuItem;
    @FXML
    private MenuItem showPreviewInDialogMenuItem;
    @FXML
    private RadioMenuItem gluonMobileLightThemeMenuItem;
    @FXML
    private RadioMenuItem gluonMobileDarkThemeMenuItem;
    @FXML
    private RadioMenuItem modenaThemeMenuItem;
    @FXML
    private RadioMenuItem modenaTouchThemeMenuItem;
    @FXML
    private RadioMenuItem modenaHighContrastBlackonwhiteThemeMenuItem;
    @FXML
    private RadioMenuItem modenaHighContrastWhiteonblackThemeMenuItem;
    @FXML
    private RadioMenuItem modenaHighContrastYellowonblackThemeMenuItem;
    @FXML
    private RadioMenuItem caspianThemeMenuItem;
    @FXML
    private CheckMenuItem caspianHighContrastThemeMenuItem;
    @FXML
    private RadioMenuItem caspianEmbeddedThemeMenuItem;
    @FXML
    private RadioMenuItem caspianEmbeddedQVGAThemeMenuItem;
    @FXML
    private MenuItem addSceneStyleSheetMenuItem;
    @FXML
    private Menu removeSceneStyleSheetMenu;
    @FXML
    private Menu openSceneStyleSheetMenu;
    @FXML
    private MenuItem setResourceMenuItem;
    @FXML
    private MenuItem removeResourceMenuItem;
    @FXML
    private MenuItem revealResourceMenuItem;
    @FXML
    private RadioMenuItem phonePreviewSizeMenuItem;
    @FXML
    private RadioMenuItem tabletPreviewSizeMenuItem;
    @FXML
    private RadioMenuItem qvgaPreviewSizeMenuItem;
    @FXML
    private RadioMenuItem vgaPreviewSizeMenuItem;
    @FXML
    private RadioMenuItem touchPreviewSizeMenuItem;
    @FXML
    private RadioMenuItem hdPreviewSizeMenuItem;
    @FXML
    private RadioMenuItem preferredPreviewSizeMenuItem;

    @FXML
    private RadioMenuItem blueSwatch;
    @FXML
    private RadioMenuItem cyanSwatch;
    @FXML
    private RadioMenuItem deepOrangeSwatch;
    @FXML
    private RadioMenuItem deepPurpleSwatch;
    @FXML
    private RadioMenuItem greenSwatch;
    @FXML
    private RadioMenuItem indigoSwatch;
    @FXML
    private RadioMenuItem lightBlueSwatch;
    @FXML
    private RadioMenuItem pinkSwatch;
    @FXML
    private RadioMenuItem purpleSwatch;
    @FXML
    private RadioMenuItem redSwatch;
    @FXML
    private RadioMenuItem tealSwatch;
    @FXML
    private RadioMenuItem lightGreenSwatch;
    @FXML
    private RadioMenuItem limeSwatch;
    @FXML
    private RadioMenuItem yellowSwatch;
    @FXML
    private RadioMenuItem amberSwatch;
    @FXML
    private RadioMenuItem orangeSwatch;
    @FXML
    private RadioMenuItem brownSwatch;
    @FXML
    private RadioMenuItem greySwatch;
    @FXML
    private RadioMenuItem blueGreySwatch;

    // Window
    // Help
    @FXML
    private MenuItem helpMenuItem;
    @FXML
    private MenuItem aboutMenuItem;
    @FXML
    private MenuItem checkUpdatesMenuItem;

    private static final KeyCombination.Modifier modifier;
    private final Map<KeyCombination, MenuItem> keyToMenu = new HashMap<>();

    static {
        if (EditorPlatform.IS_MAC) {
            modifier = KeyCombination.META_DOWN;
        } else {
            // Should cover Windows, Solaris, Linux
            modifier = KeyCombination.CONTROL_DOWN;
        }
    }

    public MenuBarController(DocumentWindowController documentWindowController) {
        this.documentWindowController = documentWindowController;
    }

    public MenuBar getMenuBar() {

        if (menuBar == null) {
            final URL fxmlURL = MenuBarController.class.getResource("MenuBar.fxml"); //NOI18N
            final FXMLLoader loader = new FXMLLoader();

            loader.setController(this);
            loader.setLocation(fxmlURL);
            loader.setResources(I18N.getBundle());
            try {
                loader.load();
                controllerDidLoadFxml();
            } catch (RuntimeException | IOException x) {
                System.out.println("loader.getController()=" + loader.getController()); //NOI18N
                System.out.println("loader.getLocation()=" + loader.getLocation()); //NOI18N
                throw new RuntimeException("Failed to load " + fxmlURL.getFile(), x); //NOI18N
            }
        }

        return menuBar;
    }

    public void setDebugMenuVisible(boolean visible) {
        if (isDebugMenuVisible() != visible) {
            if (visible) {
                if (debugMenuController == null) {
                    debugMenuController = new DebugMenuController(documentWindowController);
                }
                menuBar.getMenus().add(debugMenuController.getMenu());
            } else {
                menuBar.getMenus().remove(debugMenuController.getMenu());
            }
        }
    }

    public boolean isDebugMenuVisible() {
        final boolean result;
        if (debugMenuController == null) {
            result = false;
        } else {
            result = menuBar.getMenus().contains(debugMenuController.getMenu());
        }
        return result;
    }
    
    
    public static synchronized MenuBarController getSystemMenuBarController() {
        if (systemMenuBarController == null) {
            systemMenuBarController = new MenuBarController(null);
        }
        return systemMenuBarController;
    }
    
    /*
     * Private
     */
    private void controllerDidLoadFxml() {

        assert menuBar != null;
        assert menuBar.getParent() instanceof StackPane;
        assert insertMenu != null;
        assert addEffectMenu != null;
        assert fileMenu != null;
        assert windowMenu != null;

        assert newMenuItem != null;
        assert newTemplateMenuItem != null;
        assert openMenuItem != null;
        assert openRecentMenu != null;
        assert saveMenuItem != null;
        assert saveAsMenuItem != null;
        assert revertMenuItem != null;
        assert closeMenuItem != null;
        assert revealMenuItem != null;
        assert importFxmlMenuItem != null;
        assert importMediaMenuItem != null;
        assert includeFileMenuItem != null;
        assert editIncludedFileMenuItem != null;
        assert revealIncludedFileMenuItem != null;
        assert showPreferencesMenuItem != null;
        assert exitMenuItem != null;

        assert undoMenuItem != null;
        assert redoMenuItem != null;
        assert copyMenuItem != null;
        assert cutMenuItem != null;
        assert pasteMenuItem != null;
        assert pasteIntoMenuItem != null;
        assert duplicateMenuItem != null;
        assert deleteMenuItem != null;
        assert selectAllMenuItem != null;
        assert selectNoneMenuItem != null;
        assert selectParentMenuItem != null;
        assert selectNextMenuItem != null;
        assert selectPreviousMenuItem != null;
        assert trimMenuItem != null;

        assert gotoContentMenuItem != null;
        assert gotoPropertiesMenuItem != null;
        assert gotoLayoutMenuItem != null;
        assert gotoCodeMenuItem != null;
        assert toggleLibraryPanelMenuItem != null;
        assert toggleHierarchyPanelMenuItem != null;
        assert toggleCSSPanelMenuItem != null;
        assert toggleLeftPanelMenuItem != null;
        assert toggleRightPanelMenuItem != null;
        assert toggleOutlinesMenuItem != null;
        assert toggleSampleDataMenuItem != null;
        assert toggleAlignmentGuidesMenuItem != null;
        assert showSampleControllerMenuItem != null;
        assert zoomMenu != null;
        assert zoomMenu.getItems().isEmpty();

        assert fitToParentMenuItem != null;
        assert useComputedSizesMenuItem != null;
        assert addContextMenuMenuItem != null;
        assert addTooltipMenuItem != null;
        assert moveRowAboveMenuItem != null;
        assert moveRowBelowMenuItem != null;
        assert moveColumnBeforeMenuItem != null;
        assert moveColumnAfterMenuItem != null;
        assert addRowAboveMenuItem != null;
        assert addRowBelowMenuItem != null;
        assert addColumnBeforeMenuItem != null;
        assert addColumnAfterMenuItem != null;
        assert increaseRowSpanMenuItem != null;
        assert decreaseRowSpanMenuItem != null;
        assert increaseColumnSpanMenuItem != null;
        assert decreaseColumnSpanMenuItem != null;
        assert phoneSetSizeMenuItem != null;
        assert tabletSetSizeMenuItem != null;
        assert qvgaSetSizeMenuItem != null;
        assert vgaSetSizeMenuItem != null;
        assert touchSetSizeMenuItem != null;
        assert hdSetSizeMenuItem != null;

        assert bringToFrontMenuItem != null;
        assert sendToBackMenuItem != null;
        assert bringForwardMenuItem != null;
        assert sendBackwardMenuItem != null;
        assert wrapInAnchorPaneMenuItem != null;
        assert wrapInBorderPaneMenuItem != null;
        assert wrapInButtonBarMenuItem != null;
        assert wrapInDialogPaneMenuItem != null;
        assert wrapInFlowPaneMenuItem != null;
        assert wrapInGridPaneMenuItem != null;
        assert wrapInHBoxMenuItem != null;
        assert wrapInPaneMenuItem != null;
        assert wrapInScrollPaneMenuItem != null;
        assert wrapInSplitPaneMenuItem != null;
        assert wrapInStackPaneMenuItem != null;
        assert wrapInTabPaneMenuItem != null;
        assert wrapInTextFlowMenuItem != null;
        assert wrapInTilePaneMenuItem != null;
        assert wrapInTitledPaneMenuItem != null;
        assert wrapInToolBarMenuItem != null;
        assert wrapInVBoxMenuItem != null;
        assert wrapInGroupMenuItem != null;
        assert wrapInSceneMenuItem != null;
        assert wrapInStageMenuItem != null;
        assert unwrapMenuItem != null;

        assert showPreviewInWindowMenuItem != null;
        assert showPreviewInDialogMenuItem != null;
        assert gluonMobileLightThemeMenuItem != null;
        assert gluonMobileDarkThemeMenuItem != null;
        assert modenaThemeMenuItem != null;
        assert modenaTouchThemeMenuItem != null;
        assert modenaHighContrastBlackonwhiteThemeMenuItem != null;
        assert modenaHighContrastWhiteonblackThemeMenuItem != null;
        assert modenaHighContrastYellowonblackThemeMenuItem != null;
        assert caspianThemeMenuItem != null;
        assert caspianHighContrastThemeMenuItem != null;
        assert caspianEmbeddedThemeMenuItem != null;
        assert caspianEmbeddedQVGAThemeMenuItem != null;
        assert addSceneStyleSheetMenuItem != null;
        assert removeSceneStyleSheetMenu != null;
        assert openSceneStyleSheetMenu != null;
        assert setResourceMenuItem != null;
        assert removeResourceMenuItem != null;
        assert revealResourceMenuItem != null;
        assert phonePreviewSizeMenuItem != null;
        assert tabletPreviewSizeMenuItem != null;
        assert qvgaPreviewSizeMenuItem != null;
        assert vgaPreviewSizeMenuItem != null;
        assert touchPreviewSizeMenuItem != null;
        assert hdPreviewSizeMenuItem != null;
        assert preferredPreviewSizeMenuItem != null;

        assert helpMenuItem != null;
        assert aboutMenuItem != null;
        assert checkUpdatesMenuItem != null;

        /* 
         * To make MenuBar.fxml editable with SB 1.1, the menu bar is enclosed
         * in a StackPane. This stack pane is useless now.
         * So we unwrap the menu bar and make it the panel root.
         */
        final StackPane rootStackPane = (StackPane) menuBar.getParent();
        rootStackPane.getChildren().remove(menuBar);

        /*
         * On Mac, move the menu bar on the desktop and remove the Quit item
         * from the File menu
         */
        if (EditorPlatform.IS_MAC) {
            menuBar.setUseSystemMenuBar(true);
            // SB-269
            menuBar.useSystemMenuBarProperty().addListener((obs, ov, nv) -> {
                if (! nv) {
                    // Restore System MenuBar
                    menuBar.setUseSystemMenuBar(true);
                }
            });
            exitMenuItem.getParentMenu().getItems().remove(exitMenuItem);
        }

        /*
         * Setup title of the Reveal menu item according the underlying o/s.
         */
        final String revealMenuKey;
        if (EditorPlatform.IS_MAC) {
            revealMenuKey = "menu.title.reveal.mac";
        } else if (EditorPlatform.IS_WINDOWS) {
            revealMenuKey = "menu.title.reveal.win.mnemonic";
        } else {
            assert EditorPlatform.IS_LINUX;
            revealMenuKey = "menu.title.reveal.linux";
        }
        revealMenuItem.setText(I18N.getString(revealMenuKey));

        /*
         * File menu
         */
        newMenuItem.setUserData(new ApplicationControlActionController(ApplicationControlAction.NEW_FILE));
        newMenuItem.setAccelerator(new KeyCodeCombination(KeyCode.N, modifier));
        newTemplateMenuItem.setUserData(new ApplicationControlActionController(ApplicationControlAction.NEW_TEMPLATE));
        openMenuItem.setUserData(new ApplicationControlActionController(ApplicationControlAction.OPEN_FILE));
        openMenuItem.setAccelerator(new KeyCodeCombination(KeyCode.O, modifier));
        openRecentMenu.setOnShowing(t -> updateOpenRecentMenuItems());
        saveMenuItem.setUserData(new DocumentControlActionController(DocumentControlAction.SAVE_FILE));
        saveMenuItem.setAccelerator(new KeyCodeCombination(KeyCode.S, modifier));
        saveAsMenuItem.setUserData(new DocumentControlActionController(DocumentControlAction.SAVE_AS_FILE));
        saveAsMenuItem.setAccelerator(new KeyCodeCombination(KeyCode.S, KeyCombination.SHIFT_DOWN, modifier));
        revertMenuItem.setUserData(new DocumentControlActionController(DocumentControlAction.REVERT_FILE));
        revealMenuItem.setUserData(new DocumentControlActionController(DocumentControlAction.REVEAL_FILE));
        importFxmlMenuItem.setUserData(new DocumentEditActionController(DocumentEditAction.IMPORT_FXML));
        importMediaMenuItem.setUserData(new DocumentEditActionController(DocumentEditAction.IMPORT_MEDIA));
        includeFileMenuItem.setUserData(new DocumentEditActionController(DocumentEditAction.INCLUDE_FXML));
        editIncludedFileMenuItem.setUserData(new ControlActionController(ControlAction.EDIT_INCLUDED_FILE) {

            @Override
            public String getTitle() {
                String title = I18N.getString("menu.title.edit.included.default");
                if (documentWindowController != null) {
                    final File file = documentWindowController.getEditorController().getIncludedFile();
                    if (file != null) {
                        title = I18N.getString("menu.title.edit.included", file.getName());
                    }
                }
                return title;
            }
        });
        revealIncludedFileMenuItem.setUserData(new ControlActionController(ControlAction.REVEAL_INCLUDED_FILE) {

            @Override
            public String getTitle() {
                String title = I18N.getString("menu.title.reveal.included.default");
                if (documentWindowController != null) {
                    final File file = documentWindowController.getEditorController().getIncludedFile();
                    if (file != null) {
                        if (EditorPlatform.IS_MAC) {
                            title = I18N.getString("menu.title.reveal.included.finder", file.getName());
                        } else {
                            title = I18N.getString("menu.title.reveal.included.explorer", file.getName());
                        }
                    }
                }
                return title;
            }
        });
        closeMenuItem.setUserData(new ApplicationControlActionController(ApplicationControlAction.CLOSE_FRONT_WINDOW));
        closeMenuItem.setAccelerator(new KeyCodeCombination(KeyCode.W, modifier));
        showPreferencesMenuItem.setUserData(new ApplicationControlActionController(ApplicationControlAction.SHOW_PREFERENCES));
        showPreferencesMenuItem.setAccelerator(new KeyCodeCombination(KeyCode.COMMA, modifier));
        exitMenuItem.setUserData(new ApplicationControlActionController(ApplicationControlAction.EXIT));

        /*
         * Edit menu
         */
        undoMenuItem.setUserData(new UndoActionController());
        undoMenuItem.setAccelerator(new KeyCodeCombination(KeyCode.Z, modifier));
        redoMenuItem.setUserData(new RedoActionController());
        if (EditorPlatform.IS_MAC) {
            // Mac platforms.
            redoMenuItem.setAccelerator(new KeyCodeCombination(KeyCode.Z, KeyCombination.SHIFT_DOWN, modifier));
        } else {
            // Windows and Linux platforms.
            // http://windows.microsoft.com/en-US/windows7/Keyboard-shortcuts
            redoMenuItem.setAccelerator(new KeyCodeCombination(KeyCode.Y, modifier));
        }
        copyMenuItem.setUserData(new DocumentControlActionController(DocumentControlAction.COPY));
        copyMenuItem.setAccelerator(new KeyCodeCombination(KeyCode.C, modifier));
        cutMenuItem.setUserData(new DocumentEditActionController(DocumentEditAction.CUT));
        cutMenuItem.setAccelerator(new KeyCodeCombination(KeyCode.X, modifier));
        pasteMenuItem.setUserData(new DocumentEditActionController(DocumentEditAction.PASTE));
        pasteMenuItem.setAccelerator(new KeyCodeCombination(KeyCode.V, modifier));
        pasteIntoMenuItem.setUserData(new EditActionController(EditAction.PASTE_INTO));
        pasteIntoMenuItem.setAccelerator(new KeyCodeCombination(KeyCode.V, KeyCombination.SHIFT_DOWN, modifier));
        duplicateMenuItem.setUserData(new EditActionController(EditAction.DUPLICATE));
        duplicateMenuItem.setAccelerator(new KeyCodeCombination(KeyCode.D, modifier));
        deleteMenuItem.setUserData(new DocumentEditActionController(DocumentEditAction.DELETE));
        deleteMenuItem.setAccelerator(new KeyCodeCombination(KeyCode.DELETE));
        selectAllMenuItem.setUserData(new DocumentControlActionController(DocumentControlAction.SELECT_ALL));
        selectAllMenuItem.setAccelerator(new KeyCodeCombination(KeyCode.A, modifier));
        selectNoneMenuItem.setUserData(new DocumentControlActionController(DocumentControlAction.SELECT_NONE));
        selectNoneMenuItem.setAccelerator(new KeyCodeCombination(KeyCode.A, KeyCombination.SHIFT_DOWN, modifier));
        selectParentMenuItem.setUserData(new ControlActionController(ControlAction.SELECT_PARENT));
        selectParentMenuItem.setAccelerator(new KeyCodeCombination(KeyCode.UP, modifier));
        selectNextMenuItem.setUserData(new ControlActionController(ControlAction.SELECT_NEXT));
        selectNextMenuItem.setAccelerator(new KeyCodeCombination(KeyCode.RIGHT, modifier));
        selectPreviousMenuItem.setUserData(new ControlActionController(ControlAction.SELECT_PREVIOUS));
        selectPreviousMenuItem.setAccelerator(new KeyCodeCombination(KeyCode.LEFT, modifier));
        trimMenuItem.setUserData(new EditActionController(EditAction.TRIM));

        /*
         * View menu
         */
        gotoContentMenuItem.setUserData(new DocumentControlActionController(DocumentControlAction.GOTO_CONTENT));
        gotoContentMenuItem.setAccelerator(new KeyCodeCombination(KeyCode.DIGIT0, modifier));
        gotoPropertiesMenuItem.setUserData(new DocumentControlActionController(DocumentControlAction.GOTO_PROPERTIES));
        gotoPropertiesMenuItem.setAccelerator(new KeyCodeCombination(KeyCode.DIGIT1, modifier));
        gotoLayoutMenuItem.setUserData(new DocumentControlActionController(DocumentControlAction.GOTO_LAYOUT));
        gotoLayoutMenuItem.setAccelerator(new KeyCodeCombination(KeyCode.DIGIT2, modifier));
        gotoCodeMenuItem.setUserData(new DocumentControlActionController(DocumentControlAction.GOTO_CODE));
        gotoCodeMenuItem.setAccelerator(new KeyCodeCombination(KeyCode.DIGIT3, modifier));

        toggleLibraryPanelMenuItem.setUserData(
                new DocumentControlActionController(DocumentControlAction.TOGGLE_LIBRARY_PANEL) {
                    @Override
                    public String getTitle() {
                        final String titleKey;
                        if (documentWindowController == null) {
                            titleKey = "menu.title.hide.library.panel";
                        } else if (documentWindowController.isLibraryPanelVisible()) {
                            titleKey = "menu.title.hide.library.panel";
                        } else {
                            titleKey = "menu.title.show.library.panel";
                        }
                        return I18N.getString(titleKey);
                    }
                });
        toggleLibraryPanelMenuItem.setAccelerator(new KeyCodeCombination(KeyCode.DIGIT4, modifier));
        toggleHierarchyPanelMenuItem.setUserData(new DocumentControlActionController(DocumentControlAction.TOGGLE_DOCUMENT_PANEL) {
            @Override
            public String getTitle() {
                final String titleKey;
                if (documentWindowController == null) {
                    titleKey = "menu.title.hide.document.panel";
                } else if (documentWindowController.isHierarchyPanelVisible()) {
                    titleKey = "menu.title.hide.document.panel";
                } else {
                    titleKey = "menu.title.show.document.panel";
                }
                return I18N.getString(titleKey);
            }
        });
        toggleHierarchyPanelMenuItem.setAccelerator(new KeyCodeCombination(KeyCode.DIGIT5, modifier));
        toggleCSSPanelMenuItem.setUserData(new DocumentControlActionController(DocumentControlAction.TOGGLE_CSS_PANEL) {
            @Override
            public String getTitle() {
                final String titleKey;
                if (documentWindowController == null) {
                    titleKey = "menu.title.hide.bottom.panel";
                } else if (documentWindowController.isBottomPanelVisible()) {
                    titleKey = "menu.title.hide.bottom.panel";
                } else {
                    titleKey = "menu.title.show.bottom.panel";
                }
                return I18N.getString(titleKey);
            }
        });
        toggleCSSPanelMenuItem.setAccelerator(new KeyCodeCombination(KeyCode.DIGIT6, modifier));
        toggleLeftPanelMenuItem.setUserData(new DocumentControlActionController(DocumentControlAction.TOGGLE_LEFT_PANEL) {
            @Override
            public String getTitle() {
                final String titleKey;
                if (documentWindowController == null) {
                    titleKey = "menu.title.hide.left.panel";
                } else if (documentWindowController.isLeftPanelVisible()) {
                    titleKey = "menu.title.hide.left.panel";
                } else {
                    titleKey = "menu.title.show.left.panel";
                }
                return I18N.getString(titleKey);
            }
        });
        toggleLeftPanelMenuItem.setAccelerator(new KeyCodeCombination(KeyCode.DIGIT7, modifier));
        toggleRightPanelMenuItem.setUserData(new DocumentControlActionController(DocumentControlAction.TOGGLE_RIGHT_PANEL) {
            @Override
            public String getTitle() {
                final String titleKey;
                if (documentWindowController == null) {
                    titleKey = "menu.title.hide.right.panel";
                } else if (documentWindowController.isRightPanelVisible()) {
                    titleKey = "menu.title.hide.right.panel";
                } else {
                    titleKey = "menu.title.show.right.panel";
                }
                return I18N.getString(titleKey);
            }
        });
        toggleRightPanelMenuItem.setAccelerator(new KeyCodeCombination(KeyCode.DIGIT8, modifier));
        toggleOutlinesMenuItem.setUserData(new DocumentControlActionController(DocumentControlAction.TOGGLE_OUTLINES_VISIBILITY) {
            @Override
            public String getTitle() {
                final String titleKey;
                if (documentWindowController == null) {
                    titleKey = "menu.title.hide.outlines";
                } else if (documentWindowController.getContentPanelController().isOutlinesVisible()) {
                    titleKey = "menu.title.hide.outlines";
                } else {
                    titleKey = "menu.title.show.outlines";
                }
                return I18N.getString(titleKey);
            }
        });
        toggleOutlinesMenuItem.setAccelerator(new KeyCodeCombination(KeyCode.E, modifier));
        toggleSampleDataMenuItem.setUserData(new ControlActionController(ControlAction.TOGGLE_SAMPLE_DATA) {
            @Override
            public String getTitle() {
                final String titleKey;
                if (documentWindowController == null) {
                    titleKey = "menu.title.hide.sample.data";
                } else if (documentWindowController.getEditorController().isSampleDataEnabled()) {
                    titleKey = "menu.title.hide.sample.data";
                } else {
                    titleKey = "menu.title.show.sample.data";
                }
                return I18N.getString(titleKey);
            }
        });
        toggleAlignmentGuidesMenuItem.setUserData(new DocumentControlActionController(DocumentControlAction.TOGGLE_GUIDES_VISIBILITY) {
            @Override
            public String getTitle() {
                final String titleKey;
                if (documentWindowController == null) {
                    titleKey = "menu.title.disable.guides";
                } else {
                    final ContentPanelController contentPanelController
                            = documentWindowController.getContentPanelController();
                    if (contentPanelController.isGuidesVisible()) {
                        titleKey = "menu.title.disable.guides";
                    } else {
                        titleKey = "menu.title.enable.guides";
                    }
                }
                return I18N.getString(titleKey);
            }
        });
        showSampleControllerMenuItem.setUserData(new DocumentControlActionController(DocumentControlAction.SHOW_SAMPLE_CONTROLLER));
        updateZoomMenu();

        /*
         * Insert menu: it uses specific handlers, which means we initialize it
         * later to avoid interfering with other menus.
         */

        /*
         * Modify menu
         */
        fitToParentMenuItem.setUserData(new EditActionController(EditAction.FIT_TO_PARENT));
        fitToParentMenuItem.setAccelerator(new KeyCodeCombination(KeyCode.K, modifier));
        useComputedSizesMenuItem.setUserData(new EditActionController(EditAction.USE_COMPUTED_SIZES));
        useComputedSizesMenuItem.setAccelerator(new KeyCodeCombination(KeyCode.K, KeyCombination.SHIFT_DOWN, modifier));
        addContextMenuMenuItem.setUserData(new EditActionController(EditAction.ADD_CONTEXT_MENU));
        addTooltipMenuItem.setUserData(new EditActionController(EditAction.ADD_TOOLTIP));
        moveRowAboveMenuItem.setUserData(new EditActionController(EditAction.MOVE_ROW_ABOVE));
        moveRowBelowMenuItem.setUserData(new EditActionController(EditAction.MOVE_ROW_BELOW));
        moveColumnBeforeMenuItem.setUserData(new EditActionController(EditAction.MOVE_COLUMN_BEFORE));
        moveColumnAfterMenuItem.setUserData(new EditActionController(EditAction.MOVE_COLUMN_AFTER));
        addRowAboveMenuItem.setUserData(new EditActionController(EditAction.ADD_ROW_ABOVE));
        addRowBelowMenuItem.setUserData(new EditActionController(EditAction.ADD_ROW_BELOW));
        addColumnBeforeMenuItem.setUserData(new EditActionController(EditAction.ADD_COLUMN_BEFORE));
        addColumnAfterMenuItem.setUserData(new EditActionController(EditAction.ADD_COLUMN_AFTER));
        increaseRowSpanMenuItem.setUserData(new EditActionController(EditAction.INCREASE_ROW_SPAN));
        decreaseRowSpanMenuItem.setUserData(new EditActionController(EditAction.DECREASE_ROW_SPAN));
        increaseColumnSpanMenuItem.setUserData(new EditActionController(EditAction.INCREASE_COLUMN_SPAN));
        decreaseColumnSpanMenuItem.setUserData(new EditActionController(EditAction.DECREASE_COLUMN_SPAN));

        phoneSetSizeMenuItem.setUserData(new EditActionController(EditAction.SET_SIZE_335x600) {
            @Override
            public void perform() {
                super.perform();
                updatePreviewWindowSize(Size.SIZE_335x600);
            }
        });
        tabletSetSizeMenuItem.setUserData(new EditActionController(EditAction.SET_SIZE_900x600) {
            @Override
            public void perform() {
                super.perform();
                updatePreviewWindowSize(Size.SIZE_900x600);
            }
        });
        qvgaSetSizeMenuItem.setUserData(new EditActionController(EditAction.SET_SIZE_320x240) {
            @Override
            public void perform() {
                super.perform();
                updatePreviewWindowSize(Size.SIZE_320x240);
            }
        });
        vgaSetSizeMenuItem.setUserData(new EditActionController(EditAction.SET_SIZE_640x480) {
            @Override
            public void perform() {
                super.perform();
                updatePreviewWindowSize(Size.SIZE_640x480);
            }
        });
        touchSetSizeMenuItem.setUserData(new EditActionController(EditAction.SET_SIZE_1280x800) {
            @Override
            public void perform() {
                super.perform();
                updatePreviewWindowSize(Size.SIZE_1280x800);
            }
        });
        hdSetSizeMenuItem.setUserData(new EditActionController(EditAction.SET_SIZE_1920x1080) {
            @Override
            public void perform() {
                super.perform();
                updatePreviewWindowSize(Size.SIZE_1920x1080);
            }
        });

        // Add Effect submenu
        updateAddEffectMenu();

        /*
         * Arrange menu
         */
        bringToFrontMenuItem.setUserData(new EditActionController(EditAction.BRING_TO_FRONT));
        bringToFrontMenuItem.setAccelerator(new KeyCharacterCombination("]", //NOI18N
                KeyCombination.SHIFT_DOWN, modifier));
        sendToBackMenuItem.setUserData(new EditActionController(EditAction.SEND_TO_BACK));
        sendToBackMenuItem.setAccelerator(new KeyCharacterCombination("[", //NOI18N
                KeyCombination.SHIFT_DOWN, modifier));
        bringForwardMenuItem.setUserData(new EditActionController(EditAction.BRING_FORWARD));
        bringForwardMenuItem.setAccelerator(
                new KeyCharacterCombination("]", modifier)); //NOI18N
        sendBackwardMenuItem.setUserData(new EditActionController(EditAction.SEND_BACKWARD));
        sendBackwardMenuItem.setAccelerator(
                new KeyCharacterCombination("[", modifier)); //NOI18N
        wrapInAnchorPaneMenuItem.setUserData(new EditActionController(EditAction.WRAP_IN_ANCHOR_PANE));
        wrapInBorderPaneMenuItem.setUserData(new EditActionController(EditAction.WRAP_IN_BORDER_PANE));
        wrapInButtonBarMenuItem.setUserData(new EditActionController(EditAction.WRAP_IN_BUTTON_BAR));
        wrapInDialogPaneMenuItem.setUserData(new EditActionController(EditAction.WRAP_IN_DIALOG_PANE));
        wrapInFlowPaneMenuItem.setUserData(new EditActionController(EditAction.WRAP_IN_FLOW_PANE));
        wrapInGroupMenuItem.setUserData(new EditActionController(EditAction.WRAP_IN_GROUP));
        wrapInGridPaneMenuItem.setUserData(new EditActionController(EditAction.WRAP_IN_GRID_PANE));
        wrapInHBoxMenuItem.setUserData(new EditActionController(EditAction.WRAP_IN_HBOX));
        wrapInPaneMenuItem.setUserData(new EditActionController(EditAction.WRAP_IN_PANE));
        wrapInScrollPaneMenuItem.setUserData(new EditActionController(EditAction.WRAP_IN_SCROLL_PANE));
        wrapInSplitPaneMenuItem.setUserData(new EditActionController(EditAction.WRAP_IN_SPLIT_PANE));
        wrapInStackPaneMenuItem.setUserData(new EditActionController(EditAction.WRAP_IN_STACK_PANE));
        wrapInTabPaneMenuItem.setUserData(new EditActionController(EditAction.WRAP_IN_TAB_PANE));
        wrapInTextFlowMenuItem.setUserData(new EditActionController(EditAction.WRAP_IN_TEXT_FLOW));
        wrapInTilePaneMenuItem.setUserData(new EditActionController(EditAction.WRAP_IN_TILE_PANE));
        wrapInTitledPaneMenuItem.setUserData(new EditActionController(EditAction.WRAP_IN_TITLED_PANE));
        wrapInToolBarMenuItem.setUserData(new EditActionController(EditAction.WRAP_IN_TOOL_BAR));
        wrapInVBoxMenuItem.setUserData(new EditActionController(EditAction.WRAP_IN_VBOX));
        wrapInGroupMenuItem.setUserData(new EditActionController(EditAction.WRAP_IN_GROUP));
        wrapInSceneMenuItem.setUserData(new EditActionController(EditAction.WRAP_IN_SCENE));
        wrapInStageMenuItem.setUserData(new EditActionController(EditAction.WRAP_IN_STAGE));
        unwrapMenuItem.setUserData(new EditActionController(EditAction.UNWRAP));
        unwrapMenuItem.setAccelerator(
                new KeyCodeCombination(KeyCode.U, modifier));

        /*
         * Preview menu
         */
        showPreviewInWindowMenuItem.setUserData(new DocumentControlActionController(DocumentControlAction.SHOW_PREVIEW_WINDOW));
        showPreviewInWindowMenuItem.setAccelerator(new KeyCodeCombination(KeyCode.P, modifier));
        showPreviewInDialogMenuItem.setUserData(new DocumentControlActionController(DocumentControlAction.SHOW_PREVIEW_DIALOG));
        caspianHighContrastThemeMenuItem.setUserData(new SetThemeActionController(EditorPlatform.Theme.CASPIAN_HIGH_CONTRAST));
        caspianThemeMenuItem.setUserData(new SetThemeActionController(EditorPlatform.Theme.CASPIAN));
        caspianEmbeddedThemeMenuItem.setUserData(new SetThemeActionController(EditorPlatform.Theme.CASPIAN_EMBEDDED));
        caspianEmbeddedQVGAThemeMenuItem.setUserData(new SetThemeActionController(EditorPlatform.Theme.CASPIAN_EMBEDDED_QVGA));

        gluonMobileLightThemeMenuItem.setUserData(new SetThemeActionController(EditorPlatform.Theme.GLUON_MOBILE_LIGHT));
        gluonMobileDarkThemeMenuItem.setUserData(new SetThemeActionController(EditorPlatform.Theme.GLUON_MOBILE_DARK));
        modenaThemeMenuItem.setUserData(new SetThemeActionController(EditorPlatform.Theme.MODENA));
        modenaTouchThemeMenuItem.setUserData(new SetThemeActionController(EditorPlatform.Theme.MODENA_TOUCH));
        modenaHighContrastBlackonwhiteThemeMenuItem.setUserData(new SetThemeActionController(EditorPlatform.Theme.MODENA_HIGH_CONTRAST_BLACK_ON_WHITE));
        modenaHighContrastWhiteonblackThemeMenuItem.setUserData(new SetThemeActionController(EditorPlatform.Theme.MODENA_HIGH_CONTRAST_WHITE_ON_BLACK));
        modenaHighContrastYellowonblackThemeMenuItem.setUserData(new SetThemeActionController(EditorPlatform.Theme.MODENA_HIGH_CONTRAST_YELLOW_ON_BLACK));

        blueSwatch.setUserData(new GluonActionController(EditorPlatform.GluonSwatch.BLUE));
        addSwatchGraphic(blueSwatch);
        cyanSwatch.setUserData(new GluonActionController(EditorPlatform.GluonSwatch.CYAN));
        addSwatchGraphic(cyanSwatch);
        deepOrangeSwatch.setUserData(new GluonActionController(EditorPlatform.GluonSwatch.DEEP_ORANGE));
        addSwatchGraphic(deepOrangeSwatch);
        deepPurpleSwatch.setUserData(new GluonActionController(EditorPlatform.GluonSwatch.DEEP_PURPLE));
        addSwatchGraphic(deepPurpleSwatch);
        greenSwatch.setUserData(new GluonActionController(EditorPlatform.GluonSwatch.GREEN));
        addSwatchGraphic(greenSwatch);
        indigoSwatch.setUserData(new GluonActionController(EditorPlatform.GluonSwatch.INDIGO));
        addSwatchGraphic(indigoSwatch);
        lightBlueSwatch.setUserData(new GluonActionController(EditorPlatform.GluonSwatch.LIGHT_BLUE));
        addSwatchGraphic(lightBlueSwatch);
        pinkSwatch.setUserData(new GluonActionController(EditorPlatform.GluonSwatch.PINK));
        addSwatchGraphic(pinkSwatch);
        purpleSwatch.setUserData(new GluonActionController(EditorPlatform.GluonSwatch.PURPLE));
        addSwatchGraphic(purpleSwatch);
        redSwatch.setUserData(new GluonActionController(EditorPlatform.GluonSwatch.RED));
        addSwatchGraphic(redSwatch);
        tealSwatch.setUserData(new GluonActionController(EditorPlatform.GluonSwatch.TEAL));
        addSwatchGraphic(tealSwatch);
        lightGreenSwatch.setUserData(new GluonActionController(EditorPlatform.GluonSwatch.LIGHT_GREEN));
        addSwatchGraphic(lightGreenSwatch);
        limeSwatch.setUserData(new GluonActionController(EditorPlatform.GluonSwatch.LIME));
        addSwatchGraphic(limeSwatch);
        yellowSwatch.setUserData(new GluonActionController(EditorPlatform.GluonSwatch.YELLOW));
        addSwatchGraphic(yellowSwatch);
        amberSwatch.setUserData(new GluonActionController(EditorPlatform.GluonSwatch.AMBER));
        addSwatchGraphic(amberSwatch);
        orangeSwatch.setUserData(new GluonActionController(EditorPlatform.GluonSwatch.ORANGE));
        addSwatchGraphic(orangeSwatch);
        brownSwatch.setUserData(new GluonActionController(EditorPlatform.GluonSwatch.BROWN));
        addSwatchGraphic(brownSwatch);
        greySwatch.setUserData(new GluonActionController(EditorPlatform.GluonSwatch.GREY));
        addSwatchGraphic(greySwatch);
        blueGreySwatch.setUserData(new GluonActionController(EditorPlatform.GluonSwatch.BLUE_GREY));
        addSwatchGraphic(blueGreySwatch);

        addSceneStyleSheetMenuItem.setUserData(new DocumentControlActionController(DocumentControlAction.ADD_SCENE_STYLE_SHEET));
        updateOpenAndRemoveSceneStyleSheetMenus();
        if (documentWindowController != null) {
            this.documentWindowController.getEditorController().sceneStyleSheetProperty().addListener((ChangeListener<ObservableList<File>>) (ov, t, t1) -> {
                if (t1 != null) {
                    updateOpenAndRemoveSceneStyleSheetMenus();
                    setupMenuItemHandlers(removeSceneStyleSheetMenu);
                    setupMenuItemHandlers(openSceneStyleSheetMenu);
                }
            });
        }

        setResourceMenuItem.setUserData(new DocumentControlActionController(DocumentControlAction.SET_RESOURCE));
        removeResourceMenuItem.setUserData(new DocumentControlActionController(DocumentControlAction.REMOVE_RESOURCE) {
            @Override
            public String getTitle() {
                String title = I18N.getString("menu.title.remove.resource");
                if (documentWindowController != null
                        && documentWindowController.getResourceFile() != null) {
                    title = I18N.getString("menu.title.remove.resource.with.file",
                            documentWindowController.getResourceFile().getName());
                }

                return title;
            }
        });
        revealResourceMenuItem.setUserData(new DocumentControlActionController(DocumentControlAction.REVEAL_RESOURCE) {

            @Override
            public String getTitle() {
                String title = I18N.getString("menu.title.reveal.resource");
                if (documentWindowController != null
                        && documentWindowController.getResourceFile() != null) {
                    title = I18N.getString("menu.title.reveal.resource.with.file",
                            documentWindowController.getResourceFile().getName());
                }

                return title;
            }
        });
        phonePreviewSizeMenuItem.setUserData(new SetSizeActionController(Size.SIZE_335x600));
        tabletPreviewSizeMenuItem.setUserData(new SetSizeActionController(Size.SIZE_900x600));
        qvgaPreviewSizeMenuItem.setUserData(new SetSizeActionController(Size.SIZE_320x240));
        vgaPreviewSizeMenuItem.setUserData(new SetSizeActionController(Size.SIZE_640x480));
        touchPreviewSizeMenuItem.setUserData(new SetSizeActionController(Size.SIZE_1280x800));
        hdPreviewSizeMenuItem.setUserData(new SetSizeActionController(Size.SIZE_1920x1080));
        preferredPreviewSizeMenuItem.setUserData(new SetSizeActionController(Size.SIZE_PREFERRED));

        /*
         * Window menu : it is setup after the other menus
         */
        
        /*
         * Help menu
         */
        aboutMenuItem.setUserData(new ApplicationControlActionController(ApplicationControlAction.ABOUT));
        helpMenuItem.setUserData(new DocumentControlActionController(DocumentControlAction.HELP));
        helpMenuItem.setAccelerator(new KeyCodeCombination(KeyCode.F1));
        checkUpdatesMenuItem.setUserData(new ApplicationControlActionController(ApplicationControlAction.CHECK_UPDATES));

        /*
         * Put some generic handlers on each Menu and MenuItem.
         * For Insert and Window menu, we override with specific handlers.
         */
        for (Menu m : menuBar.getMenus()) {
            setupMenuItemHandlers(m);
        }
        
        /*
         * Insert menu: we set what is statically known.
         */
        constructBuiltinPartOfInsertMenu();
        constructCustomPartOfInsertMenu();
        
        // The handler for Insert menu deals only with Custom sub-menu.
        insertMenu.setOnMenuValidation(onCustomPartOfInsertMenuValidationHandler);
        
        windowMenu.setOnMenuValidation(onWindowMenuValidationHandler);
    }

    private void addSwatchGraphic(RadioMenuItem swatchMenuItem) {
        swatchMenuItem.setGraphic(((GluonActionController)swatchMenuItem.getUserData()).getSwatch().createGraphic());
    }

    /*
     * Generic menu and item handlers
     */
    private void setupMenuItemHandlers(MenuItem i) {
        if (i instanceof Menu) {
            final Menu m = (Menu) i;
            m.setOnMenuValidation(onMenuValidationEventHandler);
            for (MenuItem child : m.getItems()) {
                setupMenuItemHandlers(child);
            }
        } else {
            i.setOnAction(onActionEventHandler);
            if (i.getAccelerator() != null) {
                keyToMenu.put(i.getAccelerator(), i);
            }
        }
    }

    private final EventHandler<Event> onMenuValidationEventHandler
            = t -> {
        assert t.getSource() instanceof Menu;
        handleOnMenuValidation((Menu) t.getSource());
    };

    private void handleOnMenuValidation(Menu menu) {
        for (MenuItem i : menu.getItems()) {
            final boolean disable, selected;
            final String title;
            if (i.getUserData() instanceof MenuItemController) {
                final MenuItemController c = (MenuItemController) i.getUserData();
                boolean canPerform;
                try {
                    canPerform = c.canPerform();
                } catch(RuntimeException x) {
                    // This catch is protection against a bug in canPerform().
                    // It avoids to block all the items in the menu in case
                    // of crash in canPerform() (see DTL-6164).
                    canPerform = false;
                    final Exception xx 
                            = new Exception(c.getClass().getSimpleName() 
                            + ".canPerform() did break for menu item " + i, x); //NOI18N
                    xx.printStackTrace();
                }
                disable = !canPerform;
                title = c.getTitle();
                selected = c.isSelected();
            } else {
                if (i instanceof Menu) {
                    disable = false;
                    selected = false;
                    title = null;
                } else {
                    disable = true;
                    selected = false;
                    title = null;
                }
            }
            i.setDisable(disable);
            if (title != null) {
                i.setText(title);
            }
            if (i instanceof RadioMenuItem) {
                final RadioMenuItem ri = (RadioMenuItem) i;
                ri.setSelected(selected);
            }
        }
    }

    private final EventHandler<ActionEvent> onActionEventHandler
            = t -> {
        assert t.getSource() instanceof MenuItem;
        handleOnActionMenu((MenuItem) t.getSource());
    };

    private void handleOnActionMenu(MenuItem i) {
        assert i.getUserData() instanceof MenuItemController;
        final MenuItemController c = (MenuItemController) i.getUserData();
        c.perform();
    }
    
    /*
     * Private (zoom menu)
     */
    
    final static double[] scalingTable = {0.25, 0.50, 0.75, 1.00, 1.50, 2.0, 4.0};
    
    private void updateZoomMenu() {
        final double[] scalingTable = {0.25, 0.50, 0.75, 1.00, 1.50, 2.0, 4.0};

        final MenuItem zoomInMenuItem = new MenuItem(I18N.getString("menu.title.zoom.in"));
        zoomInMenuItem.setUserData(new ZoomInActionController());
        zoomInMenuItem.setAccelerator(new KeyCharacterCombination("+", modifier)); //NOI18N
        zoomMenu.getItems().add(zoomInMenuItem);
        
        final MenuItem zoomOutMenuItem = new MenuItem(I18N.getString("menu.title.zoom.out"));
        zoomOutMenuItem.setUserData(new ZoomOutActionController());
        zoomOutMenuItem.setAccelerator(new KeyCharacterCombination("/", modifier));  //NOI18N
        zoomMenu.getItems().add(zoomOutMenuItem);
        
        zoomMenu.getItems().add(new SeparatorMenuItem());
        
        for (int i = 0; i < scalingTable.length; i++) {
            final double scaling = scalingTable[i];
            final String title = String.format("%.0f%%", scaling * 100); //NOI18N
            final RadioMenuItem mi = new RadioMenuItem(title);
            mi.setUserData(new SetZoomActionController(scaling));
            zoomMenu.getItems().add(mi);
        }
    }

    
    private static int findZoomScaleIndex(double zoomScale) {
        int result = -1;
        
        for (int i = 0; i < scalingTable.length; i++) {
            if (MathUtils.equals(zoomScale, scalingTable[i])) {
                result = i;
                break;
            }
        }
        
        return result;
    }
    
    private void updateOpenRecentMenuItems() {

        final List<MenuItem> menuItems = new ArrayList<>();

        final PreferencesController pc = PreferencesController.getSingleton();
        final PreferencesRecordGlobal recordGlobal = pc.getRecordGlobal();
        final List<String> recentItems = recordGlobal.getRecentItems();

        final MenuItem clearMenuItem = new MenuItem(I18N.getString("menu.title.open.recent.clear"));
        clearMenuItem.setOnAction(new ClearOpenRecentHandler());

        if (recentItems.isEmpty()) {
            clearMenuItem.setDisable(true);
            menuItems.add(clearMenuItem);
        } else {
            clearMenuItem.setDisable(false);

            final Map<String, Integer> recentItemsNames = new HashMap<>();
            final List<String> recentItemsToRemove = new ArrayList<>();

            // First pass to build recentItemsNames and recentItemsToRemove
            for (String recentItem : recentItems) {
                final File recentItemFile = new File(recentItem);
                if (recentItemFile.exists()) {
                    final String name = recentItemFile.getName();
                    if (recentItemsNames.containsKey(name)) {
                        recentItemsNames.replace(name, recentItemsNames.get(name) + 1);
                    } else {
                        recentItemsNames.put(name, 1);
                    }
                } else {
                    // recent item file is still in preferences DB but has been removed from disk
                    recentItemsToRemove.add(recentItem);
                }
            }
            // Second pass to build MenuItems
            for (String recentItem : recentItems) {
                final File recentItemFile = new File(recentItem);
                if (recentItemFile.exists()) {
                    final String name = recentItemFile.getName();
                    assert recentItemsNames.keySet().contains(name);
                    final MenuItem mi;
                    if (recentItemsNames.get(name) > 1) {
                        // Several files with same name : display full path
                        mi = new MenuItem(recentItem);
                    } else {
                        // Single file with this name : display file name only
                        assert recentItemsNames.get(name) == 1;
                        mi = new MenuItem(name);
                    }
                    mi.setOnAction(t -> {
                        final File file = new File(recentItem);
                        SceneBuilderApp.getSingleton().performOpenRecent(documentWindowController, file);
                    });
                    mi.setMnemonicParsing(false);
                    menuItems.add(mi);
                }
            }
            
            // Cleanup recent items preferences if needed
            if (recentItemsToRemove.isEmpty() == false) {
                recordGlobal.removeRecentItems(recentItemsToRemove);
            }

            menuItems.add(new SeparatorMenuItem());
            menuItems.add(clearMenuItem);
        }

        openRecentMenu.getItems().setAll(menuItems);
    }

    private void updateOpenAndRemoveSceneStyleSheetMenus() {
        assert removeSceneStyleSheetMenu != null;

        if (documentWindowController != null) {
            ObservableList<File> sceneStyleSheets = documentWindowController.getEditorController().getSceneStyleSheets();

            if (sceneStyleSheets != null) {
                removeSceneStyleSheetMenu.getItems().clear();
                openSceneStyleSheetMenu.getItems().clear();

                if (sceneStyleSheets.size() == 0) {
                    MenuItem mi = new MenuItem(I18N.getString("scenestylesheet.none"));
                    mi.setDisable(true);
                    removeSceneStyleSheetMenu.getItems().add(mi);
                    MenuItem mi2 = new MenuItem(I18N.getString("scenestylesheet.none"));
                    mi2.setDisable(true);
                    openSceneStyleSheetMenu.getItems().add(mi2);
                } else {
                    for (File f : sceneStyleSheets) {
                        MenuItem mi = new MenuItem(f.getName());
                        mi.setUserData(new RemoveSceneStyleSheetActionController(f));
                        removeSceneStyleSheetMenu.getItems().add(mi);
                        MenuItem mi2 = new MenuItem(f.getName());
                        mi2.setUserData(new OpenSceneStyleSheetActionController(f));
                        openSceneStyleSheetMenu.getItems().add(mi2);
                    }
                }
            }
        }
    }

    /*
     * Private (insert menu)
     */
    private final EventHandler<Event> onCustomPartOfInsertMenuValidationHandler
            = t -> {
        assert t.getSource() == insertMenu;
        updateCustomPartOfInsertMenu();
    };
    
    private void updateCustomPartOfInsertMenu() {
        assert insertMenu != null;
        assert insertCustomMenu != null;        

        if (documentWindowController != null) {
            final EditorController editorController = documentWindowController.getEditorController();
            assert editorController.getLibrary() != null;

            Set<LibraryItem> sectionItems = new TreeSet<>(new LibraryItemNameComparator());
            
            // Collect custom items
            for (LibraryItem li : editorController.getLibrary().getItems()) {
                if (li.getSection().equals(UserLibrary.TAG_USER_DEFINED)) {
                    sectionItems.add(li);
                }
            }

            // Make custom items visible and accessible via custom menu.
            if (sectionItems.size() > 0) {
                insertCustomMenu.getItems().clear();
                
                for (LibraryItem li : sectionItems) {
                    insertCustomMenu.getItems().add(makeMenuItemForLibraryItem(li));
                }
                
                insertCustomMenu.setVisible(true);
            } else {
                insertCustomMenu.setVisible(false);
            }
        }
    }
    
    // At constructing time we dunno if we've custom items then we keep it hidden.
    private void constructCustomPartOfInsertMenu() {
        assert insertMenu != null;
        insertCustomMenu = makeMenuForLibrarySection(UserLibrary.TAG_USER_DEFINED);
        insertMenu.getItems().add(0, insertCustomMenu);
        insertCustomMenu.setVisible(false);
    }
    
    // We consider the content of built-in library is static: it cannot change
    // unless its implementation is modified.
    private void constructBuiltinPartOfInsertMenu() {
        assert insertMenu != null;
        insertMenu.getItems().clear();

        final Map<String, Set<LibraryItem>> sectionMap
                = new TreeMap<>(new BuiltinSectionComparator());

        for (LibraryItem li : BuiltinLibrary.getLibrary().getItems()) {
            Set<LibraryItem> sectionItems = sectionMap.get(li.getSection());
            if (sectionItems == null) {
                sectionItems = new TreeSet<>(new LibraryItemNameComparator());
                sectionMap.put(li.getSection(), sectionItems);
            }
            // Add all builtin Library items except the ContextMenu (see DTL-6831)
            if (!ContextMenu.class.getSimpleName().equals(li.getName())) {
                sectionItems.add(li);
            }
        }

        for (Map.Entry<String, Set<LibraryItem>> e : sectionMap.entrySet()) {
            final Menu sectionMenu = makeMenuForLibrarySection(e.getKey());
            insertMenu.getItems().add(sectionMenu);
            for (LibraryItem li : e.getValue()) {
                sectionMenu.getItems().add(makeMenuItemForLibraryItem(li));
            }
        }
    }

    private Menu makeMenuForLibrarySection(String section) {
        final Menu result = new Menu();
        result.setText(section);
        result.setOnShowing(t -> updateInsertMenuState(result));
        return result;
    }

    private MenuItem makeMenuItemForLibraryItem(final LibraryItem li) {
        final MenuItem result = new MenuItem();

        result.setText(li.getName());
        result.setUserData(li);
        result.setOnAction(t -> handleInsertMenuAction(li));
        return result;
    }

    private void updateInsertMenuState(Menu sectionMenu) {
        if (documentWindowController != null && documentWindowController.getStage().isFocused()) {
            final EditorController editorController = documentWindowController.getEditorController();
            for (MenuItem menuItem : sectionMenu.getItems()) {
                assert menuItem.getUserData() instanceof LibraryItem;
                final LibraryItem li = (LibraryItem) menuItem.getUserData();
                final boolean enabled = editorController.canPerformInsert(li);
                menuItem.setDisable(!enabled);
            }
        } else {
            // See DTL-6017 and DTL-6554.
            // This case is relevant on Mac only; on Win and Linux the top menu
            // bar is part of the document window then even if some other non-modal
            // window is opened (Preferences, Skeleton, Preview) one has to give
            // focus to the document window to become able to open the Insert menu.
            for (MenuItem menuItem : sectionMenu.getItems()) {
                assert menuItem.getUserData() instanceof LibraryItem;
                menuItem.setDisable(true);
            }
        }
    }

    private void handleInsertMenuAction(LibraryItem li) {
        if (documentWindowController != null) {
            final EditorController editorController = documentWindowController.getEditorController();
            editorController.performInsert(li);
        }
    }

    /*
     * Private (Add Effect menu)
     */
    private void updateAddEffectMenu() {
        addEffectMenu.getItems().clear();
        for (Class<? extends Effect> c : EffectPicker.getEffectClasses()) {
            addEffectMenu.getItems().add(makeMenuItemForEffect(c));
        }
    }

    private MenuItem makeMenuItemForEffect(Class<? extends Effect> effectClass) {
        final MenuItem result = new MenuItem();
        result.setText(effectClass.getSimpleName());
        result.setUserData(new AddEffectActionController(effectClass));
        return result;
    }

    /*
     * Private (window menu)
     */
    private final EventHandler<Event> onWindowMenuValidationHandler
            = t -> {
        assert t.getSource() == windowMenu;
        handleOnWindowMenuValidation();
    };

    private void handleOnWindowMenuValidation() {
        windowMenu.getItems().clear();

        final List<DocumentWindowController> documentWindowControllers
                = SceneBuilderApp.getSingleton().getDocumentWindowControllers();
        if (documentWindowControllers.isEmpty()) {
            // Adds the "No window" menu item
            windowMenu.getItems().add(makeWindowMenuItem(null));
        } else {
            final List<DocumentWindowController> sortedControllers
                    = new ArrayList<>(documentWindowControllers);
            Collections.sort(sortedControllers, new DocumentWindowController.TitleComparator());

            for (DocumentWindowController dwc : sortedControllers) {
                windowMenu.getItems().add(makeWindowMenuItem(dwc));
            }
        }
    }

    private MenuItem makeWindowMenuItem(final DocumentWindowController dwc) {
        final RadioMenuItem result = new RadioMenuItem();
        if (dwc != null) {
            result.setText(dwc.getStage().getTitle());
            result.setDisable(false);
            result.setSelected(dwc.getStage().isFocused());
            result.setOnAction(new WindowMenuEventHandler(dwc));
        } else {
            result.setText(I18N.getString("menu.title.no.window"));
            result.setDisable(true);
            result.setSelected(false);
        }

        return result;
    }

    private static class WindowMenuEventHandler implements EventHandler<ActionEvent> {

        private final DocumentWindowController dwc;

        public WindowMenuEventHandler(DocumentWindowController dwc) {
            this.dwc = dwc;
        }

        @Override
        public void handle(ActionEvent t) {
            dwc.getStage().toFront();
        }
    }

    /*
     * Private (MenuItemController)
     */
    abstract class MenuItemController {

        public abstract boolean canPerform();

        public abstract void perform();

        public String getTitle() {
            return null;
        }

        public boolean isSelected() {
            return false;
        }
    }

    class UndoActionController extends MenuItemController {

        @Override
        public boolean canPerform() {
            boolean result;
            if (documentWindowController == null
                    || documentWindowController.getStage().isFocused() == false) {
                result = false;
            } else {
                result = documentWindowController.getEditorController().canUndo();
            }
            return result;
        }

        @Override
        public void perform() {
            assert canPerform();
            documentWindowController.getEditorController().undo();
        }

        @Override
        public String getTitle() {
            final StringBuilder result = new StringBuilder();
            result.append(I18N.getString("menu.title.undo"));
            if (canPerform()) {
                result.append(" "); //NOI18N
                result.append(documentWindowController.getEditorController().getUndoDescription());
            }
            return result.toString();
        }
    }

    class RedoActionController extends MenuItemController {

        @Override
        public boolean canPerform() {
            boolean result;
            if (documentWindowController == null
                    || documentWindowController.getStage().isFocused() == false) {
                result = false;
            } else {
                result = documentWindowController.getEditorController().canRedo();
            }
            return result;
        }

        @Override
        public void perform() {
            assert canPerform();
            documentWindowController.getEditorController().redo();
        }

        @Override
        public String getTitle() {
            final StringBuilder result = new StringBuilder();
            result.append(I18N.getString("menu.title.redo"));
            if (canPerform()) {
                result.append(" "); //NOI18N
                result.append(documentWindowController.getEditorController().getRedoDescription());
            }
            return result.toString();
        }
    }

    class EditActionController extends MenuItemController {

        private final EditAction editAction;

        public EditActionController(EditAction editAction) {
            this.editAction = editAction;
        }

        @Override
        public boolean canPerform() {
            boolean result;
            if (documentWindowController == null
                    || documentWindowController.getStage().isFocused() == false) {
                result = false;
            } else {
                result = documentWindowController.getEditorController().canPerformEditAction(editAction);
            }
            return result;
        }

        @Override
        public void perform() {
            assert canPerform() : "editAction=" + editAction;
            documentWindowController.getEditorController().performEditAction(editAction);
        }

    }

    class ControlActionController extends MenuItemController {

        private final ControlAction controlAction;

        public ControlActionController(ControlAction controlAction) {
            this.controlAction = controlAction;
        }

        @Override
        public boolean canPerform() {
            boolean result;
            if (documentWindowController == null) {
                result = false;
            } else {
                result = documentWindowController.getEditorController().canPerformControlAction(controlAction);
            }
            return result;
        }

        @Override
        public void perform() {
            assert canPerform() : "controlAction=" + controlAction;
            documentWindowController.getEditorController().performControlAction(controlAction);
        }

    }

    class DocumentEditActionController extends MenuItemController {

        private final DocumentEditAction editAction;

        public DocumentEditActionController(DocumentEditAction editAction) {
            this.editAction = editAction;
        }

        @Override
        public boolean canPerform() {
            boolean result;
            if (documentWindowController == null
                    || documentWindowController.getStage().isFocused() == false) {
                result = false;
            } else {
                result = documentWindowController.canPerformEditAction(editAction);
            }
            return result;
        }

        @Override
        public void perform() {
            assert canPerform() : "editAction=" + editAction;
            documentWindowController.performEditAction(editAction);
        }

    }

    class DocumentControlActionController extends MenuItemController {

        private final DocumentControlAction controlAction;

        public DocumentControlActionController(DocumentControlAction controlAction) {
            this.controlAction = controlAction;
        }

        @Override
        public boolean canPerform() {
            boolean result;
            if (documentWindowController == null) {
                result = false;
            } else {
                result = documentWindowController.canPerformControlAction(controlAction);
            }
            return result;
        }

        @Override
        public void perform() {
            assert canPerform() : "controlAction=" + controlAction;
            documentWindowController.performControlAction(controlAction);
        }

    }

    class ApplicationControlActionController extends MenuItemController {

        private final ApplicationControlAction controlAction;

        public ApplicationControlActionController(ApplicationControlAction controlAction) {
            this.controlAction = controlAction;
        }

        @Override
        public boolean canPerform() {
            return SceneBuilderApp.getSingleton().canPerformControlAction(controlAction,
                    documentWindowController);
        }

        @Override
        public void perform() {
            SceneBuilderApp.getSingleton().performControlAction(controlAction,
                    documentWindowController);
        }

    }

    class AddEffectActionController extends MenuItemController {

        private final Class<? extends Effect> effectClass;

        public AddEffectActionController(Class<? extends Effect> effectClass) {
            this.effectClass = effectClass;
        }

        @Override
        public boolean canPerform() {
            boolean result;
            if (documentWindowController == null
                    || documentWindowController.getStage().isFocused() == false) {
                result = false;
            } else {
                result = documentWindowController.getEditorController().canPerformSetEffect();
            }
            return result;
        }

        @Override
        public void perform() {
            documentWindowController.getEditorController().performSetEffect(effectClass);
        }
    }

    class SetZoomActionController extends MenuItemController {

        private final double scaling;

        public SetZoomActionController(double scaling) {
            this.scaling = scaling;
        }

        @Override
        public boolean canPerform() {
            return (documentWindowController != null);
        }

        @Override
        public void perform() {
            final ContentPanelController contentPanelController
                    = documentWindowController.getContentPanelController();
            final double currentScaling
                    = contentPanelController.getScaling();
            if (MathUtils.equals(currentScaling, scaling) == false) {
                contentPanelController.setScaling(scaling);
            }
        }

        @Override
        public boolean isSelected() {
            boolean result;

            if (documentWindowController == null) {
                result = false;
            } else {
                final double currentScaling
                        = documentWindowController.getContentPanelController().getScaling();
                result = MathUtils.equals(currentScaling, scaling);
            }

            return result;
        }

    }
    
    class ZoomInActionController extends MenuItemController {

        @Override
        public boolean canPerform() {
            boolean result;
            if (documentWindowController == null) {
                result = false;
            } else {
                final ContentPanelController contentPanelController
                        = documentWindowController.getContentPanelController();
                final int currentScalingIndex
                        = findZoomScaleIndex(contentPanelController.getScaling());
                result = currentScalingIndex+1 < scalingTable.length;
            }
            return result;
        }

        @Override
        public void perform() {
            final ContentPanelController contentPanelController
                    = documentWindowController.getContentPanelController();
            final int currentScalingIndex
                    = findZoomScaleIndex(contentPanelController.getScaling());
            final double newScaling
                    = scalingTable[currentScalingIndex+1];
            contentPanelController.setScaling(newScaling);
        }

    }
    

    class ZoomOutActionController extends MenuItemController {

        @Override
        public boolean canPerform() {
            boolean result;
            if (documentWindowController == null) {
                result = false;
            } else {
                final ContentPanelController contentPanelController
                        = documentWindowController.getContentPanelController();
                final int currentScalingIndex
                        = findZoomScaleIndex(contentPanelController.getScaling());
                result = 0 <= currentScalingIndex-1;
            }
            return result;
        }

        @Override
        public void perform() {
            final ContentPanelController contentPanelController
                    = documentWindowController.getContentPanelController();
            final int currentScalingIndex
                    = findZoomScaleIndex(contentPanelController.getScaling());
            final double newScaling
                    = scalingTable[currentScalingIndex-1];
            contentPanelController.setScaling(newScaling);
        }

    }
    
    private void updatePreviewWindowSize(Size size) {
        if (documentWindowController != null
                && documentWindowController.getPreviewWindowController() != null
                && documentWindowController.getPreviewWindowController().getStage().isShowing()) {
            documentWindowController.getPreviewWindowController().setSize(size);
        }
    }

    class SetSizeActionController extends MenuItemController {

        private final EditorController.Size size;

        public SetSizeActionController(EditorController.Size size) {
            this.size = size;
        }

        @Override
        public boolean canPerform() {
            boolean res = (documentWindowController != null)
                    && (documentWindowController.getPreviewWindowController() != null)
                    && documentWindowController.getPreviewWindowController().getStage().isShowing()
                    && ! documentWindowController.getEditorController().is3D()
                    && documentWindowController.getEditorController().isNode()
                    && documentWindowController.getPreviewWindowController().sizeDoesFit(size);
            return res;
        }

        @Override
        public void perform() {
            assert documentWindowController != null;
            assert documentWindowController.getPreviewWindowController() != null;
            documentWindowController.getPreviewWindowController().setSize(size);
        }

        @Override
        public boolean isSelected() {
            boolean res;

            if (documentWindowController == null || documentWindowController.getPreviewWindowController() == null) {
                res = false;
            } else {
                Size currentSize = documentWindowController.getPreviewWindowController().getSize();
                res = (size == currentSize)
                        && documentWindowController.getPreviewWindowController().getStage().isShowing()
                        && ! documentWindowController.getPreviewWindowController().userResizedPreviewWindow()
                        && ! documentWindowController.getEditorController().is3D()
                        && documentWindowController.getEditorController().isNode();
            }
            
            return res;
        }
        
        @Override
        public String getTitle() {
            if (documentWindowController == null) {
                return null;
            }
            
            if (size == EditorController.Size.SIZE_PREFERRED) {
                String title = I18N.getString("menu.title.size.preferred");
                
                if (documentWindowController.getPreviewWindowController() != null
                        && documentWindowController.getPreviewWindowController().getStage().isShowing()
                        && ! documentWindowController.getEditorController().is3D()
                        && documentWindowController.getEditorController().isNode()) {
                        title = I18N.getString("menu.title.size.preferred.with.value",
                                getStringFromDouble(documentWindowController.getPreviewWindowController().getRoot().prefWidth(-1)),
                                getStringFromDouble(documentWindowController.getPreviewWindowController().getRoot().prefHeight(-1)));
                }
                
                return title;
            } else {
                return null;
            }
        }
    }

    class SetThemeActionController extends MenuItemController {

        private final EditorPlatform.Theme theme;

        public SetThemeActionController(EditorPlatform.Theme theme) {
            this.theme = theme;
        }

        @Override
        public boolean canPerform() {
            boolean res = documentWindowController != null;
            if (res) {
                final EditorPlatform.Theme currentTheme
                        = documentWindowController.getEditorController().getTheme();
                // CASPIAN_HIGH_CONTRAST can be selected only if another CASPIAN
                // theme is active.
                // MODENA_HIGH_CONTRAST_<*> can be selected only if another MODENA
                // theme is active.
                if (theme == EditorPlatform.Theme.CASPIAN_HIGH_CONTRAST
                        && EditorPlatform.isModena(currentTheme)) {
                    res = false;
                    caspianHighContrastThemeMenuItem.setSelected(false);
                } else if (theme == EditorPlatform.Theme.MODENA_HIGH_CONTRAST_BLACK_ON_WHITE
                        && EditorPlatform.isCaspian(currentTheme)) {
                    res = false;
                    modenaHighContrastBlackonwhiteThemeMenuItem.setSelected(false);
                } else if (theme == EditorPlatform.Theme.MODENA_HIGH_CONTRAST_WHITE_ON_BLACK
                        && EditorPlatform.isCaspian(currentTheme)) {
                    res = false;
                    modenaHighContrastWhiteonblackThemeMenuItem.setSelected(false);
                } else if (theme == EditorPlatform.Theme.MODENA_HIGH_CONTRAST_YELLOW_ON_BLACK
                        && EditorPlatform.isCaspian(currentTheme)) {
                    res = false;
                    modenaHighContrastYellowonblackThemeMenuItem.setSelected(false);
                }
            }
            
            return res;
        }

        @Override
        public void perform() {
            assert documentWindowController != null;
            EditorPlatform.Theme currentTheme
                            = documentWindowController.getEditorController().getTheme();
            EditorPlatform.Theme overridingTheme = theme;

            switch (theme) {
                case CASPIAN:
                    if (caspianHighContrastThemeMenuItem.isSelected()) {
                        overridingTheme = EditorPlatform.Theme.CASPIAN_HIGH_CONTRAST;
                    }
                    break;
                case CASPIAN_EMBEDDED:
                    if (caspianHighContrastThemeMenuItem.isSelected()) {
                        overridingTheme = EditorPlatform.Theme.CASPIAN_EMBEDDED_HIGH_CONTRAST;
                    }
                    break;
                case CASPIAN_EMBEDDED_QVGA:
                    if (caspianHighContrastThemeMenuItem.isSelected()) {
                        overridingTheme = EditorPlatform.Theme.CASPIAN_EMBEDDED_QVGA_HIGH_CONTRAST;
                    }
                    break;
                case CASPIAN_HIGH_CONTRAST:
                    switch (currentTheme) {
                        case CASPIAN:
                            if (caspianHighContrastThemeMenuItem.isSelected()) {
                                overridingTheme = EditorPlatform.Theme.CASPIAN_HIGH_CONTRAST;
                            }
                            break;
                        case CASPIAN_EMBEDDED:
                            if (caspianHighContrastThemeMenuItem.isSelected()) {
                                overridingTheme = EditorPlatform.Theme.CASPIAN_EMBEDDED_HIGH_CONTRAST;
                            }
                            break;
                        case CASPIAN_EMBEDDED_QVGA:
                            if (caspianHighContrastThemeMenuItem.isSelected()) {
                                overridingTheme = EditorPlatform.Theme.CASPIAN_EMBEDDED_QVGA_HIGH_CONTRAST;
                            }
                            break;
                        case CASPIAN_HIGH_CONTRAST:
                            if (!caspianHighContrastThemeMenuItem.isSelected()) {
                                overridingTheme = EditorPlatform.Theme.CASPIAN;
                            }
                            break;
                        case CASPIAN_EMBEDDED_HIGH_CONTRAST:
                            if (!caspianHighContrastThemeMenuItem.isSelected()) {
                                overridingTheme = EditorPlatform.Theme.CASPIAN_EMBEDDED;
                            }
                            break;
                        case CASPIAN_EMBEDDED_QVGA_HIGH_CONTRAST:
                            if (!caspianHighContrastThemeMenuItem.isSelected()) {
                                overridingTheme = EditorPlatform.Theme.CASPIAN_EMBEDDED_QVGA;
                            }
                            break;
                        default:
                            // All known 6 Caspian cases are handled above.
                            assert false;
                            break;
                    }
                    break;
                case MODENA:
                    if (modenaHighContrastBlackonwhiteThemeMenuItem.isSelected()) {
                        overridingTheme = EditorPlatform.Theme.MODENA_HIGH_CONTRAST_BLACK_ON_WHITE;
                    } else if (modenaHighContrastWhiteonblackThemeMenuItem.isSelected()) {
                        overridingTheme = EditorPlatform.Theme.MODENA_HIGH_CONTRAST_WHITE_ON_BLACK;
                    } else if (modenaHighContrastYellowonblackThemeMenuItem.isSelected()) {
                        overridingTheme = EditorPlatform.Theme.MODENA_HIGH_CONTRAST_YELLOW_ON_BLACK;
                    }
                    break;
                case MODENA_TOUCH:
                    if (modenaHighContrastBlackonwhiteThemeMenuItem.isSelected()) {
                        overridingTheme = EditorPlatform.Theme.MODENA_TOUCH_HIGH_CONTRAST_BLACK_ON_WHITE;
                    } else if (modenaHighContrastWhiteonblackThemeMenuItem.isSelected()) {
                        overridingTheme = EditorPlatform.Theme.MODENA_TOUCH_HIGH_CONTRAST_WHITE_ON_BLACK;
                    } else if (modenaHighContrastYellowonblackThemeMenuItem.isSelected()) {
                        overridingTheme = EditorPlatform.Theme.MODENA_TOUCH_HIGH_CONTRAST_YELLOW_ON_BLACK;
                    }
                    break;
                case MODENA_HIGH_CONTRAST_BLACK_ON_WHITE:
                    switch (currentTheme) {
                        case MODENA:
                            if (modenaHighContrastBlackonwhiteThemeMenuItem.isSelected()) {
                                overridingTheme = EditorPlatform.Theme.MODENA_HIGH_CONTRAST_BLACK_ON_WHITE;
                            }
                            break;
                        case MODENA_TOUCH:
                            if (modenaHighContrastBlackonwhiteThemeMenuItem.isSelected()) {
                                overridingTheme = EditorPlatform.Theme.MODENA_TOUCH_HIGH_CONTRAST_BLACK_ON_WHITE;
                            }
                            break;
                        case MODENA_HIGH_CONTRAST_BLACK_ON_WHITE:
                            if (modenaHighContrastBlackonwhiteThemeMenuItem.isSelected()) {
                                overridingTheme = EditorPlatform.Theme.MODENA;
                            }
                            break;
                        case MODENA_HIGH_CONTRAST_WHITE_ON_BLACK:
                            break;
                        case MODENA_HIGH_CONTRAST_YELLOW_ON_BLACK:
                            break;
                        case MODENA_TOUCH_HIGH_CONTRAST_BLACK_ON_WHITE:
                            if (modenaHighContrastBlackonwhiteThemeMenuItem.isSelected()) {
                                overridingTheme = EditorPlatform.Theme.MODENA_TOUCH;
                            }
                            break;
                        case MODENA_TOUCH_HIGH_CONTRAST_WHITE_ON_BLACK:
                            if (modenaHighContrastBlackonwhiteThemeMenuItem.isSelected()) {
                                overridingTheme = EditorPlatform.Theme.MODENA_TOUCH_HIGH_CONTRAST_BLACK_ON_WHITE;
                            }
                            break;
                        case MODENA_TOUCH_HIGH_CONTRAST_YELLOW_ON_BLACK:
                            if (modenaHighContrastBlackonwhiteThemeMenuItem.isSelected()) {
                                overridingTheme = EditorPlatform.Theme.MODENA_TOUCH_HIGH_CONTRAST_BLACK_ON_WHITE;
                            }
                            break;
                        default:
                            break;
                    }
                break;
                case MODENA_HIGH_CONTRAST_WHITE_ON_BLACK:
                    switch (currentTheme) {
                        case MODENA:
                                if (modenaHighContrastWhiteonblackThemeMenuItem.isSelected()) {
                                overridingTheme = EditorPlatform.Theme.MODENA_HIGH_CONTRAST_WHITE_ON_BLACK;
                            }
                            break;
                        case MODENA_TOUCH:
                                if (modenaHighContrastWhiteonblackThemeMenuItem.isSelected()) {
                                overridingTheme = EditorPlatform.Theme.MODENA_TOUCH_HIGH_CONTRAST_WHITE_ON_BLACK;
                            }
                            break;
                        case MODENA_HIGH_CONTRAST_BLACK_ON_WHITE:
                            break;
                        case MODENA_HIGH_CONTRAST_WHITE_ON_BLACK:
                            if (modenaHighContrastWhiteonblackThemeMenuItem.isSelected()) {
                                overridingTheme = EditorPlatform.Theme.MODENA;
                            }
                            break;
                        case MODENA_HIGH_CONTRAST_YELLOW_ON_BLACK:
                            break;
                        case MODENA_TOUCH_HIGH_CONTRAST_BLACK_ON_WHITE:
                            if (modenaHighContrastWhiteonblackThemeMenuItem.isSelected()) {
                                overridingTheme = EditorPlatform.Theme.MODENA_TOUCH_HIGH_CONTRAST_WHITE_ON_BLACK;
                            }
                            break;
                        case MODENA_TOUCH_HIGH_CONTRAST_WHITE_ON_BLACK:
                            if (modenaHighContrastWhiteonblackThemeMenuItem.isSelected()) {
                                overridingTheme = EditorPlatform.Theme.MODENA_TOUCH;
                            }
                            break;
                        case MODENA_TOUCH_HIGH_CONTRAST_YELLOW_ON_BLACK:
                            if (modenaHighContrastWhiteonblackThemeMenuItem.isSelected()) {
                                overridingTheme = EditorPlatform.Theme.MODENA_TOUCH_HIGH_CONTRAST_WHITE_ON_BLACK;
                            }
                            break;
                        default:
                            break;
                    }
                break;
                case MODENA_HIGH_CONTRAST_YELLOW_ON_BLACK:
                    switch (currentTheme) {
                        case MODENA:
                            if (modenaHighContrastYellowonblackThemeMenuItem.isSelected()) {
                                overridingTheme = EditorPlatform.Theme.MODENA_HIGH_CONTRAST_YELLOW_ON_BLACK;
                            }
                            break;
                        case MODENA_TOUCH:
                            if (modenaHighContrastYellowonblackThemeMenuItem.isSelected()) {
                                overridingTheme = EditorPlatform.Theme.MODENA_TOUCH_HIGH_CONTRAST_YELLOW_ON_BLACK;
                            }
                            break;
                        case MODENA_HIGH_CONTRAST_BLACK_ON_WHITE:
                            break;
                        case MODENA_HIGH_CONTRAST_WHITE_ON_BLACK:
                            break;
                        case MODENA_HIGH_CONTRAST_YELLOW_ON_BLACK:
                            if (modenaHighContrastYellowonblackThemeMenuItem.isSelected()) {
                                overridingTheme = EditorPlatform.Theme.MODENA;
                            }
                            break;
                        case MODENA_TOUCH_HIGH_CONTRAST_BLACK_ON_WHITE:
                            if (modenaHighContrastYellowonblackThemeMenuItem.isSelected()) {
                                overridingTheme = EditorPlatform.Theme.MODENA_TOUCH_HIGH_CONTRAST_YELLOW_ON_BLACK;
                            }
                            break;
                        case MODENA_TOUCH_HIGH_CONTRAST_WHITE_ON_BLACK:
                            if (modenaHighContrastYellowonblackThemeMenuItem.isSelected()) {
                                overridingTheme = EditorPlatform.Theme.MODENA_TOUCH_HIGH_CONTRAST_YELLOW_ON_BLACK;
                            }
                            break;
                        case MODENA_TOUCH_HIGH_CONTRAST_YELLOW_ON_BLACK:
                            if (modenaHighContrastYellowonblackThemeMenuItem.isSelected()) {
                                overridingTheme = EditorPlatform.Theme.MODENA_TOUCH;
                            }
                            break;
                        default:
                            break;
                    }
                break;
                default:
                    assert false;
                    break;
            }

            documentWindowController.getEditorController().setTheme(overridingTheme);
            if (overridingTheme == EditorPlatform.Theme.GLUON_MOBILE_LIGHT) {
                documentWindowController.getEditorController().setGluonTheme(EditorPlatform.GluonTheme.LIGHT);
            } else if (overridingTheme == EditorPlatform.Theme.GLUON_MOBILE_DARK) {
                documentWindowController.getEditorController().setGluonTheme(EditorPlatform.GluonTheme.DARK);
            }
        }

        @Override
        public boolean isSelected() {
            boolean res;

            if (documentWindowController == null) {
                res = false;
            } else {
                final EditorPlatform.Theme currentTheme
                        = documentWindowController.getEditorController().getTheme();

                switch (theme) {
                    case GLUON_MOBILE_LIGHT:
                        res = EditorPlatform.isGluonMobileLight(currentTheme);
                        break;
                    case GLUON_MOBILE_DARK:
                        res = EditorPlatform.isGluonMobileDark(currentTheme);
                        break;
                    // CASPIAN_HIGH_CONTRAST can be selected only if another CASPIAN
                    // theme is active.
                    case CASPIAN_HIGH_CONTRAST:
                        res = EditorPlatform.isCaspian(currentTheme);
                        break;
                    case CASPIAN:
                        res = (currentTheme == theme || currentTheme == EditorPlatform.Theme.CASPIAN_HIGH_CONTRAST);
                        break;
                    case CASPIAN_EMBEDDED:
                        res = (currentTheme == theme || currentTheme == EditorPlatform.Theme.CASPIAN_EMBEDDED_HIGH_CONTRAST);
                        break;
                    case CASPIAN_EMBEDDED_QVGA:
                        res = (currentTheme == theme || currentTheme == EditorPlatform.Theme.CASPIAN_EMBEDDED_QVGA_HIGH_CONTRAST);
                        break;
                    case MODENA_HIGH_CONTRAST_BLACK_ON_WHITE:
                        res = EditorPlatform.isModenaBlackonwhite(currentTheme)
                                && EditorPlatform.isModenaHighContrast(currentTheme);
                        break;
                    case MODENA_HIGH_CONTRAST_WHITE_ON_BLACK:
                        res = EditorPlatform.isModenaWhiteonblack(currentTheme)
                                && EditorPlatform.isModenaHighContrast(currentTheme);
                        break;
                    case MODENA_HIGH_CONTRAST_YELLOW_ON_BLACK:
                        res = EditorPlatform.isModenaYellowonblack(currentTheme)
                                && EditorPlatform.isModenaHighContrast(currentTheme);
                        break;
                    case MODENA:
                        res = (currentTheme == theme
                                || (EditorPlatform.isModenaHighContrast(currentTheme)
                                    && !EditorPlatform.isModenaTouch(currentTheme)));
                        break;
                    case MODENA_TOUCH:
                        res = (currentTheme == theme || EditorPlatform.isModenaTouchHighContrast(currentTheme));
                        break;
                    default:
                        assert false;
                        res = false;
                        break;
                }
            }

            return res;
        }
    }

    class GluonActionController extends MenuItemController {

        private EditorPlatform.GluonSwatch gluonSwatch;

        public GluonActionController(EditorPlatform.GluonSwatch gluonSwatch) {
            this.gluonSwatch = gluonSwatch;
        }

        public EditorPlatform.GluonSwatch getSwatch() {
            return gluonSwatch;
        }

        @Override
        public boolean canPerform() {
            EditorPlatform.Theme currentTheme
                    = documentWindowController.getEditorController().getTheme();
            return currentTheme.equals(EditorPlatform.Theme.GLUON_MOBILE_LIGHT) || currentTheme.equals(EditorPlatform.Theme.GLUON_MOBILE_DARK);
        }

        @Override
        public void perform() {
            documentWindowController.getEditorController().setGluonSwatch(gluonSwatch);
        }

        @Override
        public boolean isSelected() {
            boolean res = false;
            if (documentWindowController == null) {
                res = false;
            } else if (gluonSwatch != null) {
                res = gluonSwatch == documentWindowController.getEditorController().getGluonSwatch();
            }
            return res;
        }
    }

    class RemoveSceneStyleSheetActionController extends MenuItemController {

        private final File styleSheet;

        public RemoveSceneStyleSheetActionController(File file) {
            this.styleSheet = file;
        }

        @Override
        public boolean canPerform() {
            return (documentWindowController != null && styleSheet.exists());
        }

        @Override
        public void perform() {
            assert documentWindowController != null;
            documentWindowController.getSceneStyleSheetMenuController().performRemoveSceneStyleSheet(styleSheet);
        }

        @Override
        public String getTitle() {
            return styleSheet.getName();
        }
    }

    class OpenSceneStyleSheetActionController extends MenuItemController {

        private final File styleSheet;

        public OpenSceneStyleSheetActionController(File file) {
            this.styleSheet = file;
        }

        @Override
        public boolean canPerform() {
            return (documentWindowController != null && styleSheet.exists());
        }

        @Override
        public void perform() {
            assert documentWindowController != null;
            documentWindowController.getSceneStyleSheetMenuController().performOpenSceneStyleSheet(styleSheet);
        }

        @Override
        public String getTitle() {
            return styleSheet.getName();
        }
    }

    public MenuItem getMenuItem(KeyCombination key) {
        return keyToMenu.get(key);
    }

    public Set<KeyCombination> getAccelerators() {
        return keyToMenu.keySet();
    }
    
    // Returns a String with no trailing zero; if decimal part is non zero then
    // it is kept.
    private String getStringFromDouble(double value) {
        String res = Double.toString(value);
        if(res.endsWith(".0")) { //NOI18N
            res = Integer.toString((int)value);
        }
        return res;
    }

    /**
     * *************************************************************************
     * Static inner class
     * *************************************************************************
     */
    private static class ClearOpenRecentHandler implements EventHandler<ActionEvent> {

        @Override
        public void handle(ActionEvent t) {
            final PreferencesController pc = PreferencesController.getSingleton();
            pc.clearRecentItems();
        }
    }
}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/content/util/LineEquation.java<|end_filename|>
/*
 * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.kit.editor.panel.content.util;

import javafx.geometry.Point2D;

/**
 *
 * 
 */
public class LineEquation {
    
    private final double x0;
    private final double y0;
    private final double x1;
    private final double y1;
    private final double distance;
    
    public LineEquation(double x0, double y0, double x1, double y1) {
        assert ! ((x0 == x1) && (y0 == y1));
        this.x0 = x0;
        this.y0 = y0;
        this.x1 = x1;
        this.y1 = y1;
        final double dx = x1 - x0;
        final double dy = y1 - y0;
        this.distance = Math.sqrt(dx * dx + dy * dy);
    }
    
    public LineEquation(Point2D p0, Point2D p1) {
        this(p0.getX(), p0.getY(), p1.getX(), p1.getY());
    }
    
    public double getDistance() {
        return distance;
    }
    
    public boolean isValid() {
        return distance > 0.0;
    }
    
    /**
     * Returns the (x,y) of a target point given its offset along this line.
     *  
     * @param offset offset of the target point
     * @return the coordinates of the target point
     */
    public Point2D pointAtOffset(double offset) {
        /*
         *  - offset=0           matches p0
         *  - offset=+D          matches p1
         *  - offset < 0         matches line points before p0
         *  - 0 < offset < D     matches line points between p0 and p1
         *  - D < offset         matches line points after p1
         * 
         * where D is the distance between p0 and p1
         */
        return pointAtP(offsetToP(offset));
    }
    
    /**
     * Projects p on this line and returns the offset of the projected point.
     * 
     * @param x target point x
     * @param y target point y
     * @return the offset of p projection on this line.
     */
    public double offsetAtPoint(double x, double y) {
        /*
         * offset is the dot product of:
         *      - v1 : vector ((x0, y0), (x, y))
         *      - v2 : normalized vector ((x0, y0), (x1, y1))
         */
        
        final double dx1 = x - x0;
        final double dy1 = y - y0;
        final double dx2 = (x1 - x0) / distance;
        final double dy2 = (y1 - y0) / distance;
        final double result = dx1 * dx2 + dy1 * dy2;
        
        return result;
    }
    
    /**
     * Returns the (x,y) of a target point given its parametric position.
     * 
     * @param p parametric position of the target point
     * @return the coordinates of the target point
     */
    public Point2D pointAtP(double p) {
        final double x, y;
        
        /*
         *  - p=0           matches p0
         *  - p=1           matches p1
         *  - p < 0         matches line points before p0
         *  - 0 < p < 1     matches line points between p0 and p1
         *  - 1 < p         matches line points after p1
         */
        
        assert isValid();
        
        x = x0 + p * (x1 - x0);
        y = y0 + p * (y1 - y0);
       
        return new Point2D(x, y);
    }
    
    /**
     * Convert an offset to a parametric position
     * 
     * @param offset an offset along this line
     * @return the matching parametric position
     */
    public double offsetToP(double offset) {
        assert isValid();
        return offset / distance;
    }
    
    /**
     * Convert a parametric position to an offset
     * 
     * @param p a parametric position
     * @return the matching offset
     */
    public double pToOffset(double p) {
        assert isValid();
        return p * distance;
    }
}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/content/gesture/SelectWithPringGesture.java<|end_filename|>
/*
 * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.kit.editor.panel.content.gesture;

import com.oracle.javafx.scenebuilder.kit.editor.panel.content.ContentPanelController;
import com.oracle.javafx.scenebuilder.kit.editor.panel.content.gesture.mouse.AbstractMouseDragGesture;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMInstance;
import javafx.scene.input.MouseEvent;

/**
 *
 * 
 */
public class SelectWithPringGesture extends AbstractMouseDragGesture {
    
    private final FXOMInstance fxomInstance;

    public SelectWithPringGesture(ContentPanelController contentPanelController,
            FXOMInstance fxomInstance) {
        super(contentPanelController);
        this.fxomInstance = fxomInstance;
    }
    
    /*
     * AbstractMouseDragGesture
     */

    @Override
    protected void mousePressed(MouseEvent e) {
        contentPanelController.getEditorController().getSelection().select(fxomInstance);
        
        /*
         * This selection operation will callback EditModeController
         * which will remove the pring where mouse has been pressed.
         * Thus mouseExited() will be called. But not mouseDragDetected() 
         * neither mouseReleased().
         */
    }

    @Override
    protected void mouseDragDetected(MouseEvent e) {
        // Should not be called : see comment in mousePressed().
        assert false;
    }

    @Override
    protected void mouseReleased(MouseEvent e) {
        // Should not be called : see comment in mousePressed().
        assert false;
    }

    @Override
    protected void mouseExited(MouseEvent e) {
        // Mouse has exited pring because it has been removed from the
        // scene graph by the selection operation in mousePressed().

//        final Selection selection 
//                = contentPanelController.getEditorController().getSelection();
//        
//        if (selection.getAncestor() != null) {
//            
//            assert selection.isSelected(fxomInstance);
//            assert selection.getGroup() instanceof ObjectSelectionGroup;
//        
//            final ObjectSelectionGroup 
//                    osg = (ObjectSelectionGroup) selection.getGroup();
//            
//            assert osg.hasSingleParent();
//            
//            final EditorController editorController
//                    = contentPanelController.getEditorController();
//            final DocumentDragSource dragSource
//                    = new DocumentDragSource(osg.getItems());
//
//            final Dragboard db 
//                    = contentPanelController.getGlassLayer().startDragAndDrop(TransferMode.ANY);
//            db.setContent(dragSource.makeClipboardContent());
//            db.setDragView(dragSource.makeDragView());
//
////                assert editorController.getDragSource() == null;
//            editorController.setDragSource(dragSource);
//        }
    }
}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/skeleton/SkeletonBuffer.java<|end_filename|>
/*
 * Copyright (c) 2016, 2021, Gluon and/or its affiliates.
 * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.kit.skeleton;

import com.oracle.javafx.scenebuilder.kit.fxom.FXOMDocument;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMObject;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMPropertyT;
import com.oracle.javafx.scenebuilder.kit.i18n.I18N;
import com.oracle.javafx.scenebuilder.kit.util.eventnames.FindEventNamesUtil;

import java.net.URL;
import java.util.ResourceBundle;

class SkeletonBuffer {

    private final FXOMDocument document;
    private final String documentName;

    private final SkeletonSettings settings = new SkeletonSettings();

    private final SkeletonCreator skeletonCreator = new SkeletonCreator();

    SkeletonBuffer(FXOMDocument document, String documentName) {
        assert document != null;
        this.document = document;
        this.documentName = documentName;
    }

    void setLanguage(SkeletonSettings.LANGUAGE language) {
        settings.setLanguage(language);
    }

    void setTextType(SkeletonSettings.TEXT_TYPE type) {
        settings.setTextType(type);
    }

    void setFormat(SkeletonSettings.FORMAT_TYPE format) {
        settings.setFormat(format);
    }

    private boolean isFull() {
        return settings.isFull();
    }

    @Override
    public String toString() {
        if (document.getFxomRoot() == null) {
            return I18N.getString("skeleton.empty");
        } else {
            SkeletonContext.Builder builder = SkeletonContext.builder()
                .withFxController(document.getFxomRoot().getFxController())
                .withDocumentName(documentName)
                .withSettings(settings);

            construct(builder);

            return skeletonCreator.createFrom(builder.build());
        }
    }

    private void construct(SkeletonContext.Builder builder) {
        constructFxIds(builder);
        constructEventHandlers(builder);
        constructAdditionalImports(builder);
    }

    private void constructFxIds(SkeletonContext.Builder builder) {
        for (FXOMObject value : document.collectFxIds().values()) {
            builder.addFxId(value);
        }
    }

    private void constructEventHandlers(SkeletonContext.Builder builder) {
        // need to initialize the internal events map
        FindEventNamesUtil.initializeEventsMap();

        for (FXOMPropertyT eventHandler : document.getFxomRoot().collectEventHandlers()) {
            builder.addEventHandler(eventHandler);
        }
    }

    private void constructAdditionalImports(SkeletonContext.Builder builder) {
        if (isFull()) {
            builder.addImportsFor(URL.class, ResourceBundle.class);
        }
    }
}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/util/control/effectpicker/EffectPicker.java<|end_filename|>
/*
 * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.kit.util.control.effectpicker;

import com.oracle.javafx.scenebuilder.kit.util.control.paintpicker.PaintPicker;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

import javafx.beans.property.ObjectProperty;
import javafx.beans.property.ReadOnlyBooleanProperty;
import javafx.beans.property.ReadOnlyIntegerProperty;
import javafx.fxml.FXMLLoader;
import javafx.scene.Node;
import javafx.scene.control.MenuItem;
import javafx.scene.effect.Effect;
import javafx.scene.layout.Pane;

/**
 * Effects editor control.
 */
public class EffectPicker extends Pane {

    private final EffectPickerController controller;

    private static List<Class<? extends Effect>> effectClasses;

    public synchronized static Collection<Class<? extends Effect>> getEffectClasses() {
        if (effectClasses == null) {
            effectClasses = new ArrayList<>();
            effectClasses.add(javafx.scene.effect.Blend.class);
            effectClasses.add(javafx.scene.effect.Bloom.class);
            effectClasses.add(javafx.scene.effect.BoxBlur.class);
            effectClasses.add(javafx.scene.effect.ColorAdjust.class);
            effectClasses.add(javafx.scene.effect.ColorInput.class);
            effectClasses.add(javafx.scene.effect.DisplacementMap.class);
            effectClasses.add(javafx.scene.effect.DropShadow.class);
            effectClasses.add(javafx.scene.effect.GaussianBlur.class);
            effectClasses.add(javafx.scene.effect.Glow.class);
            effectClasses.add(javafx.scene.effect.ImageInput.class);
            effectClasses.add(javafx.scene.effect.InnerShadow.class);
            effectClasses.add(javafx.scene.effect.Lighting.class);
            effectClasses.add(javafx.scene.effect.MotionBlur.class);
            effectClasses.add(javafx.scene.effect.PerspectiveTransform.class);
            effectClasses.add(javafx.scene.effect.Reflection.class);
            effectClasses.add(javafx.scene.effect.SepiaTone.class);
            effectClasses.add(javafx.scene.effect.Shadow.class);
            effectClasses = Collections.unmodifiableList(effectClasses);
        }

        return effectClasses;
    }

    public EffectPicker(EffectPicker.Delegate epd, PaintPicker.Delegate ppd) {
        final FXMLLoader loader = new FXMLLoader();
        loader.setLocation(EffectPicker.class.getResource("EffectPicker.fxml")); //NOI18N

        try {
            // Loading
            final Object rootObject = loader.load();
            assert rootObject instanceof Node;
            final Node rootNode = (Node) rootObject;
            getChildren().add(rootNode);

            // Retrieving the controller
            final Object ctl = loader.getController();
            assert ctl instanceof EffectPickerController;
            this.controller = (EffectPickerController) ctl;
            this.controller.setEffectPickerDelegate(epd);
            this.controller.setPaintPickerDelegate(ppd);
        } catch (IOException ex) {
            throw new IllegalStateException(ex);
        }
    }

    public final ObjectProperty<Effect> rootEffectProperty() {
        return controller.rootEffectProperty();
    }

    public final void setRootEffectProperty(Effect value) {
        // Update model
        controller.setRootEffectProperty(value);
        // Update UI
        controller.updateUI();
    }

    public final Effect getRootEffectProperty() {
        return controller.getRootEffectProperty();
    }

    public ReadOnlyIntegerProperty revisionProperty() {
        return controller.revisionProperty();
    }
    
    public final ReadOnlyBooleanProperty liveUpdateProperty() {
        return controller.liveUpdateProperty();
    }

    public boolean isLiveUpdate() {
        return controller.isLiveUpdate();
    }
    
    public String getEffectPath() {
        return controller.getEffectPath();
    }

    public List<MenuItem> getMenuItems() {
        final List<MenuItem> menuItems = new ArrayList<>();
        for (final Class<? extends Effect> clazz : getEffectClasses()) {
            final MenuItem mi = new MenuItem(clazz.getSimpleName());
            mi.setOnAction(t -> {
                final Effect effect = Utils.newInstance(clazz);
                setRootEffectProperty(effect);
                controller.incrementRevision();
            });
            menuItems.add(mi);
        }
        return menuItems;
    }
    
    public static interface Delegate {
        public void handleError(String warningKey, Object... arguments);
    }
}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/content/util/BoundsUtils.java<|end_filename|>
/*
 * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */

package com.oracle.javafx.scenebuilder.kit.editor.panel.content.util;

import com.oracle.javafx.scenebuilder.kit.util.MathUtils;
import javafx.geometry.BoundingBox;
import javafx.geometry.Bounds;
import javafx.geometry.Point2D;
import javafx.scene.Node;

/**
 *
 */
public class BoundsUtils {
    
    public static Bounds makeBounds(Point2D p1, Point2D p2) {
        return new BoundingBox(
                Math.min(p1.getX(), p2.getX()),
                Math.min(p1.getY(), p2.getY()),
                Math.abs(p2.getX() - p1.getX()),
                Math.abs(p2.getY() - p1.getY()));
    }
    
    
    public static boolean equals(Bounds b1, Bounds b2) {
        return MathUtils.equals(b1.getMinX(), b2.getMinX()) &&
                MathUtils.equals(b1.getMinY(), b2.getMinY()) &&
                MathUtils.equals(b1.getMaxX(), b2.getMaxX()) &&
                MathUtils.equals(b1.getMaxY(), b2.getMaxY());
                
    }
    
    public static Bounds inset(Bounds bounds, double dx, double dy) {
        final double minX = bounds.getMinX() + dx;
        final double minY = bounds.getMinY() + dy;
        final double maxX = bounds.getMaxX() - dx;
        final double maxY = bounds.getMaxY() - dy;
        return new BoundingBox(minX, minY, maxX - minX, maxY - minY);
    }
    
    
    public static EdgeInfo distanceToEdges(Bounds b, double x, double y, Node node) {
        assert b != null;
        assert node != null;
        assert node.getScene() != null;

        final EdgeInfo result;
        if (b.isEmpty()) {
            result = null;
        } else {
            final double minX = b.getMinX();
            final double minY = b.getMinY();
            final double maxX = b.getMaxX();
            final double maxY = b.getMaxY();
            
            final Point2D p1 = node.localToScene(minX, minY, true /* rootScene */);
            final Point2D p2 = node.localToScene(maxX, minY, true /* rootScene */);
            final Point2D p3 = node.localToScene(maxX, maxY, true /* rootScene */);
            final Point2D p4 = node.localToScene(minX, maxY, true /* rootScene */);

            final LineEquation nl = new LineEquation(p1, p2);
            final LineEquation el = new LineEquation(p2, p3);
            final LineEquation sl = new LineEquation(p3, p4);
            final LineEquation wl = new LineEquation(p4, p1);

            final Point2D p = node.localToScene(x, y, true /* rootScene */);
            final double sceneX = p.getX();
            final double sceneY = p.getY();
            final Point2D nh = nl.pointAtOffset(nl.offsetAtPoint(sceneX, sceneY));
            final Point2D eh = el.pointAtOffset(el.offsetAtPoint(sceneX, sceneY));
            final Point2D sh = sl.pointAtOffset(sl.offsetAtPoint(sceneX, sceneY));
            final Point2D wh = wl.pointAtOffset(wl.offsetAtPoint(sceneX, sceneY));

            final double nd = distance(nh, p);
            final double ed = distance(eh, p);
            final double sd = distance(sh, p);
            final double wd = distance(wh, p);

            return new EdgeInfo(nd, ed, sd, wd);
        }
        
        return result;
    }
    
    public static class EdgeInfo {
        private final double northDistance;
        private final double eastDistance;
        private final double southDistance;
        private final double westDistance;

        public EdgeInfo(double northDistance, double eastDistance,
                double southDistance, double westDistance) {
            this.northDistance = northDistance;
            this.eastDistance = eastDistance;
            this.southDistance = southDistance;
            this.westDistance = westDistance;
        }

        public double getNorthDistance() {
            return northDistance;
        }

        public double getEastDistance() {
            return eastDistance;
        }

        public double getSouthDistance() {
            return southDistance;
        }

        public double getWestDistance() {
            return westDistance;
        }

        @Override
        public String toString() {
            return "EdgeInfo{" //NOI18N
                    + "northDistance=" + northDistance  //NOI18N
                    + ", eastDistance=" + eastDistance  //NOI18N
                    + ", southDistance=" + southDistance  //NOI18N
                    + ", westDistance=" + westDistance  //NOI18N
                    + '}'; //NOI18N
        }

    }
    
    
    private static double distance(Point2D p1, Point2D p2) {
        final double dx = p2.getX() - p1.getX();
        final double dy = p2.getY() - p1.getY();
        return Math.sqrt(dx * dx + dy * dy);
    }
}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/inspector/editors/CursorEditor.java<|end_filename|>
/*
 * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.kit.editor.panel.inspector.editors;

import com.oracle.javafx.scenebuilder.kit.i18n.I18N;
import com.oracle.javafx.scenebuilder.kit.metadata.property.ValuePropertyMetadata;
import com.oracle.javafx.scenebuilder.kit.metadata.property.value.CursorPropertyMetadata;

import java.util.Map;
import java.util.Set;

import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.Cursor;
import javafx.scene.ImageCursor;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.control.CheckMenuItem;
import javafx.scene.control.Label;
import javafx.scene.control.MenuButton;
import javafx.scene.control.MenuItem;

/**
 * Insets editor (for top/right/bottom/left fields).
 *
 *
 */
public class CursorEditor extends PropertyEditor {

    private Parent root;

    @FXML
    private MenuButton cursorMb;
    @FXML
    private CheckMenuItem inheritedMi;
    @FXML
    private Label inheritedLb;

    private Cursor cursor = Cursor.DEFAULT;
    private String inheritedText, inheritedParentText;

    public CursorEditor(ValuePropertyMetadata propMeta, Set<Class<?>> selectedClasses) {
        super(propMeta, selectedClasses);
        initialize();
    }

    // Separate method to please FindBugs
    private void initialize() {
        root = EditorUtils.loadFxml("CursorEditor.fxml", this); //NOI18N
        int index = 0;
        Map<Cursor, String> predefinedCursors = CursorPropertyMetadata.getCursorMap();
        // Order the cursors
        Cursor[] cursorList = {Cursor.DEFAULT, Cursor.CLOSED_HAND, Cursor.OPEN_HAND, Cursor.HAND, Cursor.MOVE, Cursor.WAIT,
            Cursor.TEXT, Cursor.V_RESIZE, Cursor.H_RESIZE, Cursor.N_RESIZE, Cursor.NE_RESIZE, Cursor.E_RESIZE, Cursor.SE_RESIZE,
            Cursor.S_RESIZE, Cursor.SW_RESIZE, Cursor.W_RESIZE, Cursor.NW_RESIZE,
            Cursor.CROSSHAIR, Cursor.NONE, Cursor.DISAPPEAR};
        for (Cursor cursorObj : cursorList) {
            String cursorStr = predefinedCursors.get(cursorObj);
            final Label cursorLabel = new Label(cursorStr);
            cursorLabel.setCursor(cursorObj);
            CheckMenuItem menuItem = new CheckMenuItem();
            menuItem.setGraphic(cursorLabel);
            // add predefined cursors before "inherited" menu item
            cursorMb.getItems().add(index++, menuItem);
            menuItem.setOnAction(e -> {
                selectCursor(cursorStr);
                userUpdateValueProperty(getValue());
            });
        }

        // "inherited" menu item
        inheritedText = I18N.getString("inspector.cursor.inherited");
        inheritedParentText = I18N.getString("inspector.cursor.inheritedparent");
        inheritedLb.setText(inheritedParentText);
    }

    @Override
    public Node getValueEditor() {
        return super.handleGenericModes(root);
    }

    @Override
    public Object getValue() {
        return cursor;
    }

    @Override
    public void setValue(Object value) {
        setValueGeneric(value);
        if (isSetValueDone()) {
            return;
        }

        if (value == null) {
            cursor = null;
            selectCursor(inheritedParentText);

        } else {
            assert value instanceof Cursor;
            if (value instanceof ImageCursor) {
                // Custom cursor
                selectCursor(""); //NOI18N
                cursorMb.setText(I18N.getString("inspector.cursor.custom"));
            } else {
                // predefined cursor
                // select the corresponding menu item
                selectCursor(((Cursor) value).toString());
            }
        }
    }

    @Override
    public void reset(ValuePropertyMetadata propMeta, Set<Class<?>> selectedClasses) {
        super.reset(propMeta, selectedClasses);
    }

    @Override
    protected void valueIsIndeterminate() {
        handleIndeterminate(cursorMb);
    }

    //
    // FXML methods
    //
    @FXML
    void inherited(ActionEvent event) {
        cursor = null;
        selectCursor(inheritedParentText);
        userUpdateValueProperty(getValue());
    }

    // Select the menu item corresponding to a cursor string.
    private void selectCursor(String cursorStr) {
        for (MenuItem menuItem : cursorMb.getItems()) {
            if (!(menuItem instanceof CheckMenuItem)) {
                // inherited action
                continue;
            }
            CheckMenuItem checkMenuItem = (CheckMenuItem) menuItem;
            assert checkMenuItem.getGraphic() instanceof Label;
            if (cursorStr.equals(((Label) checkMenuItem.getGraphic()).getText())) {
                checkMenuItem.setSelected(true);
                // set the menu button text
                if (cursorStr.equals(inheritedParentText)) {
                    // change the menu button text to be shorter in this case...
                    cursorMb.setText(inheritedText);
                } else {
                    cursorMb.setText(cursorStr);
                }
                cursor = checkMenuItem.getGraphic().getCursor();
            } else {
                checkMenuItem.setSelected(false);
            }
        }
    }

    @Override
    public void requestFocus() {
        EditorUtils.doNextFrame(() -> cursorMb.requestFocus());
    }
}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/job/reference/ReferencesUpdater.java<|end_filename|>
/*
 * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */

package com.oracle.javafx.scenebuilder.kit.editor.job.reference;

import com.oracle.javafx.scenebuilder.kit.editor.EditorController;
import com.oracle.javafx.scenebuilder.kit.editor.job.Job;
import com.oracle.javafx.scenebuilder.kit.editor.job.atomic.RemoveNodeJob;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMCloner;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMCollection;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMDocument;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMInstance;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMIntrinsic;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMNode;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMNodes;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMObject;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMProperty;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMPropertyC;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMPropertyT;
import com.oracle.javafx.scenebuilder.kit.metadata.util.PrefixedValue;
import com.oracle.javafx.scenebuilder.kit.metadata.util.PropertyName;
import com.oracle.javafx.scenebuilder.kit.util.JavaLanguage;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 *
 */
public class ReferencesUpdater {
    
    private final EditorController editorController;
    private final FXOMDocument fxomDocument;
    private final List<Job> executedJobs = new LinkedList<>();
    private final Set<String> declaredFxIds = new HashSet<>();
    private final FXOMCloner cloner;
    
    public ReferencesUpdater(EditorController editorController) {
        assert editorController != null;
        assert editorController.getFxomDocument() != null;
        this.editorController = editorController;
        this.fxomDocument = editorController.getFxomDocument();
        this.cloner = new FXOMCloner(this.fxomDocument);
    }
    
    public void update() {
        if (fxomDocument.getFxomRoot() != null) {
            declaredFxIds.clear();
            update(fxomDocument.getFxomRoot());
        }
    }
    
    public List<Job> getExecutedJobs() {
        return new LinkedList<>(executedJobs);
    }
    
    
    /*
     * Private
     */
    
    private void update(FXOMNode node) {
        if (node instanceof FXOMCollection) {
            updateCollection((FXOMCollection) node);
        } else if (node instanceof FXOMInstance) {
            updateInstance((FXOMInstance) node);
        } else if (node instanceof FXOMIntrinsic) {
            updateIntrinsic((FXOMIntrinsic) node);
        } else if (node instanceof FXOMPropertyC) {
            updatePropertyC((FXOMPropertyC) node);
        } else if (node instanceof FXOMPropertyT) {
            updatePropertyT((FXOMPropertyT) node);
        } else {
            throw new RuntimeException("Bug"); //NOI18N
        }
    }
    
    
    private void updateCollection(FXOMCollection collection) {
        if (collection.getFxId() != null) {
            declaredFxIds.add(collection.getFxId());
        }
        final List<FXOMObject> items = collection.getItems();
        for (int i = 0, count = items.size(); i < count; i++) {
            update(items.get(i));
        }
    }
    
    
    private void updateInstance(FXOMInstance instance) {
        if (instance.getFxId() != null) {
            declaredFxIds.add(instance.getFxId());
        }
        final Map<PropertyName, FXOMProperty> properties = instance.getProperties();
        final List<PropertyName> names = new LinkedList<>(properties.keySet());
        for (PropertyName propertyName : names) {
            update(properties.get(propertyName));
        }
    }
    
    
    private void updateIntrinsic(FXOMIntrinsic intrinsic) {
        switch(intrinsic.getType()) {
            case FX_REFERENCE:
            case FX_COPY:
                updateReference(intrinsic, intrinsic.getSource());
                break;
            default:
                break;
        }
    }
    
    
    private void updatePropertyC(FXOMPropertyC property) {
        final List<FXOMObject> values = property.getValues();
        for (int i = 0, count = values.size(); i < count; i++) {
            update(values.get(i));
        }
    }
    
    
    private void updatePropertyT(FXOMPropertyT property) {
        final PrefixedValue pv = new PrefixedValue(property.getValue());
        if (pv.isExpression()) {
            final String suffix = pv.getSuffix();
            if (JavaLanguage.isIdentifier(suffix)) {
                updateReference(property, suffix);
            }
        }
    }
    
    
    private void updateReference(FXOMNode r, String fxId) {
        assert (r instanceof FXOMPropertyT) || (r instanceof FXOMIntrinsic);
        assert fxId != null;
        
        if (declaredFxIds.contains(fxId) == false) {
            // r is a forward reference
            //
            // 0) r is a toggleGroup reference
            //    => if toggle group exists, we swap it with the reference
            //    => if not, replace the reference by a new toggle group
            // 1) r is a weak reference (like labelFor)
            //    => we remove the reference
            // 2) else r is a strong reference
            //    => we expand the reference
            
            
            final FXOMObject declarer = fxomDocument.searchWithFxId(fxId);

            // 0)
            if (FXOMNodes.isToggleGroupReference(r)) {
                final Job fixJob = new FixToggleGroupReferenceJob(r, editorController);
                fixJob.execute();
                executedJobs.add(fixJob);
                declaredFxIds.add(fxId);
            }
            
            // 1
            else if (FXOMNodes.isWeakReference(r) || (declarer == null)) {
                final Job removeJob = new RemoveNodeJob(r, editorController);
                removeJob.execute();
                executedJobs.add(removeJob);
                
            // 2)
            } else {
                
                final Job expandJob = new ExpandReferenceJob(r, cloner, editorController);
                expandJob.execute();
                executedJobs.add(expandJob);
            }
        }
    }
    
}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/skeleton/SkeletonFileWriterErrorAlert.java<|end_filename|>
/*
 * Copyright (c) 2021, Gluon and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation and Gluon nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.kit.skeleton;

import java.io.File;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Objects;
import java.util.function.BiConsumer;
import java.util.function.Supplier;

import com.oracle.javafx.scenebuilder.kit.alert.SBAlert;
import com.oracle.javafx.scenebuilder.kit.i18n.I18N;

import javafx.application.Platform;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.control.TextArea;
import javafx.stage.Stage;

/**
 * This class provides an alert for the case that a controller skeleton
 * cannot be written into a file.
 * 
 * The alert provides the actual file name, the error message and the
 * exception stack trace inside the expandable pane.
 *
 */
final class SkeletonFileWriterErrorAlert implements BiConsumer<File, Exception> {

    private final Supplier<Stage> stageSupplier;

    /**
     * Creates a bi-consumer which accepts a file and an exception
     * so that an appropriate error message can be presented to the
     * user.
     *  
     * @param stageSupplier Reference to the parent JavaFX window
     */
    SkeletonFileWriterErrorAlert(Supplier<Stage> stageSupplier) {
        this.stageSupplier = Objects.requireNonNull(stageSupplier);
    }

    @Override
    public void accept(File skeletonFile, Exception error) {
        SBAlert alert = prepareErrorAlert(skeletonFile, error);
        String exceptionDetails = collectExceptionDetails(error);
        TextArea textArea = new TextArea(exceptionDetails);
        textArea.setEditable(false);
        textArea.setWrapText(true);
        textArea.setMaxWidth(Double.MAX_VALUE);
        textArea.setMaxHeight(Double.MAX_VALUE);
        alert.getDialogPane().setExpandableContent(textArea);
        Platform.runLater(()->alert.showAndWait());
    }

    private SBAlert prepareErrorAlert(File skeletonFile, Exception error) {
        var alert = new SBAlert(AlertType.ERROR, stageSupplier.get());
        alert.setTitle(I18N.getString("alert.skeleton.title"));
        alert.setHeaderText(I18N.getString("alert.skeleton.header.failed"));
        alert.setContentText(I18N.getString("alert.skeleton.saving.failed")+"\n"
                            + skeletonFile + "\n\n" 
                            + error.getLocalizedMessage());
        return alert;
    }

    private String collectExceptionDetails(Exception error) {
        StringWriter stringWriter = new StringWriter();
        PrintWriter printWriter = new PrintWriter(stringWriter);
        error.printStackTrace(printWriter);
        return stringWriter.toString();
    }

}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/metadata/property/value/CursorPropertyMetadata.java<|end_filename|>
/*
 * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.kit.metadata.property.value;

import com.oracle.javafx.scenebuilder.kit.fxom.FXOMDocument;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMInstance;
import com.oracle.javafx.scenebuilder.kit.metadata.util.DesignImage;
import com.oracle.javafx.scenebuilder.kit.metadata.util.InspectorPath;
import com.oracle.javafx.scenebuilder.kit.metadata.util.PropertyName;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import javafx.scene.Cursor;
import javafx.scene.ImageCursor;

/**
 *
 * 
 */
public class CursorPropertyMetadata extends ComplexPropertyMetadata<Cursor> {
    
    private final DoublePropertyMetadata hotspotXMetadata
            = new DoublePropertyMetadata(new PropertyName("hotspotX"), 
            DoublePropertyMetadata.DoubleKind.COORDINATE, true, 0.0, InspectorPath.UNUSED);
    private final DoublePropertyMetadata hotspotYMetadata
            = new DoublePropertyMetadata(new PropertyName("hotspotY"), 
            DoublePropertyMetadata.DoubleKind.COORDINATE, true, 0.0, InspectorPath.UNUSED);
    private final ImagePropertyMetadata imageMetadata
            = new ImagePropertyMetadata(new PropertyName("image"), 
            true, null, InspectorPath.UNUSED);

    private static Map<Cursor, String> cursorMap;
    
    public CursorPropertyMetadata(PropertyName name, boolean readWrite, 
            Cursor defaultValue, InspectorPath inspectorPath) {
        super(name, Cursor.class, readWrite, defaultValue, inspectorPath);
    }

    public static synchronized Map<Cursor, String> getCursorMap() {
        if (cursorMap == null) {
            cursorMap = new HashMap<>();
            cursorMap.put(Cursor.CLOSED_HAND,   "CLOSED_HAND"   );
            cursorMap.put(Cursor.CROSSHAIR,     "CROSSHAIR"     );
            cursorMap.put(Cursor.DEFAULT,       "DEFAULT"       );
            cursorMap.put(Cursor.DISAPPEAR,     "DISAPPEAR"     );
            cursorMap.put(Cursor.E_RESIZE,      "E_RESIZE"      );
            cursorMap.put(Cursor.HAND,          "HAND"          );
            cursorMap.put(Cursor.H_RESIZE,      "H_RESIZE"      );
            cursorMap.put(Cursor.MOVE,          "MOVE"          );
            cursorMap.put(Cursor.NE_RESIZE,     "NE_RESIZE"     );
            cursorMap.put(Cursor.NONE,          "NONE"          );
            cursorMap.put(Cursor.NW_RESIZE,     "NW_RESIZE"     );
            cursorMap.put(Cursor.N_RESIZE,      "N_RESIZE"      );
            cursorMap.put(Cursor.OPEN_HAND,     "OPEN_HAND"     );
            cursorMap.put(Cursor.SE_RESIZE,     "SE_RESIZE"     );
            cursorMap.put(Cursor.SW_RESIZE,     "SW_RESIZE"     );
            cursorMap.put(Cursor.S_RESIZE,      "S_RESIZE"      );
            cursorMap.put(Cursor.TEXT,          "TEXT"          );
            cursorMap.put(Cursor.V_RESIZE,      "V_RESIZE"      );
            cursorMap.put(Cursor.WAIT,          "WAIT"          );
            cursorMap.put(Cursor.W_RESIZE,      "W_RESIZE"      );
            cursorMap = Collections.unmodifiableMap(cursorMap);
        }
        
        return cursorMap;
    }
    
    
    /*
     * ComplexPropertyMetadata
     */
    @Override
    public FXOMInstance makeFxomInstanceFromValue(Cursor value, FXOMDocument fxomDocument) {
        final FXOMInstance result;
        
        final String cursorName = getCursorMap().get(value);
        if (cursorName != null) {
            // It's a standard cursor
            result = new FXOMInstance(fxomDocument, Cursor.class);
            result.setFxConstant(cursorName);
        } else if (value instanceof ImageCursor) {
            final ImageCursor imageCursor = (ImageCursor) value;
            result = new FXOMInstance(fxomDocument, ImageCursor.class);
            hotspotXMetadata.setValue(result, imageCursor.getHotspotX());
            hotspotYMetadata.setValue(result, imageCursor.getHotspotY());
            imageMetadata.setValue(result, new DesignImage(imageCursor.getImage()));
        } else {
            // Emergency code
            assert false;
            result = new FXOMInstance(fxomDocument, Cursor.class);
            result.setFxConstant(getCursorMap().get(Cursor.DEFAULT));
        }
        
        return result;
    }
}



<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/metadata/util/DesignHierarchyPath.java<|end_filename|>
/*
 * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.kit.metadata.util;

import com.oracle.javafx.scenebuilder.kit.fxom.FXOMObject;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 *
 */
public class DesignHierarchyPath {
    
    private final List<FXOMObject> pathItems = new ArrayList<>();
    
    public DesignHierarchyPath() {
    }
    
    public DesignHierarchyPath(FXOMObject fxomObject) {
        assert fxomObject != null;
        FXOMObject o = fxomObject;
        do {
            pathItems.add(0, o);
            o = o.getParentObject();
        } while (o != null);
    }
    
    @Override
    public boolean equals(Object obj) {
        if (obj instanceof DesignHierarchyPath) {
            final DesignHierarchyPath path = (DesignHierarchyPath) obj;
            return this.pathItems.equals(path.pathItems);
        } else {
            return false;
        }
    }

    @Override
    public int hashCode() {
        int hash = 7;
        hash = 29 * hash + Objects.hashCode(this.pathItems);
        return hash;
    }
    
    public int getSize() {
        return pathItems.size();
    }
    
    public boolean isEmpty() {
        return pathItems.isEmpty();
    }
    
    public FXOMObject getRoot() {
        final FXOMObject result;
        
        if (pathItems.isEmpty()) {
            result = null;
        } else {
            result = pathItems.get(0);
        }
        
        return result;
    }
    
    public FXOMObject getLeaf() {
        final FXOMObject result;
        
        if (pathItems.isEmpty()) {
            result = null;
        } else {
            result = pathItems.get(pathItems.size()-1);
        }
        
        return result;
    }
    
    public DesignHierarchyPath getCommonPathWith(DesignHierarchyPath another) {
        final DesignHierarchyPath result = new DesignHierarchyPath();
        
        assert another != null;
        
        int i = 0, count = Math.min(this.getSize(), another.getSize());
        while ((i < count) && this.pathItems.get(i) == another.pathItems.get(i)) {
            result.pathItems.add(this.pathItems.get(i));
            i++;
        }
        
        return result;
    }
}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/job/gridpane/v2/GridSnapshot.java<|end_filename|>
/*
 * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */

package com.oracle.javafx.scenebuilder.kit.editor.job.gridpane.v2;

import com.oracle.javafx.scenebuilder.kit.fxom.FXOMObject;
import com.oracle.javafx.scenebuilder.kit.util.GridBounds;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

/**
 *
 */
public class GridSnapshot {
    
    private final Map<FXOMObject, GridSnapshotItem> items = new HashMap<>();
    
    public GridSnapshot(Collection<FXOMObject> fxomObjects) {
        assert fxomObjects != null;
        assert fxomObjects.isEmpty() == false; // (1)
        
        for (FXOMObject fxomObject : fxomObjects) {
            assert items.containsKey(fxomObject) == false;
            items.put(fxomObject, new GridSnapshotItem(fxomObject));
        }
    }
    
    public GridSnapshot(Collection<FXOMObject> fxomObjects, int columnCount) {
        assert fxomObjects != null;
        assert fxomObjects.isEmpty() == false;
        assert columnCount >= 1;
        
        int columnIndex = 0;
        int rowIndex = 0;
        for (FXOMObject fxomObject : fxomObjects) {
            items.put(fxomObject, new GridSnapshotItem(fxomObject, columnIndex, rowIndex));
            columnIndex++;
            if (columnIndex >= columnCount) {
                columnIndex = 0;
                rowIndex++;
            }
        }
    }
    
    public int getColumnIndex(FXOMObject fxomObject) {
        assert fxomObject != null;
        assert items.containsKey(fxomObject);
        return items.get(fxomObject).getColumnIndex();
    }
    
    public int getRowIndex(FXOMObject fxomObject) {
        assert fxomObject != null;
        assert items.containsKey(fxomObject);
        return items.get(fxomObject).getRowIndex();
    }
    
    public GridBounds getBounds() {
        GridBounds result = null;
        
        for (Map.Entry<FXOMObject, GridSnapshotItem> e : items.entrySet()) {
            if (result == null) {
                result = e.getValue().getBounds();
            } else {
                result = result.union(e.getValue().getBounds());
            }
        }
        
        assert result != null; // Because (1)
        
        return result;
    }
}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/drag/target/GridPaneDropTarget.java<|end_filename|>
/*
 * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */

package com.oracle.javafx.scenebuilder.kit.editor.drag.target;

import com.oracle.javafx.scenebuilder.kit.editor.EditorController;
import com.oracle.javafx.scenebuilder.kit.editor.drag.source.AbstractDragSource;
import com.oracle.javafx.scenebuilder.kit.editor.job.BatchJob;
import com.oracle.javafx.scenebuilder.kit.editor.job.InsertAsSubComponentJob;
import com.oracle.javafx.scenebuilder.kit.editor.job.Job;
import com.oracle.javafx.scenebuilder.kit.editor.job.gridpane.v2.GridSnapshot;
import com.oracle.javafx.scenebuilder.kit.editor.job.gridpane.v2.InsertColumnJob;
import com.oracle.javafx.scenebuilder.kit.editor.job.gridpane.v2.InsertRowJob;
import com.oracle.javafx.scenebuilder.kit.editor.job.gridpane.v2.MoveCellContentJob;
import com.oracle.javafx.scenebuilder.kit.editor.job.atomic.ClearSelectionJob;
import com.oracle.javafx.scenebuilder.kit.editor.job.atomic.RemoveObjectJob;
import com.oracle.javafx.scenebuilder.kit.editor.job.atomic.UpdateSelectionJob;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMInstance;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMObject;
import com.oracle.javafx.scenebuilder.kit.metadata.util.DesignHierarchyMask;
import com.oracle.javafx.scenebuilder.kit.util.Deprecation;
import com.oracle.javafx.scenebuilder.kit.util.GridBounds;
import java.util.List;
import javafx.scene.Node;
import javafx.scene.layout.GridPane;

/**
 *
 */
public class GridPaneDropTarget extends AbstractDropTarget {
    
    public enum ColumnArea {
        LEFT, CENTER, RIGHT
    }
    
    public enum RowArea {
        TOP, CENTER, BOTTOM
    }

    private final FXOMObject targetGridPane;
    private final int targetIndex;
    private final int targetColumnIndex;
    private final int targetRowIndex;
    private final ColumnArea targetColumnArea;
    private final RowArea targetRowArea;

    public GridPaneDropTarget(FXOMObject targetGridPane, 
            int columnIndex, int rowIndex, 
            ColumnArea targetColumnArea, RowArea targetRowArea) {
        assert targetGridPane != null;
        assert targetGridPane.getSceneGraphObject() instanceof GridPane;
        assert columnIndex >= 0;
        assert rowIndex >= 0;
        
        this.targetGridPane = targetGridPane;
        this.targetIndex = -1;
        this.targetColumnIndex = columnIndex;
        this.targetRowIndex = rowIndex;
        this.targetColumnArea = targetColumnArea;
        this.targetRowArea = targetRowArea;
    }

    public GridPaneDropTarget(FXOMObject targetGridPane, int targetIndex) {
        assert targetGridPane != null;
        assert targetGridPane.getSceneGraphObject() instanceof GridPane;
        assert targetIndex >= -1;
        
        this.targetGridPane = targetGridPane;
        this.targetIndex = targetIndex;
        this.targetColumnIndex = 0;
        this.targetRowIndex = 0;
        
        final GridPane gridPane = (GridPane) targetGridPane.getSceneGraphObject();
        if (Deprecation.getGridPaneColumnCount(gridPane) == 0) {
            this.targetColumnArea = ColumnArea.LEFT;
        } else {
            this.targetColumnArea = ColumnArea.CENTER;
        }
        if (Deprecation.getGridPaneRowCount(gridPane) == 0) {
            this.targetRowArea = RowArea.TOP;
        } else {
            this.targetRowArea = RowArea.CENTER;
        }
    }

    public int getTargetColumnIndex() {
        return targetColumnIndex;
    }

    public int getTargetRowIndex() {
        return targetRowIndex;
    }

    public ColumnArea getTargetColumnArea() {
        return targetColumnArea;
    }

    public RowArea getTargetRowArea() {
        return targetRowArea;
    }
    
    /*
     * AbstractDropTarget
     */
    @Override
    public FXOMObject getTargetObject() {
        return targetGridPane;
    }

    @Override
    public boolean acceptDragSource(AbstractDragSource dragSource) {
        assert dragSource != null;
        
        final boolean result;
        if (dragSource.getDraggedObjects().isEmpty()) {
            result = false;
        } else {
            final DesignHierarchyMask m = new DesignHierarchyMask(targetGridPane);
            if (m.isAcceptingSubComponent(dragSource.getDraggedObjects())) {
                final FXOMObject draggedObject0 = dragSource.getDraggedObjects().get(0);
                assert draggedObject0.getSceneGraphObject() instanceof Node;
                
                final Node draggedNode0 = (Node) draggedObject0.getSceneGraphObject();
                final Integer columIndexObj = GridPane.getColumnIndex(draggedNode0);
                final Integer rowIndexObj = GridPane.getRowIndex(draggedNode0);
                final int currentColumnIndex = (columIndexObj == null) ? 0 : columIndexObj;
                final int currentRowIndex = (rowIndexObj == null) ? 0 : rowIndexObj;
                
                final boolean sameContainer 
                        = targetGridPane == draggedObject0.getParentObject();
                final boolean sameColumnIndex
                        = targetColumnIndex == currentColumnIndex;
                final boolean sameRowIndex
                        = targetRowIndex == currentRowIndex;
                final boolean sameArea
                        = (targetColumnArea == ColumnArea.CENTER)
                        && (targetRowArea == RowArea.CENTER);
                        
                result = (sameContainer == false) 
                        || (sameColumnIndex == false)
                        || (sameRowIndex == false)
                        || (sameArea == false);
            } else {
                result = false;
            }
        }
        
        return result;
    }

    @Override
    public Job makeDropJob(AbstractDragSource dragSource, EditorController editorController) {
        assert acceptDragSource(dragSource); // (1)
        assert editorController != null;
        
        final boolean shouldRefreshSceneGraph = true;
        final BatchJob result = new BatchJob(editorController,
                shouldRefreshSceneGraph, dragSource.makeDropJobDescription());
        
        final List<FXOMObject> draggedObjects = dragSource.getDraggedObjects();
        final FXOMObject hitObject = dragSource.getHitObject();
        final FXOMObject currentParent = hitObject.getParentObject();
        final boolean reparenting = (currentParent != targetGridPane);
        final GridPane gridPane = (GridPane) targetGridPane.getSceneGraphObject();

        //  Steps:
        //
        //  1) snapshot grid related properties of dragged objects
        //      => this must be done here because they will be lost by #1
        //  2) clear the selection
        //  3) remove drag source objects from their current parent (if any)
        //  4) add new columns/rows in target grip pane as needed
        //  5) add drag source objects to this drop target
        //  6) restore grid related properties
        //  7) select the dragged objects
        //
        //  Note: if source and target parents are the same, skip #2,#3,#5,#7 and #8
                        
        // Step #1
        final GridSnapshot gridSnapshot;
        if ((currentParent != null) 
                && (currentParent.getSceneGraphObject() instanceof GridPane)) {
            gridSnapshot = new GridSnapshot(draggedObjects);
        } else {
            gridSnapshot = new GridSnapshot(draggedObjects, 1);
        }
            
        if (reparenting) {
            
            // Step #2
            result.addSubJob(new ClearSelectionJob(editorController));
            
            // Step #3
            if (currentParent != null) {
                for (FXOMObject draggedObject : draggedObjects) {
                    result.addSubJob(new RemoveObjectJob(draggedObject,
                            editorController));
                }
            }
        }
        
        // Step #4
        final GridBounds snapshotBounds = gridSnapshot.getBounds();
        final int hitColumnIndex = gridSnapshot.getColumnIndex(hitObject);
        final int hitRowIndex = gridSnapshot.getRowIndex(hitObject);
        final int destColumnIndex = (targetColumnArea == ColumnArea.RIGHT) ? targetColumnIndex+1 : targetColumnIndex;
        final int destRowIndex = (targetRowArea == RowArea.BOTTOM) ? targetRowIndex+1 : targetRowIndex;
        final int columnDelta = destColumnIndex - hitColumnIndex;
        final int rowDelta = destRowIndex - hitRowIndex;
        final GridBounds adjustedBounds = snapshotBounds.move(columnDelta, rowDelta);
        
        // Step #4.1 : columns
        switch(targetColumnArea) {
            case LEFT: 
            case RIGHT: { // Insert columns at destColumnIndex
                final int insertCount = snapshotBounds.getColumnSpan();
                result.addSubJob(new InsertColumnJob(targetGridPane, 
                        destColumnIndex, insertCount, editorController));
                break;
            }
            case CENTER: {// Insert columns at right (first) and left ends if needed
                final int targetColumnCount = Deprecation.getGridPaneColumnCount(gridPane);
                if (adjustedBounds.getMaxColumnIndex() > targetColumnCount) {
                    final int insertCount = adjustedBounds.getMaxColumnIndex() - targetColumnCount;
                    result.addSubJob(new InsertColumnJob(targetGridPane, 
                            targetColumnCount, insertCount, editorController));
                }
                if (adjustedBounds.getMinColumnIndex() < 0) {
                    final int insertCount = -adjustedBounds.getMinColumnIndex();
                    result.addSubJob(new InsertColumnJob(targetGridPane, 
                            0, insertCount, editorController));
                }
                break;
            }
        }
        
        // Step #4.2 : rows
        switch(targetRowArea) {
            case TOP: 
            case BOTTOM: { // Insert rows at destRowIndex
                final int insertCount = snapshotBounds.getRowSpan();
                result.addSubJob(new InsertRowJob(targetGridPane, 
                        destRowIndex, insertCount, editorController));
                break;
            }
            case CENTER: { // Insert rows at bottom (first) and top ends if needed
                final int targetRowCount = Deprecation.getGridPaneRowCount(gridPane);
                if (adjustedBounds.getMaxRowIndex() > targetRowCount) {
                    final int insertCount = adjustedBounds.getMaxRowIndex() - targetRowCount;
                    result.addSubJob(new InsertRowJob(targetGridPane, 
                            targetRowCount, insertCount, editorController));
                }
                if (adjustedBounds.getMinRowIndex() < 0) {
                    final int insertCount = -adjustedBounds.getMinRowIndex();
                    result.addSubJob(new InsertRowJob(targetGridPane, 
                            0, insertCount, editorController));
                }
                break;
            }
        }
        
        if (reparenting) {
            
            // Step #5
            for (FXOMObject draggedObject : draggedObjects) {
                final Job j = new InsertAsSubComponentJob(draggedObject, 
                        targetGridPane, targetIndex, editorController);
                result.addSubJob(j);
            }
        }
        
        // Step #6
        for (FXOMObject draggedObject : draggedObjects) {
            assert draggedObject instanceof FXOMInstance; // Because (1)
            result.addSubJob(new MoveCellContentJob((FXOMInstance) draggedObject,
                    columnDelta, rowDelta, editorController));
        }
        
        if (reparenting) {
        
            // Step #7
            result.addSubJob(new UpdateSelectionJob(draggedObjects, editorController));
        }
        
        assert result.isExecutable();
        
        return result;
    }
    
    @Override
    public boolean isSelectRequiredAfterDrop() {
        return true;
    }
    
}


<|start_filename|>app/src/main/java/com/oracle/javafx/scenebuilder/app/report/JarAnalysisReportController.java<|end_filename|>
/*
 * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.app.report;

import com.oracle.javafx.scenebuilder.app.i18n.I18N;
import com.oracle.javafx.scenebuilder.kit.editor.EditorController;
import com.oracle.javafx.scenebuilder.kit.editor.panel.util.AbstractFxmlWindowController;
import com.oracle.javafx.scenebuilder.kit.library.user.UserLibrary;
import com.oracle.javafx.scenebuilder.kit.library.util.JarReport;
import com.oracle.javafx.scenebuilder.kit.library.util.JarReportEntry;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import javafx.collections.ListChangeListener;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.Label;
import javafx.scene.input.Clipboard;
import javafx.scene.input.DataFormat;
import javafx.stage.Stage;
import javafx.stage.Window;
import java.io.PrintWriter;
import java.io.StringWriter;
import javafx.scene.Node;
import javafx.scene.text.Text;
import javafx.scene.text.TextFlow;
import javafx.stage.WindowEvent;

/**
 *
 */
public class JarAnalysisReportController extends AbstractFxmlWindowController {
    
    @FXML
    TextFlow textFlow;
    @FXML
    Label timestampLabel;

    @FXML
    void onCopyAction(ActionEvent event) {
        final Map<DataFormat, Object> content = new HashMap<>();
        StringBuilder sb = new StringBuilder();

        for (Node item : textFlow.getChildrenUnmodifiable()) {
            if (item instanceof Text) {
                sb.append(((Text)item).getText());
            }
        }
        
        content.put(DataFormat.PLAIN_TEXT, sb.toString());
        Clipboard.getSystemClipboard().setContent(content);
    }

    private final EditorController editorController;
    private final String TIMESTAMP_PATTERN = "h:mm a EEEEEEEEE d MMM. yyyy"; //NOI18N
    private final SimpleDateFormat TIMESTAMP_DATE_FORMAT = new SimpleDateFormat(TIMESTAMP_PATTERN);
    private int prefixCounter = 0;
    private boolean dirty = false;

    public JarAnalysisReportController(EditorController editorController, Stage owner) {
        super(JarAnalysisReportController.class.getResource("JarAnalysisReport.fxml"), I18N.getBundle(), owner); //NOI18N
        this.editorController = editorController;
    }

    @Override
    public void onCloseRequest(WindowEvent event) {
        getStage().close();
    }
    
    @Override
    public void openWindow() {
        super.openWindow();
        
        if (dirty) {
            update();
        }
    }
    
    @Override
    protected void controllerDidCreateStage() {
        // Setup window title
        getStage().setTitle(I18N.getString("jar.analysis.report.title"));
    }

    @Override
    protected void controllerDidLoadFxml() {
        assert textFlow != null;
        assert timestampLabel != null;
                
        UserLibrary lib = (UserLibrary)editorController.getLibrary();
        lib.getJarReports().addListener((ListChangeListener<JarReport>) change -> update());
        
        update();
    }
    
    private void update() {
        // No need to eat CPU if the skeleton window isn't opened
        if (getStage().isShowing()) {
            textFlow.getChildren().clear();
            
            updateTimeStampLabel();
            
            UserLibrary lib = (UserLibrary)editorController.getLibrary();
            
            for (JarReport report : lib.getJarReports()) {
                for (JarReportEntry entry : report.getEntries()) {
                    if (entry.getStatus() != JarReportEntry.Status.OK) {
                        if (entry.getKlass() != null && entry.getException() != null) {
                            // We use a Text instance for header and another one
                            // for full stack in order to style them separately
                            StringBuilder sb = new StringBuilder();
                            sb.append(getSectionPrefix()).append(I18N.getString("jar.analysis.exception"));
                            sb.append(" ").append(entry.getName()); //NOI18N
                            Text text = new Text();
                            text.setText(sb.toString());
                            text.getStyleClass().add("header"); //NOI18N
                            textFlow.getChildren().add(text);
                            
                            StringBuilder sb2 = new StringBuilder();
                            sb2.append(getFullStack(entry.getException()));
                            Text text2 = new Text();
                            text2.setText(sb2.toString());
                            text2.getStyleClass().add("body"); //NOI18N
                            textFlow.getChildren().add(text2);
                        }
                    } else if (! entry.isNode()) {
                        StringBuilder sb = new StringBuilder();
                        sb.append(getSectionPrefix()).append(I18N.getString("jar.analysis.not.node"));
                        sb.append(" ").append(entry.getName()); //NOI18N
                        Text text = new Text();
                        text.setText(sb.toString());
                        text.getStyleClass().add("header"); //NOI18N
                        textFlow.getChildren().add(text);
                    }
                }
            }

            dirty = false;
        } else {
            dirty = true;
        }
    }
    
    // The very first section must start on top, it is only for the next one we
    // need a separator.
    private String getSectionPrefix() {
        if (prefixCounter == 0) {
            prefixCounter++;
            return ""; //NOI18N
        } else {
            return "\n\n"; //NOI18N
        }
    }
    
    private StringBuilder getFullStack(Throwable t) {
        StringBuilder res = new StringBuilder("\n"); //NOI18N
        StringWriter writer = new StringWriter();
            t.printStackTrace(new PrintWriter(writer, true));
            res.append(writer.getBuffer().toString());
        return res;
    }
    
    private void updateTimeStampLabel() {
        UserLibrary lib = (UserLibrary)editorController.getLibrary();
        Date date = (Date)lib.getExplorationDate();
        String timestampValue = TIMESTAMP_DATE_FORMAT.format(date);
        timestampLabel.setText(I18N.getString("jar.analysis.report.timestamp", timestampValue));
    }
}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/job/togglegroup/ModifySelectionToggleGroupJob.java<|end_filename|>
/*
 * Copyright (c) 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */

package com.oracle.javafx.scenebuilder.kit.editor.job.togglegroup;

import com.oracle.javafx.scenebuilder.kit.editor.EditorController;
import com.oracle.javafx.scenebuilder.kit.i18n.I18N;
import com.oracle.javafx.scenebuilder.kit.editor.job.BatchDocumentJob;
import com.oracle.javafx.scenebuilder.kit.editor.job.Job;
import com.oracle.javafx.scenebuilder.kit.editor.selection.ObjectSelectionGroup;
import com.oracle.javafx.scenebuilder.kit.editor.selection.Selection;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMDocument;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMInstance;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMObject;
import java.util.ArrayList;
import java.util.List;
import javafx.scene.control.ToggleGroup;

/**
 *
 */
public class ModifySelectionToggleGroupJob extends BatchDocumentJob {

    private final String toggleGroupId;

    public ModifySelectionToggleGroupJob(String toggleGroupId, EditorController editorController) {
        super(editorController);
        
        assert editorController.getFxomDocument() != null;
        
        this.toggleGroupId = toggleGroupId;
    }
    
    
    /*
     * BatchSelectionJob
     */
    
    @Override
    protected List<Job> makeSubJobs() {
        final List<Job> result = new ArrayList<>();
        
        /*
         * Checks that toggleGroupId is:
         *  0) either null
         *  1) either an unused fx:id
         *  2) either the fx:id of an existing ToggleGroup instance
         */
        
        final boolean executable;
        if (toggleGroupId == null) {
            executable = true;
        } else {
            final FXOMDocument fxomDocument = getEditorController().getFxomDocument();
            final FXOMObject toggleGroupObject = fxomDocument.searchWithFxId(toggleGroupId);
            if (toggleGroupObject == null) {
                // Case #1
                executable = true;
            } else if (toggleGroupObject instanceof FXOMInstance) {
                // Case #2
                final FXOMInstance toggleGroupInstance = (FXOMInstance) toggleGroupObject;
                executable = toggleGroupInstance.getDeclaredClass() == ToggleGroup.class;
            } else {
                executable = false;
            }
        }
        
        /*
         * Creates some ModifyToggleGroupJob instances
         */
        if (executable) {
            final Selection selection = getEditorController().getSelection();
            if (selection.getGroup() instanceof ObjectSelectionGroup) {
                final ObjectSelectionGroup osg = (ObjectSelectionGroup) selection.getGroup();
                for (FXOMObject fxomObject : osg.getItems()) {
                    final Job subJob
                            = new ModifyToggleGroupJob(fxomObject, toggleGroupId, getEditorController());
                    if (subJob.isExecutable()) {
                        result.add(subJob);
                    }
                }
            }
        }
        
        return result;
    }

    @Override
    protected String makeDescription() {
        return I18N.getString("job.set.toggle.group");
    }
    
}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/util/eventnames/FindEventNamesUtil.java<|end_filename|>
package com.oracle.javafx.scenebuilder.kit.util.eventnames;

import java.util.Map;
import java.util.Optional;
import java.util.TreeMap;

/**
 * Utility which offers a map with pairs of even type names and corresponding event names.
 * Event names can be searched for.
 */
public class FindEventNamesUtil {

    private static Map<String, String> events = new TreeMap<String, String>();

    // should be used in a static way
    private FindEventNamesUtil() {
    }

    /**
     * Initializes the internal map by loading it with data.
     */
    public static void initializeEventsMap() {
        loadEventsInMap();
    }

    /**
     * Gets the event name to the given event type name. If no event name was found, a default value (ActionEvent) is returned.
     *
     * @param eventType event type name
     * @return event name
     */
    public static String findEventName(String eventType) {
        return Optional.ofNullable(events.get(eventType)).orElse(EventNames.ACTION_EVENT);
    }

    /**
     * Loads the map with different kinds of event names and event type names.
     */
    private static void loadEventsInMap() {
        loadMainEvents();
        loadDragDropEvents();
        loadKeyboardEvents();
        loadMouseEvents();
        loadRotationEvents();
        loadSwipeEvents();
        loadTouchEvents();
        loadZoomEvents();
    }

    private static void loadMainEvents() {
        events.put(EventTypeNames.ON_ACTION, EventNames.ACTION_EVENT);
    }

    private static void loadDragDropEvents() {
        events.put(EventTypeNames.ON_DRAG_DETECTED, EventNames.MOUSE_EVENT);
        events.put(EventTypeNames.ON_DRAG_DONE, EventNames.DRAG_EVENT);
        events.put(EventTypeNames.ON_DRAG_DROPPED, EventNames.DRAG_EVENT);
        events.put(EventTypeNames.ON_DRAG_ENTERED, EventNames.DRAG_EVENT);
        events.put(EventTypeNames.ON_DRAG_EXITED, EventNames.DRAG_EVENT);
        events.put(EventTypeNames.ON_DRAG_OVER, EventNames.DRAG_EVENT);
        events.put(EventTypeNames.ON_MOUSE_DRAG_ENTERED, EventNames.MOUSE_DRAG_EVENT);
        events.put(EventTypeNames.ON_MOUSE_DRAG_EXITED, EventNames.MOUSE_DRAG_EVENT);
        events.put(EventTypeNames.ON_MOUSE_DRAG_OVER, EventNames.MOUSE_DRAG_EVENT);
        events.put(EventTypeNames.ON_MOUSE_DRAG_RELEASED, EventNames.MOUSE_DRAG_EVENT);
    }

    private static void loadKeyboardEvents() {
        events.put(EventTypeNames.ON_INPUT_METHOD_TEXT_CHANGED, EventNames.INPUT_METHOD_EVENT);
        events.put(EventTypeNames.ON_KEY_PRESSED, EventNames.KEY_EVENT);
        events.put(EventTypeNames.ON_KEY_RELEASED, EventNames.KEY_EVENT);
        events.put(EventTypeNames.ON_KEY_TYPED, EventNames.KEY_EVENT);
    }

    private static void loadMouseEvents() {
        events.put(EventTypeNames.ON_CONTEXT_MENU_REQUESTED, EventNames.CONTEXT_MENU_EVENT);
        events.put(EventTypeNames.ON_MOUSE_CLICKED, EventNames.MOUSE_EVENT);
        events.put(EventTypeNames.ON_MOUSE_DRAGGED, EventNames.MOUSE_EVENT);
        events.put(EventTypeNames.ON_MOUSE_ENTERED, EventNames.MOUSE_EVENT);
        events.put(EventTypeNames.ON_MOUSE_EXITED, EventNames.MOUSE_EVENT);
        events.put(EventTypeNames.ON_MOUSE_MOVED, EventNames.MOUSE_EVENT);
        events.put(EventTypeNames.ON_MOUSE_PRESSED, EventNames.MOUSE_EVENT);
        events.put(EventTypeNames.ON_MOUSE_RELEASED, EventNames.MOUSE_EVENT);
        events.put(EventTypeNames.ON_SCROLL, EventNames.SCROLL_EVENT);
        events.put(EventTypeNames.ON_SCROLL_STARTED, EventNames.SCROLL_EVENT);
        events.put(EventTypeNames.ON_SCROLL_FINISHED, EventNames.SCROLL_EVENT);
    }

    private static void loadRotationEvents() {
        events.put(EventTypeNames.ON_ROTATE, EventNames.ROTATE_EVENT);
        events.put(EventTypeNames.ON_ROTATION_FINISHED, EventNames.ROTATE_EVENT);
        events.put(EventTypeNames.ON_ROTATION_STARTED, EventNames.ROTATE_EVENT);
    }

    private static void loadSwipeEvents() {
        events.put(EventTypeNames.ON_SWIPE_LEFT, EventNames.SWIPE_EVENT);
        events.put(EventTypeNames.ON_SWIPE_RIGHT, EventNames.SWIPE_EVENT);
        events.put(EventTypeNames.ON_SWIPE_UP, EventNames.SWIPE_EVENT);
        events.put(EventTypeNames.ON_SWIPE_DOWN, EventNames.SWIPE_EVENT);
    }

    private static void loadTouchEvents() {
        events.put(EventTypeNames.ON_TOUCH_MOVED, EventNames.TOUCH_EVENT);
        events.put(EventTypeNames.ON_TOUCH_PRESSED, EventNames.TOUCH_EVENT);
        events.put(EventTypeNames.ON_TOUCH_RELEASED, EventNames.TOUCH_EVENT);
        events.put(EventTypeNames.ON_TOUCH_STATIONARY, EventNames.TOUCH_EVENT);
    }

    private static void loadZoomEvents() {
        events.put(EventTypeNames.ON_ZOOM, EventNames.ZOOM_EVENT);
        events.put(EventTypeNames.ON_ZOOM_STARTED, EventNames.ZOOM_EVENT);
        events.put(EventTypeNames.ON_ZOOM_FINISHED, EventNames.ZOOM_EVENT);
    }
}


<|start_filename|>.github/scripts/jpackage.bat<|end_filename|>
for /F %%i in ('%JAVA_HOME%\bin\jdeps --module-path %JAVAFX_HOME% --print-module-deps --ignore-missing-deps %GITHUB_WORKSPACE%/app/target/lib/scenebuilder-%VERSION%-all.jar') do SET JDEPS_MODULES=%%i

REM jdeps doesn't include JavaFX modules in Windows
set JAVAFX_MODULES=javafx.fxml,javafx.media,javafx.swing,javafx.web
REM set MODULES=java.desktop,java.logging,java.naming,java.prefs,java.security.jgss,java.sql,java.xml,javafx.fxml,javafx.media,javafx.swing,javafx.web,jdk.unsupported

%JAVA_HOME%\bin\jlink ^
--module-path %JAVAFX_HOME% ^
--add-modules %JDEPS_MODULES%,%JAVAFX_MODULES% ^
--output app/target/runtime ^
--strip-debug --compress 2 --no-header-files --no-man-pages

%JPACKAGE_HOME%\bin\jpackage ^
--app-version %VERSION% ^
--input app/target/lib ^
--license-file LICENSE.txt ^
--main-jar scenebuilder-%VERSION%-all.jar ^
--main-class %MAIN_CLASS% ^
--name SceneBuilder ^
--description "Scene Builder" ^
--vendor Gluon ^
--verbose ^
--runtime-image app/target/runtime ^
--dest %INSTALL_DIR% ^
--type msi ^
--java-options "--add-opens=javafx.fxml/javafx.fxml=ALL-UNNAMED" ^
--java-options "-Djava.library.path=runtime\bin;runtime\lib" ^
--icon app/assets/windows/icon-windows.ico ^
--win-dir-chooser ^
--win-menu ^
--win-menu-group "Scene Builder" ^
--win-per-user-install ^
--win-shortcut

<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/content/driver/TabDriver.java<|end_filename|>
/*
 * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.kit.editor.panel.content.driver;

import com.oracle.javafx.scenebuilder.kit.editor.drag.target.AbstractDropTarget;
import com.oracle.javafx.scenebuilder.kit.editor.drag.target.AccessoryDropTarget;
import com.oracle.javafx.scenebuilder.kit.editor.panel.content.ContentPanelController;
import com.oracle.javafx.scenebuilder.kit.editor.panel.content.driver.curve.AbstractCurveEditor;
import com.oracle.javafx.scenebuilder.kit.editor.panel.content.driver.handles.AbstractHandles;
import com.oracle.javafx.scenebuilder.kit.editor.panel.content.driver.handles.TabHandles;
import com.oracle.javafx.scenebuilder.kit.editor.panel.content.driver.pring.AbstractPring;
import com.oracle.javafx.scenebuilder.kit.editor.panel.content.driver.pring.TabPring;
import com.oracle.javafx.scenebuilder.kit.editor.panel.content.driver.resizer.AbstractResizer;
import com.oracle.javafx.scenebuilder.kit.editor.panel.content.driver.tring.AbstractTring;
import com.oracle.javafx.scenebuilder.kit.editor.panel.content.driver.tring.TabTring;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMInstance;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMObject;
import com.oracle.javafx.scenebuilder.kit.metadata.util.DesignHierarchyMask.Accessory;
import javafx.geometry.Bounds;
import javafx.scene.Node;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;

/**
 *
 */
public class TabDriver extends AbstractDriver {

    public TabDriver(ContentPanelController contentPanelController) {
        super(contentPanelController);
    }

    /*
     * AbstractDriver
     */
    
    @Override
    public AbstractHandles<?> makeHandles(FXOMObject fxomObject) {
        assert fxomObject.getSceneGraphObject() instanceof Tab;
        assert fxomObject instanceof FXOMInstance;
        return new TabHandles(contentPanelController, (FXOMInstance) fxomObject);
    }
    
    @Override
    public AbstractTring<?> makeTring(AbstractDropTarget dropTarget) {
        assert dropTarget != null;
        assert dropTarget.getTargetObject() instanceof FXOMInstance;
        assert dropTarget.getTargetObject().getSceneGraphObject() instanceof Tab;
        return new TabTring(contentPanelController, (FXOMInstance) dropTarget.getTargetObject());
    }

    @Override
    public AbstractPring<?> makePring(FXOMObject fxomObject) {
        assert fxomObject.getSceneGraphObject() instanceof Tab;
        assert fxomObject instanceof FXOMInstance;
        return new TabPring(contentPanelController, (FXOMInstance) fxomObject);
    }
    
    @Override
    public AbstractResizer<?> makeResizer(FXOMObject fxomObject) {
        // Resize gesture does not apply to Tab objects
        return null;
    }

    @Override
    public AbstractCurveEditor<?> makeCurveEditor(FXOMObject fxomObject) {
        return null;
    }
    
    @Override
    public FXOMObject refinePick(Node hitNode, double sceneX, double sceneY, FXOMObject fxomObject) {
        return fxomObject;
    }

    @Override
    public AbstractDropTarget makeDropTarget(FXOMObject fxomObject, double sceneX, double sceneY) {
        assert fxomObject instanceof FXOMInstance;
        return new AccessoryDropTarget((FXOMInstance) fxomObject, Accessory.CONTENT);
    }

    @Override
    public Node getInlineEditorBounds(FXOMObject fxomObject) {
        assert fxomObject.getSceneGraphObject() instanceof Tab;
        final Tab tab = (Tab) fxomObject.getSceneGraphObject();
        final TabPaneDesignInfoX di = new TabPaneDesignInfoX();
        return di.getTabNode(tab.getTabPane(), tab);
    }

    @Override
    public boolean intersectsBounds(FXOMObject fxomObject, Bounds bounds) {
        assert fxomObject.getSceneGraphObject() instanceof Tab;
        
        final Tab tab = (Tab) fxomObject.getSceneGraphObject();
        final boolean result;
        if (tab.isSelected()) {
            final TabPane tabPane
                    = tab.getTabPane();
            final Bounds sceneGraphNodeBounds 
                    = tabPane.localToScene(tabPane.getLayoutBounds(), true /* rootScene */);
            result = sceneGraphNodeBounds.intersects(bounds);
        } else {
            result = false;
        }
        
        return result;
    }
    
}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/library/maven/repository/RepositoryManagerController.java<|end_filename|>
/*
 * Copyright (c) 2016, Gluon and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation and Gluon nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.kit.editor.panel.library.maven.repository;

import com.oracle.javafx.scenebuilder.kit.editor.EditorController;
import com.oracle.javafx.scenebuilder.kit.i18n.I18N;
import com.oracle.javafx.scenebuilder.kit.editor.panel.library.LibraryPanelController;
import com.oracle.javafx.scenebuilder.kit.editor.panel.library.maven.preset.MavenPresets;
import com.oracle.javafx.scenebuilder.kit.editor.panel.library.maven.repository.dialog.RepositoryDialogController;
import com.oracle.javafx.scenebuilder.kit.editor.panel.util.AbstractFxmlWindowController;
import com.oracle.javafx.scenebuilder.kit.preferences.PreferencesControllerBase;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.scene.control.ListView;
import javafx.stage.Stage;
import javafx.stage.Window;
import javafx.stage.WindowEvent;

import java.util.stream.Collectors;
import javafx.beans.InvalidationListener;
import javafx.beans.Observable;
import javafx.stage.Modality;

/**
 * Controller for the JAR/FXML Library dialog.
 */
public class RepositoryManagerController extends AbstractFxmlWindowController {

    @FXML
    private ListView<RepositoryListItem> repositoryListView;

    private final EditorController editorController;
    private final Stage owner;

    private ObservableList<RepositoryListItem> listItems;

    private final String userM2Repository;
    private final String tempM2Repository;
    private final PreferencesControllerBase preferencesControllerBase;
    
    public RepositoryManagerController(EditorController editorController, String userM2Repository,
                                       String tempM2Repository, PreferencesControllerBase preferencesControllerBase,
                                       Stage owner) {
        super(LibraryPanelController.class.getResource("RepositoryManager.fxml"), I18N.getBundle(), owner); //NOI18N
        this.owner = owner;
        this.editorController = editorController;
        this.userM2Repository = userM2Repository;
        this.tempM2Repository = tempM2Repository;
        this.preferencesControllerBase = preferencesControllerBase;
    }

    @Override
    protected void controllerDidCreateStage() {
        if (this.owner == null) {
            // Dialog will be appliation modal
            getStage().initModality(Modality.APPLICATION_MODAL);
        } else {
            // Dialog will be window modal
            getStage().initOwner(this.owner);
            getStage().initModality(Modality.WINDOW_MODAL);
        }
    }
    
    @Override
    public void onCloseRequest(WindowEvent event) {
        close();
    }

    @Override
    public void openWindow() {
        super.openWindow();
        super.getStage().setTitle(I18N.getString("repository.manager.title"));
        loadRepositoryList();
    }

    private void loadRepositoryList() {
        if (listItems == null) {
            listItems = FXCollections.observableArrayList();
        }
        listItems.clear();
        repositoryListView.setItems(listItems);
        repositoryListView.setCellFactory(param -> new RepositoryManagerListCell());
        
        // custom repositories
        listItems.addAll(preferencesControllerBase.getRepositoryPreferences().getRepositories()
                .stream()
                .map(r -> new CustomRepositoryListItem(this, r))
                .collect(Collectors.toList()));
        
        // preset on top
        listItems.addAll(0, MavenPresets.getPresetRepositories()
            .stream()
            .map(r -> new RepositoryListItem(this, r))
            .collect(Collectors.toList()));
    }

    @FXML
    private void close() {
        repositoryListView.getItems().clear();
        closeWindow();
    }

    @FXML
    private void addRepository() {
        repositoryDialog(null);
    }

    private void repositoryDialog(Repository repository) {
        RepositoryDialogController repositoryDialogController = new RepositoryDialogController(editorController,
                userM2Repository, tempM2Repository, preferencesControllerBase, getStage());
        repositoryDialogController.openWindow();
        repositoryDialogController.setRepository(repository);
        repositoryDialogController.getStage().showingProperty().addListener(new InvalidationListener() {
            @Override
            public void invalidated(Observable observable) {
                if (!repositoryDialogController.getStage().isShowing()) {
                    loadRepositoryList();
                    repositoryDialogController.getStage().showingProperty().removeListener(this);
                }
            }
        });
    }
    
    public void edit(RepositoryListItem item) {
        repositoryDialog(item.getRepository());
    }
    
    public void delete(RepositoryListItem item) {
        // Remove repository
        logInfoMessage("log.user.repository.removed", item.getRepository().getId());
        preferencesControllerBase.removeRepository(item.getRepository().getId());
        loadRepositoryList();
    }
    
    private void logInfoMessage(String key, Object... args) {
        editorController.getMessageLog().logInfoMessage(key, I18N.getBundle(), args);
    }
    
}

<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/util/control/effectpicker/SingleInputPathItem.java<|end_filename|>
/*
 * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.kit.util.control.effectpicker;

import javafx.scene.effect.Bloom;
import javafx.scene.effect.BoxBlur;
import javafx.scene.effect.ColorAdjust;
import javafx.scene.effect.DisplacementMap;
import javafx.scene.effect.DropShadow;
import javafx.scene.effect.Effect;
import javafx.scene.effect.GaussianBlur;
import javafx.scene.effect.Glow;
import javafx.scene.effect.InnerShadow;
import javafx.scene.effect.MotionBlur;
import javafx.scene.effect.PerspectiveTransform;
import javafx.scene.effect.Reflection;
import javafx.scene.effect.SepiaTone;
import javafx.scene.effect.Shadow;

/**
 * Effect path item for effects defining single input.
 */
public class SingleInputPathItem extends EffectPathItem {

    private EffectPathItem inputPathItem;

    public SingleInputPathItem(EffectPickerController epc, Effect effect, EffectPathItem hostPathItem) {
        super(epc, effect, hostPathItem);
        assert effect instanceof Bloom
                || effect instanceof BoxBlur
                || effect instanceof ColorAdjust
                || effect instanceof DisplacementMap
                || effect instanceof DropShadow
                || effect instanceof GaussianBlur
                || effect instanceof Glow
                || effect instanceof InnerShadow
                || effect instanceof MotionBlur
                || effect instanceof PerspectiveTransform
                || effect instanceof Reflection
                || effect instanceof SepiaTone
                || effect instanceof Shadow;
    }

    @Override
    EffectPathItem getSelectedInputPathItem() {
        return inputPathItem;
    }

    void setInputPathItem(EffectPathItem epi) {
        inputPathItem = epi;
    }

    @Override
    void setSelectedInputEffect(Effect input) {
        if (effect instanceof Bloom) {
            ((Bloom) effect).setInput(input);
        } else if (effect instanceof BoxBlur) {
            ((BoxBlur) effect).setInput(input);
        } else if (effect instanceof ColorAdjust) {
            ((ColorAdjust) effect).setInput(input);
        } else if (effect instanceof DisplacementMap) {
            ((DisplacementMap) effect).setInput(input);
        } else if (effect instanceof DropShadow) {
            ((DropShadow) effect).setInput(input);
        } else if (effect instanceof GaussianBlur) {
            ((GaussianBlur) effect).setInput(input);
        } else if (effect instanceof Glow) {
            ((Glow) effect).setInput(input);
        } else if (effect instanceof InnerShadow) {
            ((InnerShadow) effect).setInput(input);
        } else if (effect instanceof MotionBlur) {
            ((MotionBlur) effect).setInput(input);
        } else if (effect instanceof PerspectiveTransform) {
            ((PerspectiveTransform) effect).setInput(input);
        } else if (effect instanceof Reflection) {
            ((Reflection) effect).setInput(input);
        } else if (effect instanceof SepiaTone) {
            ((SepiaTone) effect).setInput(input);
        } else {
            assert effect instanceof Shadow;
            ((Shadow) effect).setInput(input);
        }
    }

    Effect getInput() {
        if (effect instanceof Bloom) {
            return ((Bloom) effect).getInput();
        } else if (effect instanceof BoxBlur) {
            return ((BoxBlur) effect).getInput();
        } else if (effect instanceof ColorAdjust) {
            return ((ColorAdjust) effect).getInput();
        } else if (effect instanceof DisplacementMap) {
            return ((DisplacementMap) effect).getInput();
        } else if (effect instanceof DropShadow) {
            return ((DropShadow) effect).getInput();
        } else if (effect instanceof GaussianBlur) {
            return ((GaussianBlur) effect).getInput();
        } else if (effect instanceof Glow) {
            return ((Glow) effect).getInput();
        } else if (effect instanceof InnerShadow) {
            return ((InnerShadow) effect).getInput();
        } else if (effect instanceof MotionBlur) {
            return ((MotionBlur) effect).getInput();
        } else if (effect instanceof PerspectiveTransform) {
            return ((PerspectiveTransform) effect).getInput();
        } else if (effect instanceof Reflection) {
            return ((Reflection) effect).getInput();
        } else if (effect instanceof SepiaTone) {
            return ((SepiaTone) effect).getInput();
        } else {
            assert effect instanceof Shadow;
            return ((Shadow) effect).getInput();
        }
    }
}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/skeleton/SkeletonFileWriter.java<|end_filename|>
/*
 * Copyright (c) 2021, Gluon and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation and Gluon nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.kit.skeleton;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.OpenOption;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.Collections;
import java.util.EnumMap;
import java.util.Map;
import java.util.Objects;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.logging.Level;
import java.util.logging.Logger;

import javafx.beans.property.ReadOnlyStringProperty;
import javafx.stage.FileChooser;
import javafx.stage.FileChooser.ExtensionFilter;
import javafx.stage.Stage;

final class SkeletonFileWriter {

    private final Supplier<Stage> stageSupplier;

    private final Map<SkeletonSettings.LANGUAGE, File> savedFilePerLanguage;
    private final Map<SkeletonSettings.LANGUAGE, ExtensionFilter> extensionFilterByLanguage;

    private final BiFunction<FileChooser,Stage,File> saveDialogInteraction;

    private final Function<Supplier<Stage>,Consumer<File>> onSuccess;
    private final Function<Supplier<Stage>,BiConsumer<File, Exception>> onError;

    private SkeletonSettings.LANGUAGE language;
    private URL fxmlLocation;
    private String controllerName;
    private FileChooser saveFileChooser;
    private ReadOnlyStringProperty textProperty;

    /**
     * Provides the option to save a controller skeleton to a file considering
     * language specifics.
     * 
     * @param stageSupplier As a file chooser and alerts are used, the reference to
     *                      the parent stage is required. Must not be null.
     * @param textProperty  The contents of the file is read from this property.
     *                      Must not be null.
     * 
     */
    public SkeletonFileWriter(Supplier<Stage> stageSupplier, ReadOnlyStringProperty textProperty) {
        this(stageSupplier, textProperty, (fileChooser,stage) -> fileChooser.showSaveDialog(stage),
                SkeletonFileWriterSuccessAlert::new, SkeletonFileWriterErrorAlert::new);
    }

    /**
     * This constructor only exists for the purpose of testing. It allows to replace
     * the way how the file chooser interaction is handled. Also it allows to replace
     * the GUI interaction using alerts with custom logic for testing.
     * 
     * @param stageSupplier         As a file chooser and alerts are used, the
     *                              reference to the parent stage is required. Must
     *                              not be null.
     * @param textProperty          The contents of the file is read from this
     *                              property. Must not be null.
     * @param saveDialogInteraction Allows to replace the dialog interaction with
     *                              the file chooser with custom functionality (e.g.
     *                              for testing).
     * @param onSuccessNotify       This parameter provides the notification which
     *                              is raised in case of successfully writing the
     *                              skeleton file.
     * @param onErrorNotify         This parameter provides the notification which
     *                              is raised in case of an exception during an
     *                              attempt to write the skeleton file.
     */
    protected SkeletonFileWriter(Supplier<Stage> stageSupplier, ReadOnlyStringProperty textProperty,
                                 BiFunction<FileChooser,Stage,File> saveDialogInteraction,
                                 Function<Supplier<Stage>,Consumer<File>> onSuccessNotify,
                                 Function<Supplier<Stage>,BiConsumer<File, Exception>> onErrorNotify) {
        this.stageSupplier = Objects.requireNonNull(stageSupplier);
        this.textProperty = Objects.requireNonNull(textProperty);
        this.saveDialogInteraction = Objects.requireNonNull(saveDialogInteraction);
        this.onSuccess = Objects.requireNonNull(onSuccessNotify);
        this.onError   = Objects.requireNonNull(onErrorNotify);
        this.savedFilePerLanguage = new EnumMap<>(SkeletonSettings.LANGUAGE.class);
        this.extensionFilterByLanguage = new EnumMap<>(SkeletonSettings.LANGUAGE.class);
    }


    /**
     * Prepares a language specific file name proposal for a controller skeleton
     * file. Then, a file chooser is shown to allow adjustments to the proposed file
     * name. Finally, the contents of the provided textProperty is saved to the
     * file. In case of success, a message is shown - same applies for the error
     * case.
     * 
     * The file name proposal is based on values of fxmlLocation and fxControllerName.
     * Once a filename has been defined, either by accepting the proposal or by 
     * manually overriding it in the FileChooser, the filename will remain the one
     * provided by the FileChooser. The accepted file name is remembered for each
     * available language.
     * 
     * To create a new file name proposal, the FXML file must be closed and re-opened in
     * SceneBuilder.
     * 
     * Please refer to {@link SkeletonFileNameProposal} for details.
     * 
     * 
     * @param fxmlLocation     The location of the FXML file in works. In case the
     *                         file has not been saved, this location will be null.
     * @param fxControllerName This is usually the name of the controller as defined
     *                         in the FXML file. If no controller name has been
     *                         given, this value might be null
     * @param language         The language is required in order to adjust file naming
     *                         according to language specific rules. Must not be null.
     */
    public void run(URL fxmlLocation, String fxControllerName, SkeletonSettings.LANGUAGE language) {
        this.fxmlLocation = fxmlLocation;
        this.controllerName = fxControllerName;
        this.language = Objects.requireNonNull(language);
        createFileChooserWhenNeeded();
        /*
         * TODO: Ask user if a corresponding directory src/main/java or 
         * src/main/kotlin shall be created if it does not exist.
         */
        File fileToSave = determineSaveFileName();
        updateFileChooser(fileToSave);
        saveToFileWhenConfirmed();
    }

    /**
     * This method allows to verify which files have been previously saved per language.
     *  
     * @return The map of the previously saved file per language.
     */
    Map<SkeletonSettings.LANGUAGE,File> getLastSavedFilesPerLanguage() {
        return Collections.unmodifiableMap(savedFilePerLanguage);
    }

    /**
     * @return the defined notification function for the success case.
     */
    Function<Supplier<Stage>, Consumer<File>> getOnSuccess() {
        return onSuccess;
    }

    /**
     * @return the effectively defined error handling function. 
     */
    Function<Supplier<Stage>, BiConsumer<File, Exception>> getOnError() {
        return onError;
    }

    private void saveToFileWhenConfirmed() {
        File confirmedSavedFile = saveDialogInteraction.apply(saveFileChooser, stageSupplier.get());
        if (null != confirmedSavedFile) {
            updateFileChooserAndSave(confirmedSavedFile);
        }
    }

    private File determineSaveFileName() {
        File fileToSave = savedFilePerLanguage.get(language);
        if (fileToSave == null) {
            fileToSave = new SkeletonFileNameProposal(language).create(fxmlLocation, controllerName);
        }
        return fileToSave;
    }

    private void createFileChooserWhenNeeded() {
        if (saveFileChooser == null) {
            saveFileChooser = new FileChooser(); 
            createExtensionFilters();
        }
    }

    private void createExtensionFilters() {
        for (SkeletonSettings.LANGUAGE lang : SkeletonSettings.LANGUAGE.values()) {
            ExtensionFilter filter = new ExtensionFilter(lang.toString(), "*"+lang.getExtension());
            extensionFilterByLanguage.put(lang, filter);
            saveFileChooser.getExtensionFilters().add(filter);
        }
    }

    private void updateFileChooser(File fileToSave) {
        saveFileChooser.setInitialDirectory(fileToSave.getParentFile());
        saveFileChooser.setInitialFileName(fileToSave.getName());
        saveFileChooser.setSelectedExtensionFilter(extensionFilterByLanguage.get(language));
    }

    private void updateFileChooserAndSave(File savedFile) {
        updateFileChooser(savedFile);
        rememberLastSavedFilePerLanguage(savedFile);
        saveToFile(savedFile.toPath().toAbsolutePath());
    }

    private void rememberLastSavedFilePerLanguage(File savedFile) {
        savedFilePerLanguage.put(language, savedFile);
    }

    private void saveToFile(Path skeletonFile) {
        try {
            writeSkeletonFileAndNotifyUser(skeletonFile);
        } catch (IOException error) {
            logErrorAndNotifyUser(skeletonFile, error);
        }
    }

    private void writeSkeletonFileAndNotifyUser(Path skeletonFile) throws IOException {
        String skeleton = textProperty.getValueSafe();
        OpenOption openOption = Files.exists(skeletonFile) ? StandardOpenOption.TRUNCATE_EXISTING : StandardOpenOption.CREATE_NEW;
        Files.write(skeletonFile, skeleton.getBytes(), openOption);
        onSuccess.apply(stageSupplier)
                 .accept(skeletonFile.toFile());
    }

    private void logErrorAndNotifyUser(Path skeletonFile, IOException error) {
        onError.apply(stageSupplier)
               .accept(skeletonFile.toFile(),error);
        Logger logger = Logger.getLogger(SkeletonFileWriter.class.getSimpleName());
        String template = "Could not write controller skeleton to file: %s .";
        logger.log(Level.SEVERE, String.format(template, skeletonFile.normalize().toAbsolutePath()), error);
    }
}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/library/maven/repository/dialog/RepositoryDialogController.java<|end_filename|>
/*
 * Copyright (c) 2016, Gluon and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation and Gluon nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.kit.editor.panel.library.maven.repository.dialog;

import com.oracle.javafx.scenebuilder.kit.preferences.PreferencesRecordRepository;
import com.oracle.javafx.scenebuilder.kit.editor.EditorController;
import com.oracle.javafx.scenebuilder.kit.i18n.I18N;
import com.oracle.javafx.scenebuilder.kit.editor.panel.library.LibraryPanelController;
import com.oracle.javafx.scenebuilder.kit.editor.panel.library.maven.MavenRepositorySystem;
import com.oracle.javafx.scenebuilder.kit.editor.panel.library.maven.repository.Repository;
import com.oracle.javafx.scenebuilder.kit.editor.panel.util.AbstractFxmlWindowController;
import com.oracle.javafx.scenebuilder.kit.preferences.PreferencesControllerBase;
import javafx.concurrent.Service;
import javafx.concurrent.Task;
import javafx.concurrent.Worker;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
import javafx.scene.control.Label;
import javafx.scene.control.PasswordField;
import javafx.scene.control.ProgressIndicator;
import javafx.scene.control.TextField;
import javafx.scene.control.Tooltip;
import javafx.scene.layout.AnchorPane;
import javafx.stage.Modality;
import javafx.stage.Stage;
import javafx.stage.Window;
import javafx.stage.WindowEvent;

public class RepositoryDialogController extends AbstractFxmlWindowController {
    
    @FXML
    private AnchorPane MavenDialog;

    @FXML
    private TextField nameIDTextfield;

    @FXML
    private TextField typeTextfield;

    @FXML
    private Button searchButton;

    @FXML
    private ProgressIndicator progress;

    @FXML
    private Button cancelButton;

    @FXML
    private Button addButton;

    @FXML
    private TextField urlTextfield;

    @FXML
    private CheckBox privateCheckBox;
    
    @FXML
    private Label userLabel;
    
    @FXML
    private TextField userTextfield;

    @FXML
    private Label passwordLabel;
    
    @FXML
    private PasswordField passwordTextfield;
    
    @FXML
    private Button testButton;
    
    @FXML
    private Label resultLabel;
//    
    private final Window owner;
    private final EditorController editorController;
    private MavenRepositorySystem maven;
    private Repository oldRepository;
    private final Service<String> testService;

    private final String userM2Repository;
    private final String tempM2Repository;
    private final PreferencesControllerBase preferencesControllerBase;
    
    public RepositoryDialogController(EditorController editorController, String userM2Repository,
                                      String tempM2Repository, PreferencesControllerBase preferencesControllerBase,
                                      Stage owner) {
        super(LibraryPanelController.class.getResource("RepositoryDialog.fxml"), I18N.getBundle(), owner); //NOI18N
        this.owner = owner;
        this.editorController = editorController;
        this.userM2Repository = userM2Repository;
        this.tempM2Repository = tempM2Repository;
        this.preferencesControllerBase = preferencesControllerBase;
        
        testService = new Service<String>() {
            @Override
            protected Task<String> createTask() {
                return new Task<String>() {
                    @Override
                    protected String call() throws Exception {
                        return maven.validateRepository(new Repository(nameIDTextfield.getText(), 
                            typeTextfield.getText(), urlTextfield.getText(),
                            userTextfield.getText(), passwordTextfield.getText()));
                    }
                };
            }
        };
        
        testService.stateProperty().addListener((obs, ov, nv) -> {
            if (nv.equals(Worker.State.SUCCEEDED)) {
                String result = testService.getValue();
                if (result.isEmpty()) {
                    if (resultLabel.getStyleClass().contains("label-error")) {
                        resultLabel.getStyleClass().remove("label-error");
                    }
                    result = I18N.getString("repository.dialog.result");
                } else {
                    if (!resultLabel.getStyleClass().contains("label-error")) {
                        resultLabel.getStyleClass().add("label-error");
                    }
                }
                resultLabel.setText(result);
                resultLabel.setTooltip(new Tooltip(result));
            }
        });
    }

    @Override
    protected void controllerDidCreateStage() {
        if (this.owner == null) {
            // Dialog will be appliation modal
            getStage().initModality(Modality.APPLICATION_MODAL);
        } else {
            // Dialog will be window modal
            getStage().initOwner(this.owner);
            getStage().initModality(Modality.WINDOW_MODAL);
        }
    }       
    
    @FXML
    void cancel() {
        closeWindow();
        
        nameIDTextfield.clear();
        typeTextfield.clear();
        urlTextfield.clear();
        userTextfield.clear();
        passwordTextfield.clear();
        privateCheckBox.setSelected(false);
        resultLabel.setText("");
    }

    @FXML
    void addRepository() {
        Repository repository;
        if (privateCheckBox.isSelected()) {
            repository = new Repository(nameIDTextfield.getText(), 
                typeTextfield.getText(), urlTextfield.getText(), 
                userTextfield.getText(), passwordTextfield.getText());
        } else {
            repository = new Repository(nameIDTextfield.getText(), 
                typeTextfield.getText(), urlTextfield.getText());
        }
        if (oldRepository != null) {
            preferencesControllerBase.removeRepository(oldRepository.getId());
        }
        updatePreferences(repository);
        logInfoMessage(oldRepository == null ? "log.user.repository.added" :
                "log.user.repository.updated", repository.getId());
        cancel();
    }
    
    @FXML
    void test() {
        testService.restart();
    }

    @Override
    public void onCloseRequest(WindowEvent event) {
        cancel();
    }

    @Override
    public void openWindow() {
        super.openWindow();
        
        userLabel.disableProperty().bind(privateCheckBox.selectedProperty().not());
        userTextfield.disableProperty().bind(privateCheckBox.selectedProperty().not());
        passwordLabel.disableProperty().bind(privateCheckBox.selectedProperty().not());
        passwordTextfield.disableProperty().bind(privateCheckBox.selectedProperty().not());
        progress.visibleProperty().bind(testService.runningProperty());
        resultLabel.setText("");
        maven = new MavenRepositorySystem(false, userM2Repository, tempM2Repository,
                preferencesControllerBase.getRepositoryPreferences());
    }
    
    private void logInfoMessage(String key, Object... args) {
        editorController.getMessageLog().logInfoMessage(key, I18N.getBundle(), args);
    }
    
    private void updatePreferences(Repository repository) {
        if (repository == null) {
            return;
        }
        
        // Update record repository
        final PreferencesRecordRepository recordRepository = preferencesControllerBase.
                getRecordRepository(repository);
        recordRepository.writeToJavaPreferences();
        
    }

    public void setRepository(Repository repository) {
        oldRepository = repository;
        if (repository == null) {
            super.getStage().setTitle(I18N.getString("repository.dialog.title.add"));
            addButton.setText(I18N.getString("repository.dialog.add"));
            addButton.setTooltip(new Tooltip(I18N.getString("repository.dialog.add.tooltip")));
        
            addButton.disableProperty().bind(nameIDTextfield.textProperty().isEmpty().or(
                      typeTextfield.textProperty().isEmpty().or(
                      urlTextfield.textProperty().isEmpty())));
        
            return;
        }
        
        nameIDTextfield.setText(repository.getId());
        typeTextfield.setText(repository.getType());
        urlTextfield.setText(repository.getURL());
        
        userTextfield.clear();
        if (repository.getUser() != null) {
            userTextfield.setText(repository.getUser());
            privateCheckBox.setSelected(true);
        } 
        passwordTextfield.clear();
        if (repository.getPassword()!= null) {
            passwordTextfield.setText(repository.getPassword());
            privateCheckBox.setSelected(true);
        } 
        
        addButton.disableProperty().bind(nameIDTextfield.textProperty().isEqualTo(repository.getId()).and(
                  typeTextfield.textProperty().isEqualTo(repository.getType()).and(
                  urlTextfield.textProperty().isEqualTo(repository.getURL()).and(
                  userTextfield.textProperty().isEqualTo(repository.getUser()).and(
                  passwordTextfield.textProperty().isEqualTo(repository.getPassword()))))));
        super.getStage().setTitle(I18N.getString("repository.dialog.title.update"));
        addButton.setText(I18N.getString("repository.dialog.update"));
        addButton.setTooltip(new Tooltip(I18N.getString("repository.dialog.update.tooltip")));
    }
}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/skeleton/SkeletonFileWriterSuccessAlert.java<|end_filename|>
/*
 * Copyright (c) 2021, Gluon and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation and Gluon nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.kit.skeleton;

import java.io.File;
import java.util.Map;
import java.util.Objects;
import java.util.function.Consumer;
import java.util.function.Supplier;

import com.oracle.javafx.scenebuilder.kit.alert.SBAlert;
import com.oracle.javafx.scenebuilder.kit.i18n.I18N;

import javafx.application.Platform;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.control.Button;
import javafx.scene.control.DialogPane;
import javafx.scene.control.TextField;
import javafx.scene.control.Tooltip;
import javafx.scene.input.Clipboard;
import javafx.scene.input.DataFormat;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.shape.SVGPath;
import javafx.stage.Stage;
/**
 * When a controller skeleton file has been successfully saved as file,
 * this class provides an alert which shows the file name (incl. path)
 * inside a text field. A button with a copy to clip board icon 
 * is placed right next to the text field. If clicked, the content of
 * the text field is copied into the system clip board.
 *  
 */
final class SkeletonFileWriterSuccessAlert implements Consumer<File> {

    private final Supplier<Stage> stageSupplier;

    /**
     * Creates a consumer which accepts the written file so
     * that an appropriate notification can be presented
     * to the user.
     *  
     * @param stageSupplier Reference to the parent JavaFX window
     */
    SkeletonFileWriterSuccessAlert(Supplier<Stage> stageSupplier) {
        this.stageSupplier = Objects.requireNonNull(stageSupplier);
    }

    @Override
    public void accept(File fileWritten) {
        var alert = prepareSuccessAlert();
        DialogPane dialogPane = alert.getDialogPane();
        TextField textField = new TextField(fileWritten.toString());
        textField.setEditable(false);
        Button copyButton = createCopyToClipboardButton(textField);
        HBox hbox = new HBox(textField,copyButton);
        HBox.setHgrow(textField, Priority.ALWAYS);
        dialogPane.setContent(hbox);
        Platform.runLater(() -> alert.showAndWait());
    }

    private SBAlert prepareSuccessAlert() {
        var alert = new SBAlert(AlertType.INFORMATION, stageSupplier.get());
        alert.setTitle(I18N.getString("alert.skeleton.title"));
        alert.setHeaderText(I18N.getString("alert.skeleton.header.success"));
        return alert;
    }

    private Button createCopyToClipboardButton(TextField textField) {
        SVGPath clipboardIcon = new SVGPath();
        clipboardIcon.setContent("M11.983,1.973L11.983,12.001L3.935,12.001L3.935,1.973L3.319,1.973C2.577,1.973 1.975,2.575 1.975,3.317L1.975,12.684C1.975,13.426 2.577,14.029 3.319,14.029L12.644,14.029C13.386,14.029 13.989,13.426 13.989,12.684L13.989,3.317C13.989,2.575 13.386,1.973 12.644,1.973L11.983,1.973ZM10.959,0.997L4.965,0.997L4.965,5.018L10.959,5.018L10.959,0.997ZM10.021,3.948L10.038,3.948L10.038,1.932L6.016,1.932L6.016,3.948L6.034,3.948L6.034,4.005L10.021,4.005L10.021,3.948Z");
        Button copyButton = new Button(null, clipboardIcon);
        copyButton.setTooltip(new Tooltip(I18N.getString("alert.skeleton.copy2clipboard")));
        copyButton.setOnAction(event -> Clipboard.getSystemClipboard()
                  .setContent(Map.of(DataFormat.PLAIN_TEXT, textField.getText())));
        return copyButton;
    }

}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/metadata/property/value/effect/DisplacementMapPropertyMetadata.java<|end_filename|>
/*
 * Copyright (c) 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */

package com.oracle.javafx.scenebuilder.kit.metadata.property.value.effect;

import com.oracle.javafx.scenebuilder.kit.fxom.FXOMDocument;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMInstance;
import com.oracle.javafx.scenebuilder.kit.metadata.property.value.BooleanPropertyMetadata;
import com.oracle.javafx.scenebuilder.kit.metadata.property.value.ComplexPropertyMetadata;
import com.oracle.javafx.scenebuilder.kit.metadata.property.value.DoublePropertyMetadata;
import com.oracle.javafx.scenebuilder.kit.metadata.util.InspectorPath;
import com.oracle.javafx.scenebuilder.kit.metadata.util.PropertyName;
import javafx.scene.effect.DisplacementMap;

/**
 *
 */
public class DisplacementMapPropertyMetadata extends ComplexPropertyMetadata<DisplacementMap> {
    
    private final DisplacementMap DEFAULT = new DisplacementMap();
    
    private final EffectPropertyMetadata inputMetadata
            = new EffectPropertyMetadata(new PropertyName("input"), //NOI18N
            true /* readWrite */, null, InspectorPath.UNUSED);
    private final DoublePropertyMetadata offsetXMetadata
            = new DoublePropertyMetadata(new PropertyName("offsetX"), //NOI18N
            DoublePropertyMetadata.DoubleKind.COORDINATE, true /* readWrite */, DEFAULT.getOffsetX(), InspectorPath.UNUSED);
    private final DoublePropertyMetadata offsetYMetadata
            = new DoublePropertyMetadata(new PropertyName("offsetY"), //NOI18N
            DoublePropertyMetadata.DoubleKind.COORDINATE, true /* readWrite */, DEFAULT.getOffsetY(), InspectorPath.UNUSED);
    private final DoublePropertyMetadata scaleXMetadata
            = new DoublePropertyMetadata(new PropertyName("scaleX"), //NOI18N
            DoublePropertyMetadata.DoubleKind.COORDINATE, true /* readWrite */, DEFAULT.getScaleX(), InspectorPath.UNUSED);
    private final DoublePropertyMetadata scaleYMetadata
            = new DoublePropertyMetadata(new PropertyName("scaleY"), //NOI18N
            DoublePropertyMetadata.DoubleKind.COORDINATE, true /* readWrite */, DEFAULT.getScaleY(), InspectorPath.UNUSED);
    private final BooleanPropertyMetadata wrapMetadata
            = new BooleanPropertyMetadata(new PropertyName("wrap"), //NOI18N
            true /* readWrite */, DEFAULT.isWrap(), InspectorPath.UNUSED);
    private final FloatMapPropertyMetadata mapDataMetadata
            = new FloatMapPropertyMetadata(new PropertyName("mapData"), //NOI18N
            true /* readWrite */, DEFAULT.getMapData(), InspectorPath.UNUSED);

    public DisplacementMapPropertyMetadata(PropertyName name, boolean readWrite, 
            DisplacementMap defaultValue, InspectorPath inspectorPath) {
        super(name, DisplacementMap.class, readWrite, defaultValue, inspectorPath);
    }

    /*
     * ComplexPropertyMetadata
     */
    
    @Override
    public FXOMInstance makeFxomInstanceFromValue(DisplacementMap value, FXOMDocument fxomDocument) {
        final FXOMInstance result = new FXOMInstance(fxomDocument, value.getClass());
        
        inputMetadata.setValue(result, value.getInput());
        offsetXMetadata.setValue(result, value.getOffsetX());
        offsetYMetadata.setValue(result, value.getOffsetY());
        scaleXMetadata.setValue(result, value.getScaleX());
        scaleYMetadata.setValue(result, value.getScaleY());
        wrapMetadata.setValue(result, value.isWrap());
        mapDataMetadata.setValue(result, value.getMapData());

        return result;
    }
}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/job/gridpane/v2/InsertColumnConstraintsJob.java<|end_filename|>
/*
 * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */

package com.oracle.javafx.scenebuilder.kit.editor.job.gridpane.v2;

import com.oracle.javafx.scenebuilder.kit.editor.EditorController;
import com.oracle.javafx.scenebuilder.kit.editor.job.Job;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMInstance;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMObject;
import com.oracle.javafx.scenebuilder.kit.metadata.property.value.list.ColumnConstraintsListPropertyMetadata;
import com.oracle.javafx.scenebuilder.kit.metadata.util.InspectorPath;
import com.oracle.javafx.scenebuilder.kit.metadata.util.PropertyName;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javafx.scene.layout.ColumnConstraints;
import javafx.scene.layout.GridPane;

/**
 *
 */
public class InsertColumnConstraintsJob extends Job {

    private static final ColumnConstraintsListPropertyMetadata columnContraintsMeta =
            new ColumnConstraintsListPropertyMetadata(
                new PropertyName("columnConstraints"), //NOI18N
                true, /* readWrite */
                Collections.emptyList(), /* defaultValue */
                InspectorPath.UNUSED);

    private final FXOMInstance gridPaneObject;
    private final int columnIndex;
    private final int insertCount;

    public InsertColumnConstraintsJob(FXOMObject gridPaneObject, 
            int columnIndex, int insertCount, EditorController editorController) {
        super(editorController);
        
        assert gridPaneObject instanceof FXOMInstance;
        assert gridPaneObject.getSceneGraphObject() instanceof GridPane;
        assert columnIndex >= 0;
        assert columnIndex <= columnContraintsMeta.getValue((FXOMInstance)gridPaneObject).size();
        assert insertCount >= 1;
        
        this.gridPaneObject = (FXOMInstance)gridPaneObject;
        this.columnIndex = columnIndex;
        this.insertCount = insertCount;
    }

    /*
     * Job
     */
    @Override
    public boolean isExecutable() {
        return true;
    }

    @Override
    public void execute() {
        // Same as redo()
        redo();
    }

    @Override
    public void undo() {
        final List<ColumnConstraints> constraintsList 
                = new ArrayList<>(columnContraintsMeta.getValue(gridPaneObject));
        assert columnIndex < constraintsList.size();
        for (int i = 0; i < insertCount; i++) {
            constraintsList.remove(columnIndex);
        }
        columnContraintsMeta.setValue(gridPaneObject, constraintsList);
    }

    @Override
    public void redo() {
        final List<ColumnConstraints> constraintsList 
                = new ArrayList<>(columnContraintsMeta.getValue(gridPaneObject));
        final ColumnConstraints template;
        if (columnIndex >= 1) {
            template = constraintsList.get(columnIndex-1);
        } else {
            template = null;
        }
        for (int i = 0; i < insertCount; i++) {
            constraintsList.add(columnIndex, makeColumnConstraints(template));
        }
        columnContraintsMeta.setValue(gridPaneObject, constraintsList);
    }

    @Override
    public String getDescription() {
        return getClass().getSimpleName();
    }
    
    
    /*
     * Private
     */
    
    private ColumnConstraints makeColumnConstraints(ColumnConstraints template) {
        final ColumnConstraints result = new ColumnConstraints();
        if (columnIndex >= 1) {
            result.setFillWidth(template.isFillWidth());
            result.setHalignment(template.getHalignment());
            result.setHgrow(template.getHgrow());
            result.setMaxWidth(template.getMaxWidth());
            result.setMinWidth(template.getMinWidth());
            result.setPercentWidth(template.getPercentWidth());
            result.setPrefWidth(template.getPrefWidth());
        }
        return result;
    }
}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/content/driver/tring/GenericParentTring.java<|end_filename|>
/*
 * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.kit.editor.panel.content.driver.tring;

import com.oracle.javafx.scenebuilder.kit.editor.panel.content.ContentPanelController;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMInstance;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMObject;
import com.oracle.javafx.scenebuilder.kit.metadata.util.DesignHierarchyMask;
import com.oracle.javafx.scenebuilder.kit.util.Deprecation;
import com.oracle.javafx.scenebuilder.kit.util.MathUtils;
import javafx.geometry.BoundingBox;
import javafx.geometry.Bounds;
import javafx.geometry.Point2D;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.shape.Line;

/**
 *
 * 
 */
public class GenericParentTring extends AbstractNodeTring<Parent> {
    
    private static final double CRACK_MIN_WIDTH = 6;
    
    private final int targetIndex;
    private final Line crackLine = new Line();

    public GenericParentTring(ContentPanelController contentPanelController, 
            FXOMInstance fxomInstance, int targetIndex) {
        super(contentPanelController, fxomInstance, Parent.class);
        assert targetIndex >= -1;
        this.targetIndex = targetIndex;
        
        crackLine.getStyleClass().add(TARGET_CRACK_CLASS);
        crackLine.setMouseTransparent(true);
        getRootNode().getChildren().add(crackLine);
    }

    
    public static int lookupCrackIndex(FXOMObject fxomObject, double sceneX, double sceneY) {
        assert fxomObject != null;
        assert fxomObject.getSceneGraphObject() instanceof Parent;
        
        final DesignHierarchyMask m = new DesignHierarchyMask(fxomObject);
        final Parent parent = (Parent) m.getFxomObject().getSceneGraphObject();
        final Point2D hitPoint = parent.sceneToLocal(sceneX, sceneY, true /* rootScene */);
        final int childCount = m.getSubComponentCount();
        
        final int targetIndex;
        if (childCount == 0) {
            // No children : we append
            targetIndex = -1;
            
        } else {
            assert childCount >= 1;
            
            final double hitX = hitPoint.getX();
            final double hitY = hitPoint.getY();
            double minDistance = Double.MAX_VALUE;
            int minIndex = -1;
            for (int i = 0, count = childCount; i < count; i++) {
                final Bounds cb 
                        = GenericParentTring.computeCrackBounds(m, i);
                final double midX = (cb.getMinX() + cb.getMaxX()) / 2.0;
                final double midY = (cb.getMinY() + cb.getMaxY()) / 2.0;
                final double d = MathUtils.distance(hitX, hitY, midX, midY);
                if (d < minDistance) {
                    minIndex = i;
                    minDistance = d;
                }
            }

            final Bounds cb 
                    = GenericParentTring.computeCrackBounds(m, -1);
            final double midX = (cb.getMinX() + cb.getMaxX()) / 2.0;
            final double midY = (cb.getMinY() + cb.getMaxY()) / 2.0;
            final double d = MathUtils.distance(hitX, hitY, midX, midY);
            if (d < minDistance) {
                minIndex = -1;
            }
            
            targetIndex = minIndex;
        }
        
        return targetIndex;
    }
    
    /*
     * AbstractGenericTring
     */
        
    @Override
    protected void layoutDecoration() {
        
        super.layoutDecoration();
        
        final DesignHierarchyMask m = new DesignHierarchyMask(getFxomObject());
        final int childCount = m.getSubComponentCount();
        
        if (childCount == 0) {
            // No crack line
            crackLine.setVisible(false);
            
        } else {
            // Computes the crack x
            final Bounds crackBounds = computeCrackBounds(m, targetIndex);
            final double crackX = (crackBounds.getMinX() + crackBounds.getMaxX()) / 2.0;
            final double crackY0 = crackBounds.getMinY();
            final double crackY1 = crackBounds.getMaxY();
            final double strokeWidth = crackBounds.getWidth();
            
            // Updates the crack line
            final boolean snapToPixel = true;
            final Point2D p0 = sceneGraphObjectToDecoration(crackX, crackY0, snapToPixel);
            final Point2D p1 = sceneGraphObjectToDecoration(crackX, crackY1, snapToPixel);

            crackLine.setVisible(true);
            crackLine.setStartX(p0.getX());
            crackLine.setStartY(p0.getY());
            crackLine.setEndX(p1.getX());
            crackLine.setEndY(p1.getY());
            crackLine.setStrokeWidth(strokeWidth);
        }
    }
    
    /*
     * Private
     */
    
    private static Bounds computeCrackBounds(DesignHierarchyMask m, int childIndex) {
        assert m != null;
        assert m.isAcceptingSubComponent();
        assert childIndex >= -1;
        assert childIndex < m.getSubComponentCount();
        
        
        final double crackX, crackY0, crackY1, crackWidth;
        final int childCount = m.getSubComponentCount();
        final Node child, skinParent;
        if (childIndex == -1) {
            child = getChildNode(m, childCount-1);
            skinParent = child.getParent();
            final Bounds cb = child.localToParent(child.getLayoutBounds());
            crackX = cb.getMaxX();
            crackY0 = cb.getMinY();
            crackY1 = cb.getMaxY();
            crackWidth = CRACK_MIN_WIDTH;
        } else if (childIndex == 0) {
            child = getChildNode(m, 0);
            skinParent = child.getParent();
            final Bounds cb = child.localToParent(child.getLayoutBounds());
            crackX = cb.getMinX();
            crackY0 = cb.getMinY();
            crackY1 = cb.getMaxY();
            crackWidth = CRACK_MIN_WIDTH;
        } else {

            /*        child at                  child at
             *      targetIndex-1             targetIndex
             * 
             * y0                           +--------------+   crackY0
             * y1 +---------------+         |              | 
             *    |               |         |              |
             *    |   prevBounds  |         |    bounds    |
             *    |               |         |              |
             * y2 +---------------+         |              |
             *                              |              |
             * y3                           +--------------+   crackY1
             *                   x0  crackX x1
             * 
             * 
             */
            assert (1 <= childIndex) && (childIndex < childCount);
            child = getChildNode(m, childIndex);
            final Node prevChild = getChildNode(m, childIndex-1);
            
            /*
             * child and prevChild may or may not be visible.
             * For example, when a ToolBar is shrinked, it automatically
             * hides the rightmost children. Those nodes are disconnected
             * from the skin and have a null parent.
             */
            
            final double x0, x1, y0, y2, y1, y3;
            if ((child.getParent() != null) && (prevChild.getParent() != null)) {
                assert child.getParent() == prevChild.getParent();
                skinParent = child.getParent();

                final Bounds prevBounds = prevChild.getBoundsInParent();
                final Bounds bounds = child.getBoundsInParent();
                x0 = prevBounds.getMaxX();
                x1 = bounds.getMinX();
                y0 = bounds.getMinY();
                y1 = prevBounds.getMinY();
                y2 = prevBounds.getMaxY();
                y3 = bounds.getMaxY();
            } else if (child.getParent() != null) {
                skinParent = child.getParent();
                final Bounds bounds = child.getBoundsInParent();
                x0 = x1 = bounds.getMinX();
                y0 = y1 = bounds.getMinY();
                y2 = y3 = bounds.getMaxY();
            } else if (prevChild.getParent() != null) {
                skinParent = prevChild.getParent();
                final Bounds prevBounds = prevChild.getBoundsInParent();
                x0 = x1 = prevBounds.getMaxX();
                y0 = y1 = prevBounds.getMinY();
                y2 = y3 = prevBounds.getMaxY();
            } else {
                // Both children are disconnect from the skin :(
                x0 = x1 = y0 = y1 = y2 = y3 = 0.0; // To generate empty bounds
                skinParent = null;
            }

            if (x0 <= x1) {
                crackX = (x0 + x1) / 2.0;
                crackY0 = Math.min(y0, y1);
                crackY1 = Math.max(y2, y3);
                crackWidth = Math.max(CRACK_MIN_WIDTH, x1 - x0);
            } else {
                crackX = x1;
                crackY0 = y0;
                crackY1 = y3;
                crackWidth = CRACK_MIN_WIDTH;
            }
        }
        
        assert m.getFxomObject().getSceneGraphObject() instanceof Parent;
        final Parent parent = (Parent) m.getFxomObject().getSceneGraphObject();
        final double pCrackX, pCrackY0, pCrackY1;
        if (parent != skinParent) {
            // m.getFxomObject() is a skinned component : so its fxom children
            // are not the direct Node children.
            if (skinParent != null) {
                final Point2D p0 = Deprecation.localToLocal(skinParent, crackX, crackY0, parent);
                final Point2D p1 = Deprecation.localToLocal(skinParent, crackX, crackY1, parent);
                assert MathUtils.equals(p0.getX(), p1.getX());
                pCrackX = p0.getX();
                pCrackY0 = p0.getY();
                pCrackY1 = p1.getY();
            } else {
                // Child at childIndex is inside a skin and is currently not
                // visible : we generate and return empty bounds.
                pCrackX = 0.0;
                pCrackY0 = 0.0;
                pCrackY1 = 0.0;
            }
            
        } else {
            pCrackX = crackX;
            pCrackY0 = crackY0;
            pCrackY1 = crackY1;
        }
        
        return new BoundingBox(pCrackX - crackWidth /2, pCrackY0, crackWidth, pCrackY1 - pCrackY0);
    }
    
    
    private static Node getChildNode(DesignHierarchyMask m, int childIndex) {
        assert m != null;
        assert m.isAcceptingSubComponent();
        assert 0 <= childIndex;
        assert childIndex < m.getSubComponentCount();
        
        final FXOMObject childObject = m.getSubComponentAtIndex(childIndex);
        assert childObject.getSceneGraphObject() instanceof Node;
        
        return (Node)childObject.getSceneGraphObject();
    }
}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/content/gesture/mouse/AdjustDividerGesture.java<|end_filename|>
/*
 * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.kit.editor.panel.content.gesture.mouse;

import com.oracle.javafx.scenebuilder.kit.editor.EditorController;
import com.oracle.javafx.scenebuilder.kit.editor.job.atomic.ModifyObjectJob;
import com.oracle.javafx.scenebuilder.kit.editor.panel.content.ContentPanelController;
import com.oracle.javafx.scenebuilder.kit.editor.panel.content.HudWindowController;
import com.oracle.javafx.scenebuilder.kit.editor.panel.content.driver.SplitPaneDesignInfoX;
import com.oracle.javafx.scenebuilder.kit.editor.panel.content.util.CardinalPoint;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMInstance;
import com.oracle.javafx.scenebuilder.kit.metadata.Metadata;
import com.oracle.javafx.scenebuilder.kit.metadata.property.ValuePropertyMetadata;
import com.oracle.javafx.scenebuilder.kit.metadata.util.PropertyName;
import java.util.ArrayList;
import java.util.List;
import javafx.scene.control.SplitPane;
import javafx.scene.input.KeyEvent;

/**
 *
 * 
 */
public class AdjustDividerGesture extends AbstractMouseGesture {

    private final FXOMInstance splitPaneInstance;
    private final int dividerIndex;
    private final SplitPaneDesignInfoX di = new SplitPaneDesignInfoX();
    private double[] originalDividerPositions;
    
    private static final PropertyName dividerPositionsName 
            = new PropertyName("dividerPositions"); //NOI18N

    public AdjustDividerGesture(ContentPanelController contentPanelController,
            FXOMInstance splitPaneInstance, int dividerIndex) {
        super(contentPanelController);
        
        assert splitPaneInstance.getSceneGraphObject() instanceof SplitPane;
        this.splitPaneInstance = splitPaneInstance;
        this.dividerIndex = dividerIndex;
    }

    /*
     * AbstractMouseGesture
     */
    
    @Override
    protected void mousePressed() {
        // Everthing is done in mouseDragStarted
    }

    @Override
    protected void mouseDragStarted() {
        originalDividerPositions = getSplitPane().getDividerPositions();
        setupAndOpenHudWindow();
        contentPanelController.getHandleLayer().setVisible(false);
        // Now same as mouseDragged
        mouseDragged();
    }

    @Override
    protected void mouseDragged() {
        final SplitPane splitPane = (SplitPane)splitPaneInstance.getSceneGraphObject();
        final double sceneX = getLastMouseEvent().getSceneX();
        final double sceneY = getLastMouseEvent().getSceneY();
        final double[] newDividerPositions 
                = di.simulateDividerMove(splitPane, dividerIndex, sceneX, sceneY);
        splitPane.setDividerPositions(newDividerPositions);
        splitPane.layout();
        updateHudWindow();
        contentPanelController.getHudWindowController().updatePopupLocation();
    }

    @Override
    protected void mouseDragEnded() {
        /*
         * Three steps
         * 
         * 1) Copy the updated divider positions
         * 2) Reverts to initial divider positions
         *    => this step is equivalent to userDidCancel()
         * 3) Push a BatchModifyObjectJob to officially update dividers
         */
        
        // Step #1
        final List<Double> newDividerPositions = new ArrayList<>();
        for (double p : getSplitPane().getDividerPositions()) {
            newDividerPositions.add(Double.valueOf(p));
        }
        
        // Step #2
        userDidCancel();
        
        // Step #3
        final Metadata metadata = Metadata.getMetadata();
        final EditorController editorController 
                = contentPanelController.getEditorController();
        final ValuePropertyMetadata dividerPositionsMeta 
                = metadata.queryValueProperty(splitPaneInstance, dividerPositionsName);
        final ModifyObjectJob j = new ModifyObjectJob(
                splitPaneInstance, 
                dividerPositionsMeta,
                newDividerPositions,
                editorController);
        if (j.isExecutable()) {
            editorController.getJobManager().push(j);
        } // else divider has been release to its original position
    }

    @Override
    protected void mouseReleased() {
        // Everything is done in mouseDragEnded
    }

    @Override
    protected void keyEvent(KeyEvent e) {
    }

    @Override
    protected void userDidCancel() {
        getSplitPane().setDividerPositions(originalDividerPositions);
        contentPanelController.getHudWindowController().closeWindow();
        contentPanelController.getHandleLayer().setVisible(true);
        getSplitPane().layout();
    }
    
    
    /*
     * Private
     */
    
    private SplitPane getSplitPane() {
        assert splitPaneInstance.getSceneGraphObject() instanceof SplitPane;
        return (SplitPane) splitPaneInstance.getSceneGraphObject();
    }
    
    private void setupAndOpenHudWindow() {
        final HudWindowController hudWindowController
                = contentPanelController.getHudWindowController();
        
        hudWindowController.setRowCount(1);
        hudWindowController.setNameAtRowIndex("dividerPosition", 0); //NOI18N
        updateHudWindow();
        
        final CardinalPoint cp;
        switch(getSplitPane().getOrientation()) {
            default:
            case HORIZONTAL:
                cp = CardinalPoint.S;
                break;
            case VERTICAL:
                cp = CardinalPoint.E;
                break;
        }
        hudWindowController.setRelativePosition(cp);
        hudWindowController.openWindow(getSplitPane());
    }
    
    private void updateHudWindow() {
        final HudWindowController hudWindowController
                = contentPanelController.getHudWindowController();
        
        double dividerPosition = getSplitPane().getDividerPositions()[dividerIndex];
        String str = String.format("%.2f %%", dividerPosition * 100); //NOI18N
        hudWindowController.setValueAtRowIndex(str, 0);
    }
}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/util/control/paintpicker/colorpicker/ColorPicker.java<|end_filename|>
/*
 * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.kit.util.control.paintpicker.colorpicker;

import com.oracle.javafx.scenebuilder.kit.util.control.paintpicker.PaintPicker.Mode;
import com.oracle.javafx.scenebuilder.kit.util.control.paintpicker.PaintPickerController;
import com.oracle.javafx.scenebuilder.kit.util.control.paintpicker.gradientpicker.GradientPicker;
import com.oracle.javafx.scenebuilder.kit.util.control.paintpicker.gradientpicker.GradientPickerStop;

import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;

import javafx.beans.value.ChangeListener;
import javafx.event.ActionEvent;
import javafx.fxml.FXMLLoader;
import javafx.scene.control.ScrollPane;
import javafx.fxml.FXML;
import javafx.geometry.Bounds;
import javafx.scene.control.Slider;
import javafx.scene.control.TextField;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Region;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.paint.LinearGradient;
import javafx.scene.paint.Paint;
import javafx.scene.paint.RadialGradient;
import javafx.scene.shape.Circle;

/**
 * Controller class for the color part of the paint editor.
 */
public class ColorPicker extends VBox {

    @FXML
    private Region chip_region;
    @FXML
    private Region alpha_region;
    @FXML
    private ScrollPane picker_scrollpane;
    @FXML
    private Region picker_region;
    @FXML
    private StackPane picker_handle_stackpane;
    @FXML
    private Circle picker_handle_chip_circle;
    @FXML
    private Slider hue_slider;
    @FXML
    private Slider alpha_slider;
    @FXML
    private TextField hue_textfield;
    @FXML
    private TextField saturation_textfield;
    @FXML
    private TextField brightness_textfield;
    @FXML
    private TextField red_textfield;
    @FXML
    private TextField green_textfield;
    @FXML
    private TextField blue_textfield;
    @FXML
    private TextField alpha_textfield;
    @FXML
    private TextField hexa_textfield;

    private final PaintPickerController paintPickerController;
    private boolean updating = false;

    public ColorPicker(PaintPickerController pe) {
        paintPickerController = pe;
        initialize();
    }

    public Color getValue() {
        double hue = Double.valueOf(hue_textfield.getText());
        double saturation = Double.valueOf(saturation_textfield.getText()) / 100.0;
        double brightness = Double.valueOf(brightness_textfield.getText()) / 100.0;
        double alpha = Double.valueOf(alpha_textfield.getText());
        return Color.hsb(hue, saturation, brightness, alpha);
    }

    public void updateUI(final Color color) {
        double hue = color.getHue();
        double saturation = color.getSaturation();
        double brightness = color.getBrightness();
        double alpha = color.getOpacity();
        updateUI(hue, saturation, brightness, alpha);
    }

    /**
     * Private
     */
    private void initialize() {

        final FXMLLoader loader = new FXMLLoader();
        loader.setLocation(ColorPicker.class.getResource("ColorPicker.fxml")); //NOI18N
        loader.setController(this);
        loader.setRoot(this);
        try {
            loader.load();
        } catch (IOException ex) {
            Logger.getLogger(ColorPicker.class.getName()).log(Level.SEVERE, null, ex);
        }

        assert hue_slider != null;
        assert picker_region != null;
        assert hue_textfield != null;
        assert saturation_textfield != null;
        assert brightness_textfield != null;
        assert alpha_textfield != null;
        assert red_textfield != null;
        assert green_textfield != null;
        assert blue_textfield != null;
        assert alpha_slider != null;

        hue_slider.setStyle(makeHueSliderCSS()); // Make the grad for hue slider

        // Investigate why height + width listeners do not work
        // Indeed, the picker_handle_stackpane bounds may still be null at this point
        // UPDATE BELOW TO BE CALLED ONCE ONLY AT DISPLAY TIME
        picker_region.boundsInParentProperty().addListener((ChangeListener<Bounds>) (ov, oldb, newb) -> {
            picker_scrollpane.setHvalue(0.5);
            picker_scrollpane.setVvalue(0.5);
            // Init time only
            final Paint paint = paintPickerController.getPaintProperty();
            if (paint instanceof Color) {
                updateUI((Color) paint);
            } else if (paint instanceof LinearGradient
                    || paint instanceof RadialGradient) {
                final GradientPicker gradientPicker = paintPickerController.getGradientPicker();
                final GradientPickerStop gradientPickerStop = gradientPicker.getSelectedStop();
                // Update the color preview with the color of the selected stop
                if (gradientPickerStop != null) {
                    updateUI(gradientPickerStop.getColor());
                }
            }
        });

        final ChangeListener<Boolean> onHSBFocusedChange = (ov, oldValue, newValue) -> {
            if (newValue == false) {
                // Update UI
                final Color color = updateUI_OnHSBChange();
                // Update model
                setPaintProperty(color);
            }
        };
        final ChangeListener<Boolean> onRGBFocusedChange = (ov, oldValue, newValue) -> {
            if (newValue == false) {
                // Update UI
                final Color color = updateUI_OnRGBChange();
                // Update model
                setPaintProperty(color);
            }
        };
        final ChangeListener<Boolean> onHexaFocusedChange = (ov, oldValue, newValue) -> {
            if (newValue == false) {
                try {
                    // Update UI
                    final Color color = updateUI_OnHexaChange();
                    // Update model
                    setPaintProperty(color);
                } catch (IllegalArgumentException iae) {
                    handleHexaException();
                }
            }
        };

        // TextField ON FOCUS LOST event handler
        hue_textfield.focusedProperty().addListener(onHSBFocusedChange);
        saturation_textfield.focusedProperty().addListener(onHSBFocusedChange);
        brightness_textfield.focusedProperty().addListener(onHSBFocusedChange);
        alpha_textfield.focusedProperty().addListener(onHSBFocusedChange);
        red_textfield.focusedProperty().addListener(onRGBFocusedChange);
        green_textfield.focusedProperty().addListener(onRGBFocusedChange);
        blue_textfield.focusedProperty().addListener(onRGBFocusedChange);
        hexa_textfield.focusedProperty().addListener(onHexaFocusedChange);

        // Slider ON VALUE CHANGE event handler
        hue_slider.valueProperty().addListener((ChangeListener<Number>) (ov, oldValue, newValue) -> {
            if (updating == true) {
                return;
            }
            double hue = newValue.doubleValue();
            // retrieve HSB TextFields values
            double saturation = Double.valueOf(saturation_textfield.getText()) / 100.0;
            double brightness = Double.valueOf(brightness_textfield.getText()) / 100.0;
            double alpha = Double.valueOf(alpha_textfield.getText());
            // Update UI
            final Color color = updateUI(hue, saturation, brightness, alpha);
            // Update model
            setPaintProperty(color);
        });
        alpha_slider.valueProperty().addListener((ChangeListener<Number>) (ov, oldValue, newValue) -> {
            if (updating == true) {
                return;
            }
            double alpha = newValue.doubleValue();
            // retrieve HSB TextFields values
            double hue = Double.valueOf(hue_textfield.getText());
            double saturation = Double.valueOf(saturation_textfield.getText()) / 100.0;
            double brightness = Double.valueOf(brightness_textfield.getText()) / 100.0;
            // Update UI
            final Color color = updateUI(hue, saturation, brightness, alpha);
            // Update model
            setPaintProperty(color);
        });

        final ChangeListener<Boolean> liveUpdateListener = (ov, oldValue, newValue) -> paintPickerController.setLiveUpdate(newValue);
        picker_region.pressedProperty().addListener(liveUpdateListener);
        hue_slider.pressedProperty().addListener(liveUpdateListener);
        alpha_slider.pressedProperty().addListener(liveUpdateListener);
    }

    /**
     * When updating the color picker, we may update :
     * - either the color of the paint picker itself (Color mode)
     * - or the color of the selected stop (LinearGradient or RadialGradient mode)
     *
     * @param color
     */
    private void setPaintProperty(Color color) {
        final Mode mode = paintPickerController.getMode();
        final Paint paint;
        switch (mode) {
            case COLOR:
                paint = color;
                break;
            case LINEAR:
            case RADIAL:
                final GradientPicker gradientPicker = paintPickerController.getGradientPicker();
                final GradientPickerStop gradientPickerStop = gradientPicker.getSelectedStop();
                // Set the color of the selected stop
                if (gradientPickerStop != null) {
                    gradientPickerStop.setColor(color);
                }
                // Update gradient preview
                paint = gradientPicker.getValue(mode);
                gradientPicker.updatePreview(paint);
                break;
            default:
                paint = null;
                break;
        }
        paintPickerController.setPaintProperty(paint);
    }

    @FXML
    void onActionHue(ActionEvent event) {
        onHSBChange(event);
        event.consume();
    }

    @FXML
    void onActionSaturation(ActionEvent event) {
        onHSBChange(event);
        event.consume();
    }

    @FXML
    void onActionBrightness(ActionEvent event) {
        onHSBChange(event);
        event.consume();
    }

    @FXML
    void onActionAlpha(ActionEvent event) {
        onHSBChange(event);
        event.consume();
    }

    @FXML
    void onActionRed(ActionEvent event) {
        onRGBChange(event);
        event.consume();
    }

    @FXML
    void onActionGreen(ActionEvent event) {
        onRGBChange(event);
        event.consume();
    }

    @FXML
    void onActionBlue(ActionEvent event) {
        onRGBChange(event);
        event.consume();
    }

    @FXML
    void onActionHexa(ActionEvent event) {
        onHexaChange(event);
        event.consume();
    }

    private void onHSBChange(ActionEvent event) {
        // Update UI
        final Color color = updateUI_OnHSBChange();
        final Object source = event.getSource();
        assert source instanceof TextField;
        ((TextField) source).selectAll();
        // Update model
        setPaintProperty(color);
    }

    private void onRGBChange(ActionEvent event) {
        // Update UI
        final Color color = updateUI_OnRGBChange();
        final Object source = event.getSource();
        assert source instanceof TextField;
        ((TextField) source).selectAll();
        // Update model
        setPaintProperty(color);
    }

    private void onHexaChange(ActionEvent event) {
        try {
            // Update UI
            final Color color = updateUI_OnHexaChange();
            final Object source = event.getSource();
            assert source instanceof TextField;
            ((TextField) source).selectAll();
            // Update model
            setPaintProperty(color);
        } catch (IllegalArgumentException iae) {
            handleHexaException();
        }
    }

    @FXML
    void onPickerRegionPressed(MouseEvent e) {
        double mx = e.getX();
        double my = e.getY();
        // Update UI
        final Color color = updateUI_OnPickerChange(mx, my);
        // Update model
        setPaintProperty(color);
    }

    @FXML
    void onPickerRegionDragged(MouseEvent e) {
        double mx = e.getX();
        double my = e.getY();
        // Update UI
        final Color color = updateUI_OnPickerChange(mx, my);
        // Update model
        setPaintProperty(color);
    }

    private Color updateUI_OnPickerChange(double x, double y) {
        double w = picker_region.getWidth();
        double h = picker_region.getHeight();
        double hue = Double.valueOf(hue_textfield.getText());
        double saturation = x / w;
        double brightness = 1.0 - (y / h);
        double alpha = Double.valueOf(alpha_textfield.getText());
        return updateUI(hue, saturation, brightness, alpha);
    }

    private Color updateUI_OnHSBChange() {
        // retrieve HSB TextFields values
        double hue = Double.valueOf(hue_textfield.getText());
        double saturation = Double.valueOf(saturation_textfield.getText()) / 100.0;
        double brightness = Double.valueOf(brightness_textfield.getText()) / 100.0;
        double alpha = Double.valueOf(alpha_textfield.getText());
        return updateUI(hue, saturation, brightness, alpha);
    }

    private Color updateUI_OnRGBChange() {
        // retrieve RGB TextFields values
        int red = Double.valueOf(red_textfield.getText()).intValue();
        int green = Double.valueOf(green_textfield.getText()).intValue();
        int blue = Double.valueOf(blue_textfield.getText()).intValue();
        // retrieve HSB values from RGB values
        final Color color = Color.rgb(red, green, blue);
        double hue = color.getHue();
        double saturation = color.getSaturation();
        double brightness = color.getBrightness();
        double alpha = Double.valueOf(alpha_textfield.getText());
        return updateUI(hue, saturation, brightness, alpha);
    }

    private Color updateUI_OnHexaChange() {
        // retrieve Hexa TextField value
        final String hexa = hexa_textfield.getText().trim();
        final Color color = Color.web(hexa);
        double hue = color.getHue();
        double saturation = color.getSaturation();
        double brightness = color.getBrightness();
        double alpha = Double.valueOf(alpha_textfield.getText());
        return updateUI(hue, saturation, brightness, alpha);
    }

    private Color updateUI(double hue, double saturation, double brightness, double alpha) {

        updating = true;

        // update the HSB values so they are within range
        hue = PaintPickerController.clamp(0, hue, 360);
        saturation = PaintPickerController.clamp(0, saturation, 1);
        brightness = PaintPickerController.clamp(0, brightness, 1);
        alpha = PaintPickerController.clamp(0, alpha, 1);
        // make an rgb color from the hsb
        final Color color = Color.hsb(hue, saturation, brightness, alpha);
        int red   = (int) Math.round(color.getRed() * 255);
        int green = (int) Math.round(color.getGreen() * 255);
        int blue  = (int) Math.round(color.getBlue() * 255);
        final String hexa = String.format("#%02x%02x%02x", red, green, blue); //NOI18N

        // Set TextFields value
        hue_textfield.setText(String.valueOf((int) hue));
        saturation_textfield.setText(String.valueOf((int) (saturation * 100)));
        brightness_textfield.setText(String.valueOf((int) (brightness * 100)));
        double alpha_rounded = round(alpha, 100); // 2 decimals rounding
        alpha_textfield.setText(Double.toString(alpha_rounded));
        red_textfield.setText(Integer.toString(red));
        green_textfield.setText(Integer.toString(green));
        blue_textfield.setText(Integer.toString(blue));
        hexa_textfield.setText(hexa);

        // Set the background color of the chips
        final StringBuilder sb = new StringBuilder();
        sb.append("hsb("); //NOI18N
        sb.append(hue);
        sb.append(", "); //NOI18N
        sb.append(saturation * 100);
        sb.append("%, "); //NOI18N
        sb.append(brightness * 100);
        sb.append("%, "); //NOI18N
        sb.append(alpha);
        sb.append(")"); //NOI18N
        final String hsbCssValue = sb.toString();
        final String chipStyle = "-fx-background-color: " + hsbCssValue; //NOI18N
        chip_region.setStyle(chipStyle);
        picker_handle_chip_circle.setFill(Color.rgb(red, green, blue));
        final String alphaChipStyle = "-fx-background-color: " //NOI18N
                + "linear-gradient(to right, transparent, " + hsbCssValue + ")"; //NOI18N
        alpha_region.setStyle(alphaChipStyle);

        // Set the background color of the picker region
        // (force saturation and brightness to 100% - don't add opacity)
        final String pickerRegionStyle = "-fx-background-color: hsb(" //NOI18N
                + hue + ", 100%, 100%, 1.0);"; //NOI18N
        picker_region.setStyle(pickerRegionStyle);

        // Position the picker dot
        double xSat = picker_region.getWidth() * saturation; // Saturation is on x axis
        double yBri = picker_region.getHeight() * (1.0 - brightness); // Brightness is on y axis (reversed as white is top)
        double xPos = (picker_region.getBoundsInParent().getMinX() + xSat) - picker_handle_stackpane.getWidth() / 2;
        double yPos = (picker_region.getBoundsInParent().getMinY() + yBri) - picker_handle_stackpane.getHeight() / 2;
        picker_handle_stackpane.setLayoutX(xPos);
        picker_handle_stackpane.setLayoutY(yPos);

        // Set the Sliders value
        hue_slider.adjustValue(hue);
        alpha_slider.adjustValue(alpha);

        updating = false;
        return color;
    }

    private String makeHueSliderCSS() {
        final StringBuilder sb = new StringBuilder();
        sb.append("-fx-background-color: linear-gradient(to right "); //NOI18N
        for (int i = 0; i < 12; i++) { // max 12 gradient stops
            sb.append(", hsb("); //NOI18N
            sb.append(i * (360 / 11));
            sb.append(", 100%, 100%)"); //NOI18N
        }
        sb.append(");"); //NOI18N
        return sb.toString();
    }

    private double round(double value, int roundingFactor) {
        double doubleRounded = Math.round(value * roundingFactor);
        return doubleRounded / roundingFactor;
    }

    private void handleHexaException() {
        paintPickerController.getDelegate().handleError(
                "log.warning.color.creation.error.hexadecimal",
                hexa_textfield.getText().trim());
        // Update UI to previous value
        final Color color;
        switch (paintPickerController.getMode()) {
            case COLOR:
                assert paintPickerController.getPaintProperty() instanceof Color;
                color = (Color) paintPickerController.getPaintProperty();
                break;
            case LINEAR:
            case RADIAL:
                final GradientPicker gradientPicker = paintPickerController.getGradientPicker();
                if (gradientPicker.getGradientStops().isEmpty() == false) {
                    final GradientPickerStop stop = paintPickerController.getGradientPicker().getSelectedStop();
                    assert stop != null;
                    color = stop.getColor();
                } else {
                    color = Color.BLACK;
                }
                break;
            default:
                color = null;
                assert false;
        }
        assert color != null;
        updateUI(color);
        hexa_textfield.selectAll();
    }
}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/inspector/popupeditors/EffectPopupEditor.java<|end_filename|>
/*
 * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.kit.editor.panel.inspector.popupeditors;

import com.oracle.javafx.scenebuilder.kit.editor.EditorController;
import com.oracle.javafx.scenebuilder.kit.metadata.property.ValuePropertyMetadata;
import com.oracle.javafx.scenebuilder.kit.util.control.effectpicker.EffectPicker;
import com.oracle.javafx.scenebuilder.kit.util.control.effectpicker.Utils;
import com.oracle.javafx.scenebuilder.kit.util.control.paintpicker.PaintPicker;

import java.util.List;
import java.util.Set;

import javafx.beans.value.ChangeListener;
import javafx.scene.Node;
import javafx.scene.control.MenuItem;
import javafx.scene.effect.Effect;

/**
 * Popup editor for the Effect property.
 */
public class EffectPopupEditor extends PopupEditor {

    private EffectPicker effectPicker;
    private List<MenuItem> effectMenuItems;
    private EditorController editorController;

    private final ChangeListener<Number> effectRevisionChangeListener = (ov, t, t1) -> {
        final Effect rootEffect = effectPicker.getRootEffectProperty();
        // Need to clone the root effect of the effect picker
        // in order to commit with a new value
        final Effect rootEffectClone = Utils.clone(rootEffect);
        // If live update, do not commit the value
        if (effectPicker.isLiveUpdate() == true) {
            userUpdateTransientValueProperty(rootEffectClone);
        } else {
            commitValue(rootEffectClone);
            updateMenuButton(rootEffectClone);
        }
    };

    private final ChangeListener<Boolean> liveUpdateListener = (ov, oldValue, newValue) -> {
        if (effectPicker.isLiveUpdate() == false) {
            final Effect rootEffect = effectPicker.getRootEffectProperty();
            // Need to clone the root effect of the effect picker
            // in order to commit with a new value
            final Effect rootEffectClone = Utils.clone(rootEffect);
            commitValue(rootEffectClone);
            updateMenuButton(rootEffectClone);
        }
    };

    public EffectPopupEditor(ValuePropertyMetadata propMeta, Set<Class<?>> selectedClasses, EditorController editorController) {
        super(propMeta, selectedClasses);
        initialize(editorController);
    }
    
    private void initialize(EditorController editorController) {
        this.editorController = editorController;
    }

    @Override
    public void setPopupContentValue(Object value) {
        assert value == null || value instanceof Effect;
        effectPicker.revisionProperty().removeListener(effectRevisionChangeListener);
        effectPicker.liveUpdateProperty().removeListener(liveUpdateListener);
        // We first clone the root effect and initializePopupContent the effect picker with the clone value :
        // then the clone value will be updated and passed back to the model
        final Effect rootEffectClone = Utils.clone((Effect) value);
        effectPicker.setRootEffectProperty(rootEffectClone);
        // Refresh MenuButton items if needed
        updateMenuButton(rootEffectClone);
        effectPicker.revisionProperty().addListener(effectRevisionChangeListener);
        effectPicker.liveUpdateProperty().addListener(liveUpdateListener);
    }

    @Override
    public void initializePopupContent() {
        final EffectPicker.Delegate epd = (warningKey, arguments) -> editorController.getMessageLog().logWarningMessage(warningKey, arguments);
        final PaintPicker.Delegate ppd = (warningKey, arguments) -> editorController.getMessageLog().logWarningMessage(warningKey, arguments);
        effectPicker = new EffectPicker(epd, ppd);
        effectMenuItems = effectPicker.getMenuItems();
    }

    @Override
    public String getPreviewString(Object value) {
        if (value == null) {
            return "+"; //NOI18N
        }
        assert value instanceof Effect;
        Effect effect = (Effect) value;
        final StringBuilder sb = new StringBuilder();
        while (effect != null) {
            sb.append(effect.getClass().getSimpleName());
            effect = Utils.getDefaultInput(effect);
            if (effect != null) {
                sb.append(", "); //NOI18N
            }
        }
        return sb.toString();
    }

    @Override
    public Node getPopupContentNode() {
        return effectPicker;
    }

    private void updateMenuButton(Effect value) {
        if (value != null) {
            if (popupMb.getItems().contains(popupMenuItem) == false) {
                popupMb.getItems().removeAll(effectMenuItems);
                popupMb.getItems().add(popupMenuItem);
            }
        } else {
            if (popupMb.getItems().contains(popupMenuItem) == true) {
                popupMb.getItems().addAll(effectMenuItems);
                popupMb.getItems().remove(popupMenuItem);
            }
        }
    }
}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/fxom/FXOMAssetIndex.java<|end_filename|>
/*
 * Copyright (c) 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */

package com.oracle.javafx.scenebuilder.kit.fxom;

import com.oracle.javafx.scenebuilder.kit.metadata.property.value.list.StringListPropertyMetadata;
import com.oracle.javafx.scenebuilder.kit.metadata.util.PrefixedValue;
import com.oracle.javafx.scenebuilder.kit.metadata.util.PropertyName;
import com.oracle.javafx.scenebuilder.kit.util.URLUtils;
import java.io.File;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.FileSystemNotFoundException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import javafx.fxml.FXMLLoader;

/**
 *
 */
public class FXOMAssetIndex {
    
    private final FXOMDocument fxomDocument;
    private final Map<Path, FXOMNode> fileAssets;
    
    public FXOMAssetIndex(FXOMDocument fxomDocument) {
        assert fxomDocument != null;
        this.fxomDocument = fxomDocument;
        this.fileAssets = Collections.unmodifiableMap(collectAssets());
    }
    
    public Map<Path, FXOMNode> getFileAssets() {
        return fileAssets;
    }
    
    /*
     * Private
     */
    
    private static final PropertyName valueName = new PropertyName("value"); //NOI18N
    
    private Map<Path, FXOMNode> collectAssets() {
        final Map<Path, FXOMNode> result = new HashMap<>();
        
        if (fxomDocument.getFxomRoot() != null) {
            final FXOMObject fxomRoot = fxomDocument.getFxomRoot();
            
            /*
             * Collects properties containing prefixed values (ie @ expression).
             */
            for (FXOMPropertyT p : fxomRoot.collectPropertiesT()) {
                for (String s : StringListPropertyMetadata.splitValue(p.getValue())) {
                    final Path path = extractPath(s);
                    if (path != null) {
                        result.put(path, p);
                    }
                }
            }
            
            
            /*
             * Collects URL instances.
             */
            for (FXOMObject fxomObject : fxomRoot.collectObjectWithSceneGraphObjectClass(URL.class)) {
                if (fxomObject instanceof FXOMInstance) {
                    final FXOMInstance urlInstance = (FXOMInstance) fxomObject;
                    final FXOMProperty valueProperty = urlInstance.getProperties().get(valueName);
                    if (valueProperty instanceof FXOMPropertyT) {
                        FXOMPropertyT valuePropertyT = (FXOMPropertyT) valueProperty;
                        final Path path = extractPath(valuePropertyT.getValue());
                        if (path != null) {
                            result.put(path, valuePropertyT);
                        }
                    } else {
                        assert false : "valueProperty.getName()=" + valueProperty.getName();
                    }
                }
            }
            
            /*
             * Collects fx:include
             */
            for (FXOMIntrinsic fxomInclude : fxomRoot.collectIncludes(null)) {
                final String equivalentValue 
                        = FXMLLoader.RELATIVE_PATH_PREFIX + fxomInclude.getSource();
                final Path path 
                        = extractPath(equivalentValue);
                if (path != null) {
                    result.put(path, fxomInclude);
                }
            }
        }
        
        return result;
    }
    
    
    private Path extractPath(String stringValue) {
        Path result;
        
        final PrefixedValue pv = new PrefixedValue(stringValue);
        if (pv.isPlainString()) {
            try {
                final File file = URLUtils.getFile(pv.getSuffix());
                if (file == null) { // Not a file URL
                    result = null;
                } else {
                    result = file.toPath();
                }
            } catch(URISyntaxException x) {
                result = null;
            }
        } else if (pv.isDocumentRelativePath()) {
            final URL documentLocation = fxomDocument.getLocation();
            if (documentLocation == null) {
                result = null;
            } else {
                final URL url = pv.resolveDocumentRelativePath(documentLocation);
                if (url == null) {
                    result = null;
                } else {
                    try {
                        result = Paths.get(url.toURI());
                    } catch(FileSystemNotFoundException|URISyntaxException x) {
                        result = null;
                    }
                }
            }
        } else if (pv.isClassLoaderRelativePath()) {
            final ClassLoader classLoader = fxomDocument.getClassLoader();
            if (classLoader == null) {
                result = null;
            } else {
                final URL url = pv.resolveClassLoaderRelativePath(classLoader);
                if (url == null) {
                    result = null;
                } else {
                    try {
                        final File file = URLUtils.getFile(url);
                        if (file == null) { // Not a file URL
                            result = null;
                        } else {
                            result = file.toPath();
                        }
                    } catch(URISyntaxException x) {
                        result = null;
                    }
                }
                
            }
        } else {
            result = null;
        }
        
        return result;
    }
    
}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/util/eventnames/ImportBuilder.java<|end_filename|>
/*
 * Copyright (c) 2021, Gluon and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.kit.util.eventnames;

import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.stream.Collectors;

/**
 * Builds an import statement as a key/value pair.
 */
public class ImportBuilder {

    public static final String IMPORT_STATEMENT = "import ";
    public static final String INPUT_PACKAGE = "javafx.scene.input.";
    public static final String EVENT_PACKAGE = "javafx.event.";
    public static final String FXML_PACKAGE = "javafx.fxml.FXML";

    private static Map<String, String> imports = new TreeMap<>();

    // should be used in a static way
    private ImportBuilder() {
    }

    /**
     * Adds an imported class with its package.
     *
     * @param importedPackage package the class comes from
     * @param importedClass imported class
     */
    public static void add(String importedPackage, String importedClass) {
        imports.put(importedPackage, importedClass);
    }

    /**
     * This methods deletes all previously set events imports.
     */
    public static void reset() {
        imports.clear();
    }

    /**
     * Returns true if no imports are set, otherwise false.
     *
     * @return
     */
    public static boolean isEmpty() {
        return imports.isEmpty();
    }

    /**
     * Builds the list of import statements. The key may be the "import" keyword, or the "import" keyword combined with a
     * "javafx.scene.input." package name. The value may be a full class name (e.g. with package) or an input event name.
     *
     * @return list of new import statements
     */
    public static List<String> build() {
        return imports.entrySet()
                      .stream()
                      .map(preparedImport -> preparedImport.getKey() + preparedImport.getValue())
                      .collect(Collectors.toList());
    }
}

<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/library/maven/MavenArtifact.java<|end_filename|>
/*
 * Copyright (c) 2016, Gluon and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation and Gluon nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */

package com.oracle.javafx.scenebuilder.kit.editor.panel.library.maven;

import java.util.Objects;

public class MavenArtifact {
    
    private String coordinates;
    private String path;
    private String dependencies;
    private String filter;

    public MavenArtifact(String coordinates) {
        this.coordinates = coordinates;
    }

    public MavenArtifact(String coordinates, String path, String dependencies, String filter) {
        this.coordinates = coordinates;
        this.path = path;
        this.dependencies = dependencies;
        this.filter = filter;
    }

    public String getCoordinates() {
        return coordinates;
    }

    public void setCoordinates(String coordinates) {
        this.coordinates = coordinates;
    }

    public String getPath() {
        return path;
    }

    public void setPath(String path) {
        this.path = path;
    }

    public String getDependencies() {
        return dependencies;
    }

    public void setDependencies(String dependencies) {
        this.dependencies = dependencies;
    }

    public String getFilter() {
        return filter;
    }

    public void setFilter(String filter) {
        this.filter = filter;
    }

    @Override
    public int hashCode() {
        int hash = 7;
        hash = 97 * hash + Objects.hashCode(this.coordinates);
        hash = 97 * hash + Objects.hashCode(this.path);
        hash = 97 * hash + Objects.hashCode(this.dependencies);
        hash = 97 * hash + Objects.hashCode(this.filter);
        return hash;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final MavenArtifact other = (MavenArtifact) obj;
        if (!Objects.equals(this.coordinates, other.coordinates)) {
            return false;
        }
        if (!Objects.equals(this.path, other.path)) {
            return false;
        }
        if (!Objects.equals(this.dependencies, other.dependencies)) {
            return false;
        }
        return Objects.equals(this.filter, other.filter);
    }

    
    @Override
    public String toString() {
        return "MavenArtifact{" + "coordinates=" + coordinates + ", path=" + path + ", dependencies=" + dependencies + ", filter=" + filter + '}';
    }

}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/content/driver/resizer/GridPaneRowResizer.java<|end_filename|>
/*
 * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */

package com.oracle.javafx.scenebuilder.kit.editor.panel.content.driver.resizer;

import com.oracle.javafx.scenebuilder.kit.metadata.util.PropertyName;
import com.oracle.javafx.scenebuilder.kit.util.Deprecation;
import com.oracle.javafx.scenebuilder.kit.util.MathUtils;
import java.util.HashMap;
import java.util.Map;
import javafx.geometry.Bounds;
import javafx.scene.layout.RowConstraints;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Region;

/**
 *
 */
public class GridPaneRowResizer {
    
    private static final PropertyName minHeightName     = new PropertyName("minHeight"); //NOI18N
    private static final PropertyName prefHeightName    = new PropertyName("prefHeight"); //NOI18N
    private static final PropertyName maxHeightName     = new PropertyName("maxHeight"); //NOI18N
    private static final PropertyName percentHeightName = new PropertyName("percentHeight"); //NOI18N

    private final GridPane gridPane;
    private final int rowIndex;
    private final RowSizing originalSizing; // Row at rowIndex
    private final RowSizing originalSizingNext; // Row at rowIndex+1
    private final boolean usePercentSizing;
    private final double y1, y2, y3, y4, ym;

    public GridPaneRowResizer(GridPane gridPane, int rowIndex) {
        assert gridPane != null;
        assert rowIndex >= 0;
        assert rowIndex+1 < gridPane.getRowConstraints().size();
        
        this.gridPane = gridPane;
        this.rowIndex = rowIndex;
        this.originalSizing 
                = new RowSizing(gridPane.getRowConstraints().get(rowIndex));
        this.originalSizingNext 
                = new RowSizing(gridPane.getRowConstraints().get(rowIndex+1));
        this.usePercentSizing
                = countPercentWidths() == Deprecation.getGridPaneColumnCount(gridPane);
        
        //
        //
        //   y1 +-----
        //      |
        //      |
        //      |   row n
        //      |
        //      |
        //   y2 +-----
        //      |   vgap n
        ///  y3 +-----
        //      |
        //      |
        //      |
        //      |   row n+1
        //      |
        //   ym +
        //      |
        ///  y4 +-----
        //    
        //
        //       Range for moving y2 is [y1, ym]
        //       With (y4 - ym) == (y3 - y2)
        //

        // Compute y1, y2, y3, y4, ym
        final Bounds cellBounds 
                = Deprecation.getGridPaneCellBounds(gridPane, 0, rowIndex);
        final Bounds nextBounds 
                = Deprecation.getGridPaneCellBounds(gridPane, 0, rowIndex+1);

        y1 = cellBounds.getMinY();
        y2 = cellBounds.getMaxY();
        y3 = nextBounds.getMinY();
        y4 = nextBounds.getMaxY();
        ym = y4 - (y3 - y2);
        assert y1 <= ym;
    }

    public GridPane getSceneGraphObject() {
        return gridPane;
    }

    public int getColumnIndex() {
        return rowIndex;
    }
    
    public void updateHeight(double dy) {
        
        // Clamp y2 + dy in [y1, ym]
        final double newY2 = Math.max(y1, Math.min(ym, y2 + dy));
        final double newY3 = newY2 + (y3 - y2);
        final double newCellHeight = newY2 - y1;
        final double newNextHeight = y4 - newY3;
        
//        assert (newCellWidth+newNextWidth) == (downColWidths[colIndex]+downColWidths[colIndex+1]) :
//                "newCellWidth+newNextWidth=" +  (newCellWidth+newNextWidth) + ", " +
//                "downColWidths[colIndex]+downColWidths[colIndex+1]=" + 
//                (downColWidths[colIndex]+downColWidths[colIndex+1]);

        // Updates height of rows at rowIndex and rowIndex+1
        final RowConstraints rc = gridPane.getRowConstraints().get(rowIndex);
        final RowConstraints rcNext = gridPane.getRowConstraints().get(rowIndex+1);
        
        if (usePercentSizing) {
            final double ratio = newCellHeight / (ym - y1);
            
            final double base 
                    = originalSizing.getPercentHeight() 
                    + originalSizingNext.getPercentHeight();
            
            final double newPercentHeight = Math.floor(ratio * base);
            final double newPercentHeightNext = base - newPercentHeight;
            
            rc.setPercentHeight(newPercentHeight);
            rcNext.setPercentHeight(newPercentHeightNext);
            
        } else {
            
            // Row at rowIndex
            rc.setPrefHeight(newCellHeight);
            if (rc.getMinHeight() == Region.USE_COMPUTED_SIZE) {
                rc.setMinHeight(newCellHeight);
            } else {
                rc.setMinHeight(Math.min(newCellHeight, rc.getMinHeight()));
            }
            if (rc.getMaxHeight() == Region.USE_COMPUTED_SIZE) {
                rc.setMaxHeight(newCellHeight);
            } else {
                rc.setMaxHeight(Math.max(newCellHeight, rc.getMaxHeight()));
            }
            
            // Row at rowIndex+1
            rcNext.setPrefHeight(newNextHeight);
            if (rcNext.getMinHeight() == Region.USE_COMPUTED_SIZE) {
                rcNext.setMinHeight(newNextHeight);
            } else {
                rcNext.setMinHeight(Math.min(newNextHeight, rcNext.getMinHeight()));
            }
            if (rcNext.getMaxHeight() == Region.USE_COMPUTED_SIZE) {
                rcNext.setMaxHeight(newNextHeight);
            } else {
                rcNext.setMaxHeight(Math.max(newNextHeight, rcNext.getMaxHeight()));
            }
        }
        
    }
    
    public void revertToOriginalSize() {
        // Restore sizing of rows at rowIndex and rowIndex+1
        final RowConstraints cc = gridPane.getRowConstraints().get(rowIndex);
        final RowConstraints ccNext = gridPane.getRowConstraints().get(rowIndex+1);
        
        originalSizing.applyTo(cc);
        originalSizingNext.applyTo(ccNext);
    }
    
    
    public Map<PropertyName, Object> getChangeMap() {
        final Map<PropertyName, Object> result = new HashMap<>();
        
        final RowConstraints cc = gridPane.getRowConstraints().get(rowIndex);
        if (MathUtils.equals(cc.getMinHeight(), originalSizing.getMinHeight()) == false) {
            result.put(minHeightName, cc.getMinHeight());
        }
        if (MathUtils.equals(cc.getPrefHeight(), originalSizing.getPrefHeight()) == false) {
            result.put(prefHeightName, cc.getPrefHeight());
        }
        if (MathUtils.equals(cc.getMaxHeight(), originalSizing.getMaxHeight()) == false) {
            result.put(maxHeightName, cc.getMaxHeight());
        }
        if (MathUtils.equals(cc.getPercentHeight(), originalSizing.getPercentHeight()) == false) {
            result.put(percentHeightName, cc.getPercentHeight());
        }
        return result;
    }
    
    
    public Map<PropertyName, Object> getChangeMapNext() {
        final Map<PropertyName, Object> result = new HashMap<>();
        
        final RowConstraints ccNext = gridPane.getRowConstraints().get(rowIndex+1);
        if (MathUtils.equals(ccNext.getMinHeight(), originalSizingNext.getMinHeight()) == false) {
            result.put(minHeightName, ccNext.getMinHeight());
        }
        if (MathUtils.equals(ccNext.getPrefHeight(), originalSizingNext.getPrefHeight()) == false) {
            result.put(prefHeightName, ccNext.getPrefHeight());
        }
        if (MathUtils.equals(ccNext.getMaxHeight(), originalSizingNext.getMaxHeight()) == false) {
            result.put(maxHeightName, ccNext.getMaxHeight());
        }
        if (MathUtils.equals(ccNext.getPercentHeight(), originalSizingNext.getPercentHeight()) == false) {
            result.put(percentHeightName, ccNext.getPercentHeight());
        }
        return result;
    }
    
    
    /*
     * Private
     */    
    
    private int countPercentWidths() {
        int result = 0;
        for (RowConstraints cc : gridPane.getRowConstraints()) {
            if (cc.getPercentHeight() != -1) {
                result++;
            }
        }
        return result;
    }
    
    
    private static class RowSizing {
        private final double minHeight;
        private final double maxHeight;
        private final double prefHeight;
        private final double percentHeight;
        
        public RowSizing(RowConstraints cc) {
            this.minHeight = cc.getMinHeight();
            this.maxHeight = cc.getMaxHeight();
            this.prefHeight = cc.getPrefHeight();
            this.percentHeight = cc.getPercentHeight();
        }

        public double getMinHeight() {
            return minHeight;
        }

        public double getMaxHeight() {
            return maxHeight;
        }

        public double getPrefHeight() {
            return prefHeight;
        }

        public double getPercentHeight() {
            return percentHeight;
        }

        public void applyTo(RowConstraints cc) {
            cc.setMinHeight(minHeight);
            cc.setMaxHeight(maxHeight);
            cc.setPrefHeight(prefHeight);
            cc.setPercentHeight(percentHeight);
        }
    }
}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/metadata/property/value/RowConstraintsPropertyMetadata.java<|end_filename|>
/*
 * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.kit.metadata.property.value;

import com.oracle.javafx.scenebuilder.kit.fxom.FXOMDocument;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMInstance;
import com.oracle.javafx.scenebuilder.kit.metadata.util.InspectorPath;
import com.oracle.javafx.scenebuilder.kit.metadata.util.PropertyName;
import com.oracle.javafx.scenebuilder.kit.util.MathUtils;
import java.util.Objects;
import javafx.geometry.VPos;
import javafx.scene.layout.Priority;
import javafx.scene.layout.RowConstraints;

/**
 *
 */
public class RowConstraintsPropertyMetadata extends ComplexPropertyMetadata<RowConstraints> {
    
    private static final RowConstraints DEFAULT = new RowConstraints();

    private final BooleanPropertyMetadata fillHeightMetadata
            = new BooleanPropertyMetadata(new PropertyName("fillHeight"), 
                    true, DEFAULT.isFillHeight(), InspectorPath.UNUSED);
    private final DoublePropertyMetadata maxHeightMetadata
            = new DoublePropertyMetadata(new PropertyName("maxHeight"), 
                    DoublePropertyMetadata.DoubleKind.USE_COMPUTED_SIZE, true,
                    DEFAULT.getMaxHeight(), InspectorPath.UNUSED);
    private final DoublePropertyMetadata minHeightMetadata
            = new DoublePropertyMetadata(new PropertyName("minHeight"), 
            DoublePropertyMetadata.DoubleKind.USE_COMPUTED_SIZE, true, 
                    DEFAULT.getMinHeight(), InspectorPath.UNUSED);
    private final DoublePropertyMetadata percentHeightMetadata
            = new DoublePropertyMetadata(new PropertyName("percentHeight"), 
            DoublePropertyMetadata.DoubleKind.PERCENTAGE, true, 
                    DEFAULT.getPercentHeight(), InspectorPath.UNUSED);
    private final DoublePropertyMetadata prefHeightMetadata
            = new DoublePropertyMetadata(new PropertyName("prefHeight"), 
            DoublePropertyMetadata.DoubleKind.USE_PREF_SIZE, true, 
                    DEFAULT.getPrefHeight(), InspectorPath.UNUSED);
    private final EnumerationPropertyMetadata valignmentMetadata
            = new EnumerationPropertyMetadata(new PropertyName("valignment"),
            VPos.class, EnumerationPropertyMetadata.EQUIV_INHERITED, true, InspectorPath.UNUSED);
    private final EnumerationPropertyMetadata vgrowMetadata
            = new EnumerationPropertyMetadata(new PropertyName("vgrow"),
            Priority.class, EnumerationPropertyMetadata.EQUIV_INHERITED, true, InspectorPath.UNUSED);
    
    public RowConstraintsPropertyMetadata(PropertyName name, boolean readWrite, 
            RowConstraints defaultValue, InspectorPath inspectorPath) {
        super(name, RowConstraints.class, readWrite, defaultValue, inspectorPath);
    }

    /*
     * Utility
     */
    
    public static boolean equals(RowConstraints r1, RowConstraints r2) {
        assert r1 != null;
        assert r2 != null;

        final boolean result;
        if (r1 == r2) {
            result = true;
        } else {
            result = Objects.equals(r1.getValignment(),r2.getValignment())
                    && Objects.equals(r1.getVgrow(), r2.getVgrow())
                    && MathUtils.equals(r1.getMaxHeight(), r2.getMaxHeight())
                    && MathUtils.equals(r1.getMinHeight(), r2.getMinHeight())
                    && MathUtils.equals(r1.getPercentHeight(), r2.getPercentHeight())
                    && MathUtils.equals(r1.getPrefHeight(), r2.getPrefHeight());
        }
        
        return result;
    }
    
    /*
     * ComplexPropertyMetadata
     */
    
    @Override
    public FXOMInstance makeFxomInstanceFromValue(RowConstraints value, FXOMDocument fxomDocument) {
        final FXOMInstance result = new FXOMInstance(fxomDocument, getValueClass());
        
        fillHeightMetadata.setValue(result, value.isFillHeight());
        maxHeightMetadata.setValue(result, value.getMaxHeight());
        minHeightMetadata.setValue(result, value.getMinHeight());
        percentHeightMetadata.setValue(result, value.getPercentHeight());
        prefHeightMetadata.setValue(result, value.getPrefHeight());
        
        final VPos valignment = value.getValignment();
        if (valignment == null) {
            valignmentMetadata.setValue(result, valignmentMetadata.getDefaultValue());
        } else {
            valignmentMetadata.setValue(result, valignment.toString());
        }
        final Priority vgrow = value.getVgrow();
        if (vgrow == null) {
            vgrowMetadata.setValue(result, vgrowMetadata.getDefaultValue());
        } else {
            vgrowMetadata.setValue(result, vgrow.toString());
        }

        return result;
    }
    
}


<|start_filename|>kit/src/test/java/com/oracle/javafx/scenebuilder/kit/fxom/AddIntrinsicPropertyTest.java<|end_filename|>
/*
 * Copyright (c) 2016, 2017 Gluon and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation and Gluon nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.kit.fxom;

import com.oracle.javafx.scenebuilder.kit.metadata.util.PropertyName;
import org.junit.BeforeClass;
import org.junit.Test;

import static org.assertj.core.api.Assertions.assertThat;

/**
 * Unit test for {@link FXOMIntrinsic#addIntrinsicProperty(FXOMDocument)}
 */
public class AddIntrinsicPropertyTest {

    private static final String CHARSET_PROPERTY_NAME = "charset";
    private static final String SOURCE_PROPERTY_NAME = "source";

    private static FXOMIntrinsic fxomIntrinsic;
    private static FXOMDocument fxomDocument;
    private static PropertyName charsetPropertyName;
    private static PropertyName sourcePropertyName;

    @BeforeClass
    public static void initialize() {
        prepareTestData();
    }

    private static void prepareTestData() {
        final String sourceFile = "test_include.fxml";
        final String exampleCharset = "UTF-8";
        fxomDocument = new FXOMDocument();
        fxomIntrinsic = new FXOMIntrinsic(fxomDocument, FXOMIntrinsic.Type.FX_INCLUDE, sourceFile);
        charsetPropertyName = new PropertyName(CHARSET_PROPERTY_NAME);
        sourcePropertyName = new PropertyName(SOURCE_PROPERTY_NAME);
        fxomIntrinsic.getGlueElement().getAttributes().put(CHARSET_PROPERTY_NAME, exampleCharset);
    }

    private void callService() {
        fxomIntrinsic.addIntrinsicProperty(fxomDocument);
    }

    @Test
    public void testAddCharsetProperty() {
        callService();
        FXOMProperty fxomProperty = fxomIntrinsic.getProperties().get(charsetPropertyName);
        assertThat(fxomProperty).isNotNull();
        assertThat(fxomProperty.getName()).isEqualTo(charsetPropertyName);
    }

    @Test
    public void testAddSourceProperty() {
        callService();
        FXOMProperty fxomProperty = fxomIntrinsic.getProperties().get(sourcePropertyName);
        assertThat(fxomProperty).isNotNull();
        assertThat(fxomProperty.getName()).isEqualTo(sourcePropertyName);
    }
}

<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/fxom/FXOMCloner.java<|end_filename|>
/*
 * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */

package com.oracle.javafx.scenebuilder.kit.fxom;

import com.oracle.javafx.scenebuilder.kit.metadata.util.PrefixedValue;
import com.oracle.javafx.scenebuilder.kit.metadata.util.PropertyName;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 *
 */
public class FXOMCloner {
    
    private final FXOMDocument targetDocument;
    private final FxIdCollector fxIdCollector;
    private FXOMObject clonee;
    private final Set<String> addedFxIds = new HashSet<>();
    
    public FXOMCloner(FXOMDocument targetDocument) {
        assert targetDocument != null;
        this.targetDocument = targetDocument;
        this.fxIdCollector = new FxIdCollector(targetDocument);
    }
    
    public FXOMDocument getTargetDocument() {
        return targetDocument;
    }
    
    public FXOMObject clone(FXOMObject clonee) {
        return clone(clonee, false /* preserveCloneFxId */);
    }
    
    public FXOMObject clone(FXOMObject clonee, boolean preserveCloneeFxId) {
        assert clonee != null;
        assert addedFxIds.isEmpty();
        
        this.clonee = clonee;
        
        // Creates a deep clone of 'clonee'
        final FXOMObject result = cloneObject(this.clonee);
        addedFxIds.clear();
        
        // Renames fxid in the clone so that there is no naming
        // conflict when the clone is hooked to its target document.
        renameFxIds(result, preserveCloneeFxId);
        
        return result;
    }
    
    
    /*
     * Private
     */
    
    private FXOMObject cloneObject(FXOMObject fxomObject) {
        final FXOMObject result;
        
        if (fxomObject instanceof FXOMCollection) {
            result = cloneCollection((FXOMCollection) fxomObject);
        } else if (fxomObject instanceof FXOMInstance) {
            result = cloneInstance((FXOMInstance) fxomObject);
        } else if (fxomObject instanceof FXOMIntrinsic) {
            result = cloneIntrinsic((FXOMIntrinsic) fxomObject);
        } else {
            throw new RuntimeException(getClass().getSimpleName()
                    + " needs some additional implementation"); //NOI18N
        }
        
        return result;
    }
    
    
    private FXOMCollection cloneCollection(FXOMCollection source) {
        assert source != null;
        
        final FXOMCollection result = new FXOMCollection(
                targetDocument,
                source.getDeclaredClass());
        
        for (FXOMObject sourceItem : source.getItems()) {
            final FXOMObject newItem = cloneObject(sourceItem);
            newItem.addToParentCollection(-1, result);
        }
        
        result.setFxConstant(source.getFxConstant());
        result.setFxController(source.getFxController());
        result.setFxFactory(source.getFxFactory());
        result.setFxId(source.getFxId());
        result.setFxValue(source.getFxValue());
        
        return result;
    }
    
    
    private FXOMInstance cloneInstance(FXOMInstance source) {
        
        assert source != null;
        
        final FXOMInstance result;
        if (source.getDeclaredClass() == null) {
            assert source.getSceneGraphObject() == null; // source is unresolved
            result = new FXOMInstance(
                    targetDocument,
                    source.getGlueElement().getTagName());
        } else {
            result = new FXOMInstance(
                    targetDocument,
                    source.getDeclaredClass());
        }
        
        for (Map.Entry<PropertyName, FXOMProperty> e : source.getProperties().entrySet()) {
            final FXOMProperty newProperty = cloneProperty(e.getValue());
            // Note: cloneProperty() may 
            if (newProperty != null) {
                newProperty.addToParentInstance(-1, result);
            }
        }
        
        result.setFxConstant(source.getFxConstant());
        result.setFxController(source.getFxController());
        result.setFxFactory(source.getFxFactory());
        result.setFxId(source.getFxId());
        result.setFxValue(source.getFxValue());
        
        return result;
    }
    
    
    private FXOMObject cloneIntrinsic(FXOMIntrinsic source) {
        assert source != null;
        
        final boolean shallowClone;
        final FXOMObject sourceObject;
        switch(source.getType()) {
            case FX_REFERENCE:
            case FX_COPY:
                final String sourceFxId = source.getSource();
                assert sourceFxId != null;
                sourceObject = clonee.getFxomDocument().searchWithFxId(sourceFxId);
                if (isInsideClonee(sourceObject) || addedFxIds.contains(sourceFxId)) {
                    shallowClone = true;
                } else {
                    shallowClone = false;
                    addedFxIds.add(sourceFxId);
                }
                break;
            default:
                sourceObject = null;
                shallowClone = true;
                break;
        }
        
        final FXOMObject result;
        if (shallowClone) {
            // We clone the intrinsic itself
            result = new FXOMIntrinsic(
                    targetDocument,
                    source.getType(),
                    source.getSource());

            result.setFxConstant(source.getFxConstant());
            result.setFxController(source.getFxController());
            result.setFxFactory(source.getFxFactory());
            result.setFxId(source.getFxId());
            result.setFxValue(source.getFxValue());
        }
        else {
            assert sourceObject != null;
            
            // We clone the target of the intrinsic
            result = cloneObject(sourceObject);
        }
        
        
        return result;
    }
    
    
    private FXOMProperty cloneProperty(FXOMProperty source) {
        final FXOMProperty result;
        
        if (source instanceof FXOMPropertyC) {
            result = clonePropertyC((FXOMPropertyC) source);
        } else if (source instanceof FXOMPropertyT) {
            result = clonePropertyT((FXOMPropertyT) source);
        } else {
            throw new RuntimeException(getClass().getSimpleName()
                    + " needs some additional implementation"); //NOI18N
        }
        
        return result;
    }

    public FXOMPropertyC clonePropertyC(FXOMPropertyC source) {
        assert source != null;
        
        final FXOMPropertyC result = new FXOMPropertyC(
                targetDocument,
                source.getName());
        
        for (FXOMObject sourceValue : source.getValues()) {
            final FXOMObject newValue = cloneObject(sourceValue);
            newValue.addToParentProperty(-1, result);
        }
        
        return result;
    }
    
    public FXOMProperty clonePropertyT(FXOMPropertyT source) {
        assert source != null;
        
        final boolean shallowClone;
        final FXOMObject sourceObject;
        final PrefixedValue pv = new PrefixedValue(source.getValue());
        if (pv.isExpression()) {
            final String sourceFxId = pv.getSuffix();
            assert sourceFxId != null;
            sourceObject = clonee.getFxomDocument().searchWithFxId(sourceFxId);
            assert sourceObject != null : "sourceFxId=" + sourceFxId;
            if (isInsideClonee(sourceObject) || addedFxIds.contains(sourceFxId)) {
                shallowClone = true;
            } else {
                shallowClone = false;
                addedFxIds.add(sourceFxId);
            }
        } else {
            shallowClone = true;
            sourceObject = null;
        }
        
        final FXOMProperty result;
        if (shallowClone) {
            result = new FXOMPropertyT(
                    targetDocument,
                    source.getName(),
                    source.getValue());
        } else {
            assert sourceObject != null;
            if (FXOMNodes.isWeakReference(source)) {
                result = null;
            } else {
                result = new FXOMPropertyC(
                        targetDocument,
                        source.getName(),
                        cloneObject(sourceObject));
            }
        }
        
        return result;
    }
    
    private boolean isInsideClonee(FXOMObject object) {
        assert object != null;
        return (object == clonee) || object.isDescendantOf(clonee);
    }    
    
    private void renameFxIds(FXOMObject clone, boolean preserveCloneeFxId) {
        
        final Map<String, FXOMObject> fxIds = clone.collectFxIds();
        
        if (preserveCloneeFxId && (clonee.getFxId() != null)) {
            // We don't apply renaming to the fx:id of the clonee
            assert clonee.getFxId().equals(clone.getFxId());
            assert fxIds.get(clonee.getFxId()) == clone;
            fxIds.remove(clonee.getFxId());
        }
        
        for (Map.Entry<String, FXOMObject> e : fxIds.entrySet()) {
            final String candidateFxId = e.getKey();
            final FXOMObject declarer = e.getValue();
            
            final String renamedFxId = fxIdCollector.importFxId(candidateFxId);
            
            if (renamedFxId.equals(candidateFxId) == false) {
                
                /*
                 * We renamed candidateFxId as renamedFxId 
                 *  1) on the declarer object
                 *  2) in each fx:reference/fx:copy object
                 *  3) in each xxx="$candidateFxId" expression property //NOI18N
                 */
                
                // 1)
                declarer.setFxId(renamedFxId);

                // 2)
                for (FXOMIntrinsic reference : clone.collectReferences(candidateFxId)) {
                    assert reference.getSource().equals(candidateFxId);
                    reference.setSource(renamedFxId);
                }

                // 3)
                final PrefixedValue pv = new PrefixedValue(PrefixedValue.Type.EXPRESSION, renamedFxId);
                final String newValue = pv.toString();
                for (FXOMPropertyT reference : FXOMNodes.collectReferenceExpression(clone, candidateFxId)) {
                    reference.setValue(newValue);
                }
            }
        }
        
    }
}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/drag/source/LibraryDragSource.java<|end_filename|>
/*
 * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.kit.editor.drag.source;

import java.net.URL;
import java.util.ArrayList;
import java.util.List;

import javafx.geometry.Bounds;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.Label;
import javafx.scene.control.Tooltip;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.ClipboardContent;
import javafx.stage.Window;

import com.oracle.javafx.scenebuilder.kit.editor.EditorController;
import com.oracle.javafx.scenebuilder.kit.i18n.I18N;
import com.oracle.javafx.scenebuilder.kit.editor.images.ImageUtils;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMDocument;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMInstance;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMObject;
import com.oracle.javafx.scenebuilder.kit.library.LibraryItem;

/**
 *
 */
public class LibraryDragSource extends AbstractDragSource {
    
    private final LibraryItem libraryItem;
    private final FXOMDocument targetDocument;
    private FXOMObject libraryItemObject; // Populated lazily
    private List<FXOMObject> draggedObjects; // Opmization

    public LibraryDragSource(LibraryItem libraryItem, FXOMDocument targetDocument, Window ownerWindow) {
        super(ownerWindow);
        
        assert libraryItem != null;
        assert targetDocument != null;
        
        this.libraryItem = libraryItem;
        this.targetDocument = targetDocument;
    }

    public LibraryItem getLibraryItem() {
        return libraryItem;
    }
    
    public FXOMObject getLibraryItemObject() {
        if (libraryItemObject == null) {
            final FXOMDocument itemDocument = libraryItem.instantiate();
            assert itemDocument != null;
            assert itemDocument.getFxomRoot() != null;
            libraryItemObject = itemDocument.getFxomRoot();
            libraryItemObject.moveToFxomDocument(targetDocument);
            assert itemDocument.getFxomRoot() == null;
            
            if (libraryItemObject.getSceneGraphObject() instanceof Node) {
                // We put the library item node in a Scene and layout it.
                // This will allow ContainerXYDropTarget to measure this
                // library item by calling Node.getLayoutBounds().
                final Node sceneGraphNode = (Node) libraryItemObject.getSceneGraphObject();
                final Group group = new Group();
                group.getChildren().add(sceneGraphNode);
                final Scene scene = new Scene(group); // Not used but required
                scene.getClass(); // used to dummy thing to silence FindBugs
                group.applyCss();
                group.layout();
            }
        }
        
        return libraryItemObject;
    }
    
    /*
     * AbstractDragSource
     */
    
    @Override
    public boolean isAcceptable() {
        // All library drag sources are 'acceptable'
        return true;
    }

    
    @Override
    public List<FXOMObject> getDraggedObjects() {
        if (draggedObjects == null) {
            draggedObjects = new ArrayList<>();
            draggedObjects.add(getLibraryItemObject());
        }
        
        return draggedObjects;
    }
    
    @Override
    public FXOMObject getHitObject() {
        return getDraggedObjects().get(0);
    }
    
    @Override
    public double getHitX() {
        final double result;
        
        final FXOMObject hitObject = getHitObject();
        if (hitObject == null) {
            result = Double.NaN;
        } else if (hitObject.isNode()) {
            final Node hitNode = (Node) hitObject.getSceneGraphObject();
            final Bounds b = hitNode.getLayoutBounds();
            result = (b.getMinX() + b.getMaxX()) / 2.0;
        } else {
            result = 0.0;
        }
        
        return result;
    }

    @Override
    public double getHitY() {
        final double result;
        
        final FXOMObject hitObject = getHitObject();
        if (hitObject == null) {
            result = Double.NaN;
        } else if (hitObject.isNode()) {
            final Node hitNode = (Node) hitObject.getSceneGraphObject();
            final Bounds b = hitNode.getLayoutBounds();
            result = (b.getMinY() + b.getMaxY()) / 2.0;
        } else {
            result = 0.0;
        }
        
        return result;
    }

    @Override
    public ClipboardContent makeClipboardContent() {
        final ClipboardContent result = new ClipboardContent();
        
        // Add to content a string which is the Lib Item as an FXML string
        result.putString(libraryItem.getFxmlText());
        
        return result;
    }

    @Override
    public Image makeDragView() {
        // We construct an image made of a Label that reads the class name
        // of the Library Item, and set as Label graphic the appropriate icon.
        URL iconURL = libraryItem.getIconURL();

        if (iconURL == null) {
            iconURL = ImageUtils.getNodeIconURL("MissingIcon.png"); //NOI18N
        }

        final Image imageFromIcon = new Image(iconURL.toExternalForm());
//        final Label visualNode = new Label(libraryItem.getName());
        final Label visualNode = new Label();
        visualNode.setGraphic(new ImageView(imageFromIcon));
        visualNode.getStylesheets().add(EditorController.getStylesheet().toString());
        visualNode.getStyleClass().add("drag-preview"); //NOI18N
        
        return ImageUtils.getImageFromNode(visualNode);
    }

    @Override
    public Node makeShadow() {
        final Group result = new Group();
        
        result.getStylesheets().add(EditorController.getStylesheet().toString());

        if (getLibraryItemObject().getSceneGraphObject() instanceof Node) {
            final Node sceneGraphNode = (Node) getLibraryItemObject().getSceneGraphObject();
            final DragSourceShadow shadowNode = new DragSourceShadow();
            shadowNode.setupForNode(sceneGraphNode);
            result.getChildren().add(shadowNode);
        }
        
        // Translate the group so that it is centered above (layoutX, layoutY)
        final Bounds b = result.getBoundsInParent();
        final double centerX = (b.getMinX() + b.getMaxX()) / 2.0;
        final double centerY = (b.getMinY() + b.getMaxY()) / 2.0;
        result.setTranslateX(-centerX);
        result.setTranslateY(-centerY);
        
        return result;
    }
    
    @Override
    public String makeDropJobDescription() {
        return I18N.getString("drop.job.insert.library.item",
                getLibraryItem().getName());
    }

    @Override
    public boolean isNodeOnly() {
        return getLibraryItemObject().isNode();
    }

    @Override
    public boolean isSingleImageViewOnly() {
        final boolean result;
        
        if (getLibraryItemObject() instanceof FXOMInstance) {
            result = getLibraryItemObject().getSceneGraphObject() instanceof ImageView;
        } else {
            result = false;
        }
        
        return result;
    }

    @Override
    public boolean isSingleTooltipOnly() {
        final boolean result;
        
        if (getLibraryItemObject() instanceof FXOMInstance) {
            result = getLibraryItemObject().getSceneGraphObject() instanceof Tooltip;
        } else {
            result = false;
        }
        
        return result;
    }

    @Override
    public boolean isSingleContextMenuOnly() {
        final boolean result;
        
        if (getLibraryItemObject() instanceof FXOMInstance) {
            result = getLibraryItemObject().getSceneGraphObject() instanceof ContextMenu;
        } else {
            result = false;
        }
        
        return result;
    }
    
    
    
    /*
     * Object
     */
    
    @Override
    public String toString() {
        return getClass().getSimpleName() + ": libraryItem=(" + libraryItem + ")"; //NOI18N
    }

}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/inspector/editors/util/SBDuration.java<|end_filename|>
/*
 * Copyright (c) 2016, Gluon and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation and Gluon nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */

package com.oracle.javafx.scenebuilder.kit.editor.panel.inspector.editors.util;

import javafx.beans.NamedArg;
import javafx.util.Duration;

public class SBDuration extends Duration {

    /**
     * Creates a new SBDuration with potentially fractional millisecond resolution.
     *
     * @param millis The number of milliseconds
     */
    public SBDuration(@NamedArg("millis") double millis) {
        super(millis);
    }

    /**
     * Creates a new SBDuration instance from a Duration object.
     * @param duration
     */
    public SBDuration(Duration duration){
        super(duration.toMillis());
    }

    @Override
    public String toString() {
        return isIndefinite() ? "INDEFINITE" : (isUnknown() ? "UNKNOWN" : this.toMillis() + "ms");
    }

    /**
     * Factory method that returns a Duration instance for a specified
     * amount of time. The syntax is "[number][ms|s|m|h]".
     *
     * @param time A non-null string properly formatted. Leading or trailing
     * spaces will not parse correctly. Throws a NullPointerException if
     * time is null.
     * @return a Duration which is represented by the <code>time</code>
     */
    public static SBDuration valueOf(String time) {
        int index = -1;
        for (int i=0; i<time.length(); i++) {
            char c = time.charAt(i);
            if (!Character.isDigit(c) && c != '.' && c != '-' && c!='E') {
                index = i;
                break;
            }
        }

        String suffix;
        double value;
        if (index == -1) {
            value = Double.parseDouble(time);
            // Never found the suffix!
            suffix = "ms";
        } else {
            value = Double.parseDouble(time.substring(0, index));
            suffix = time.substring(index);
        }
        if ("ms".equals(suffix)) {
            return new SBDuration(millis(value));
        } else if ("s".equals(suffix)) {
            return new SBDuration(seconds(value));
        } else if ("m".equals(suffix)) {
            return new SBDuration(minutes(value));
        } else if ("h".equals(suffix)) {
            return new SBDuration(hours(value));
        } else {
            // Malformed suffix
            throw new IllegalArgumentException("The time parameter must have a suffix of [ms|s|m|h]");
        }

    }
}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/hierarchy/HierarchyItem.java<|end_filename|>
/*
 * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.kit.editor.panel.hierarchy;

import com.oracle.javafx.scenebuilder.kit.editor.panel.hierarchy.AbstractHierarchyPanelController.DisplayOption;
import static com.oracle.javafx.scenebuilder.kit.editor.panel.hierarchy.AbstractHierarchyPanelController.DisplayOption.FXID;
import static com.oracle.javafx.scenebuilder.kit.editor.panel.hierarchy.AbstractHierarchyPanelController.DisplayOption.INFO;
import static com.oracle.javafx.scenebuilder.kit.editor.panel.hierarchy.AbstractHierarchyPanelController.DisplayOption.NODEID;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMObject;
import com.oracle.javafx.scenebuilder.kit.metadata.util.DesignHierarchyMask;
import com.oracle.javafx.scenebuilder.kit.metadata.util.DesignHierarchyMask.Accessory;
import com.oracle.javafx.scenebuilder.kit.metadata.util.PropertyName;
import java.net.URL;
import java.util.List;
import java.util.Objects;
import javafx.scene.image.Image;

/**
 * Object representing the data contained within the hierarchy TreeItems.
 *
 * @treatAsPrivate
 */
public class HierarchyItem {

    protected DesignHierarchyMask mask;

    /**
     * Creates a hierarchy item. Empty constructor used by the HierarchyItem
     * subclasses.
     */
    public HierarchyItem() {
    }

    /**
     * Creates a hierarchy item.
     *
     * @param fxomObject The FX object represented by this item
     */
    public HierarchyItem(final FXOMObject fxomObject) {
        assert fxomObject != null;
        this.mask = new DesignHierarchyMask(fxomObject);
    }

    /**
     * Returns true if the specified object is a HierarchyItem and it defines
     * same non null FXOMObject.
     * This method is used to retrieve the cell corresponding to this item.
     *
     * @param obj the reference object with which to compare
     * @return true if this object is the same as the obj argument, false otherwise.
     */
    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final HierarchyItem item = (HierarchyItem) obj;
        // equals method is overidden in the place holder sub classes.
        assert getFxomObject() != null;
        return getFxomObject().equals(item.getFxomObject());
    }

    @Override
    public int hashCode() {
        int hash = 5;
        hash = 79 * hash + Objects.hashCode(this.mask);
        return hash;
    }

    /**
     * Returns the mask represented by this item.
     *
     * @return the mask represented by this item.
     */
    public final DesignHierarchyMask getMask() {
        return mask;
    }

    /**
     * Returns the FX object represented by this item.
     *
     * @return the FX object represented by this item.
     */
    public final FXOMObject getFxomObject() {
        // Can be null for place holder items
        return mask == null ? null : mask.getFxomObject();
    }

    /**
     * Returns the information of the FX object represented by this item.
     *
     * @return the information of the FX object represented by this item.
     */
    public String getDescription() {
        // Can be null for place holder items
        return mask == null ? null : mask.getDescription();
    }

    /**
     * Returns the single line information of the FX object represented by this
     * item.
     *
     * @return the information of the FX object represented by this item.
     */
    public String getSingleLineDescription() {
        // Can be null for place holder items
        return mask == null ? null : mask.getSingleLineDescription();
    }

    /**
     * Returns the node ID of the FX object represented by this item.
     *
     * @return the node ID of the FX object represented by this item.
     */
    public String getNodeId() {
        // Can be null for place holder items
        return mask == null ? null : mask.getNodeId();
    }

    /**
     * Returns the FX ID of the FX object represented by this item.
     *
     * @return the FX ID of the FX object represented by this item.
     */
    public String getFxId() {
        // Can be null for place holder items
        return mask == null ? null : mask.getFxId();
    }

    public String getDisplayInfo(final DisplayOption option) {
        // Place holder items do not have display info
        if (mask == null) {
            return null;
        }
        final Object sceneGraphObject = mask.getFxomObject().getSceneGraphObject();
        if (sceneGraphObject == null) {
            // For now, handle display label for scenegraph objects only
            return null;
        }
        String info = null;
        switch (option) {
            case INFO:
                info = getSingleLineDescription();
                break;
            case FXID:
                info = getFxId();
                break;
            case NODEID:
                info = getNodeId();
                break;
        }
        return info;
    }

    public PropertyName getPropertyNameForDisplayInfo(final DisplayOption option) {
        assert mask != null;
        PropertyName propertyName = null;
        switch (option) {
            case INFO:
                propertyName = mask.getPropertyNameForDescription();
                break;
            case NODEID:
                propertyName = new PropertyName("id");
                break;
            default:
                assert false;
        }
        return propertyName;
    }

    public boolean isResourceKey(final DisplayOption option) {
        // Place holder items do not have display info
        if (mask == null) {
            return false;
        }
        return option == INFO && mask.isResourceKey();
    }

    public boolean isPlaceHolder() {
        return false;
    }

    public boolean isEmpty() {
        return false;
    }

    public boolean isAcceptingSubComponent(final List<FXOMObject> fxomObjects) {
        return !isEmpty() && mask.isAcceptingSubComponent(fxomObjects);
    }

    public boolean isAcceptingAccessory(Accessory accessory, FXOMObject fxomObject) {
        return !isEmpty()
                && mask.isAcceptingAccessory(accessory, fxomObject)
                && mask.getAccessory(accessory) == null;
    }

    public boolean hasDisplayInfo(final DisplayOption option) {
        // Item has display info if we are not on place holder and :
        // - either we display the FX ID
        // - or we display the node ID
        // - or we display the description and the item defines one
        return mask != null
                && (option == FXID
                || option == NODEID
                || (option == INFO && mask.hasDescription()));
    }

    public Image getPlaceHolderImage() {
        // No place holder
        return null;
    }

    public String getPlaceHolderInfo() {
        // No place holder
        return null;
    }

    public Image getClassNameIcon() {
        assert mask != null;
        return mask.getClassNameIcon();
    }

    public URL getClassNameIconURL() {
        assert mask != null;
        return mask.getClassNameIconURL();
    }

    public String getClassNameInfo() {
        assert mask != null;
        return mask.getClassNameInfo();
    }
}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/fxom/FXOMNormalizer.java<|end_filename|>
/*
 * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.kit.fxom;

import com.oracle.javafx.scenebuilder.kit.metadata.property.value.list.ColumnConstraintsListPropertyMetadata;
import com.oracle.javafx.scenebuilder.kit.metadata.property.value.list.RowConstraintsListPropertyMetadata;
import com.oracle.javafx.scenebuilder.kit.metadata.util.PropertyName;
import com.oracle.javafx.scenebuilder.kit.util.Deprecation;
import java.util.List;
import javafx.scene.control.TitledPane;
import javafx.scene.layout.GridPane;

/**
 *
 * 
 */
class FXOMNormalizer {
    
    /*
     * We look for properties with $null values and remove them.
     * Exemple with ScrollPane.content.
     *
     *      <ScrollPane content="$null" ... />
     *
     * is transformed in:
     *
     *      <ScrollPane ... />
     *
     */
    
    /*
     * 
     * We look for the following pattern:
     * 
     * <Accordion>
     *   <expandedPane>
     *     <TitledPane fx:id="x1" text="B">
     *       ...
     *     </TitledPane>
     *   </expandedPane>
     *   <panes>
     *     <TitledPane text="A">
     *       ...
     *     </TitledPane>
     *     <fx:reference source="x1" />
     *   </panes>
     * </Accordion>
     * 
     * 
     * and transform it as:
     * 
     * <Accordion>
     *   <panes>
     *     <TitledPane text="A">
     *       ...
     *     </TitledPane>
     *     <TitledPane text="B">
     *       ...
     *     </TitledPane>
     *   </panes>
     * </Accordion>
     * 
     * 
     */
    
    private static final PropertyName expandedPaneName
            = new PropertyName("expandedPane");
    
    private final FXOMDocument fxomDocument;
    private int changeCount;
    
    public FXOMNormalizer(FXOMDocument fxomDocument) {
        this.fxomDocument = fxomDocument;
    }
    
    public void normalize() {
        changeCount = 0;
        normalizeExpandedPaneProperties();
        normalizeGridPanes();
        if (changeCount >= 1) {
            fxomDocument.refreshSceneGraph();
        }
    }
    
    public void normalizeExpandedPaneProperties() {
        
        final List<FXOMProperty> expandedPaneProperties
                = fxomDocument.getFxomRoot().collectProperties(expandedPaneName);
        
        for (FXOMProperty p : expandedPaneProperties) {
            if (p instanceof FXOMPropertyC) {
                final FXOMPropertyC pc = (FXOMPropertyC) p;
                assert pc.getValues().isEmpty() == false;
                final FXOMObject v0 = pc.getValues().get(0);
                if (v0 instanceof FXOMInstance) {
                    normalizeExpandedPaneProperty(pc);
                } else {
                    assert v0 instanceof FXOMIntrinsic;
                    p.removeFromParentInstance();
                }
            } else {
                assert p instanceof FXOMPropertyT;
                final FXOMPropertyT pt = (FXOMPropertyT)p;
                assert pt.getValue().equals("$null");
                p.removeFromParentInstance();
            }
            
            changeCount++;
        }
    }
    
    private void normalizeExpandedPaneProperty(FXOMPropertyC p) {
        
        assert p != null;

        /*
         * 
         * <Accordion>                           // p.getParentInstance()
         *   <expandedPane>                      // p
         *     <TitledPane fx:id="x1" text="B">  // p.getValues().get(0)
         *       ...
         *     </TitledPane>
         *   </expandedPane>
         *   <panes>                             // reference.getParentProperty()         
         *     <TitledPane text="A">
         *       ...
         *     </TitledPane>
         *     <fx:reference source="x1" />      // reference
         *   </panes>
         * </Accordion>
         * 
         */
    
        final FXOMInstance parentInstance = p.getParentInstance();
        assert parentInstance != null;
        final FXOMObject titledPane = p.getValues().get(0);
        assert titledPane.getSceneGraphObject() instanceof TitledPane;
        assert titledPane.getFxId() != null;
        
        final FXOMObject fxomRoot = p.getFxomDocument().getFxomRoot();
        final List<FXOMIntrinsic> references 
                = fxomRoot.collectReferences(titledPane.getFxId());
        assert references.size() == 1;
        final FXOMIntrinsic reference = references.get(0);
        assert reference.getSource().equals(titledPane.getFxId());
        assert reference.getParentObject() == parentInstance;
        final int referenceIndex = reference.getIndexInParentProperty();
        
        p.removeFromParentInstance();
        titledPane.removeFromParentProperty();
        titledPane.addToParentProperty(referenceIndex, reference.getParentProperty());
        reference.removeFromParentProperty();
    }
    
    
    
    private void normalizeGridPanes() {
        final FXOMObject fxomRoot = fxomDocument.getFxomRoot();
        for (FXOMObject fxomGridPane : fxomRoot.collectObjectWithSceneGraphObjectClass(GridPane.class)) {
            normalizeGridPane(fxomGridPane);
            changeCount++;
        }
    }
    
    private final static ColumnConstraintsListPropertyMetadata columnConstraintsMeta
            = new ColumnConstraintsListPropertyMetadata();
    private final static RowConstraintsListPropertyMetadata rowConstraintsMeta
            = new RowConstraintsListPropertyMetadata();
    
    private void normalizeGridPane(FXOMObject fxomGridPane) {
        assert fxomGridPane instanceof FXOMInstance;
        assert fxomGridPane.getSceneGraphObject() instanceof GridPane;
        
        final GridPane gridPane = (GridPane) fxomGridPane.getSceneGraphObject();
        final int columnCount = Deprecation.getGridPaneColumnCount(gridPane);
        final int rowCount = Deprecation.getGridPaneRowCount(gridPane);
        columnConstraintsMeta.unpack((FXOMInstance) fxomGridPane, columnCount);
        rowConstraintsMeta.unpack((FXOMInstance) fxomGridPane, rowCount);
    }
}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/fxom/sampledata/TreeTableViewSampleData.java<|end_filename|>
/*
 * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */

package com.oracle.javafx.scenebuilder.kit.fxom.sampledata;

import java.util.ArrayList;
import java.util.List;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeTableColumn;
import javafx.scene.control.TreeTableView;
import javafx.scene.control.cell.TreeItemPropertyValueFactory;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Rectangle;

/**
 *
 */
class TreeTableViewSampleData extends AbstractSampleData {
    
    private final TreeItem<SampleDataItem> sampleRoot;

    public TreeTableViewSampleData() {
        int i = 0;
        sampleRoot = new TreeItem<>(new SampleDataItem(i++));
        sampleRoot.setExpanded(true);
        for (int j = 0; j<10; j++) {
            final Rectangle r = new Rectangle(10, 10);
            r.setFill(TreeTableViewSampleData.color(i));
            TreeItem<SampleDataItem> child = new TreeItem<>(new SampleDataItem(i++));
            child.setExpanded(true);
            child.setGraphic(r);
            for (int k=0; k<3; k++) {
                final TreeItem<SampleDataItem> child2 = new TreeItem<>(new SampleDataItem(i++));
                child2.setExpanded(true);
                final Circle c = new Circle(5);
                c.setFill(TreeTableViewSampleData.color(i));
                child2.setGraphic(c);
                child.getChildren().add(child2);
            }
            sampleRoot.getChildren().add(child);
        }
    }

    public static boolean canApplyTo(TreeTableView<?> treeTableView) {
        final boolean result;
        
        /*
         * We can insert sample data if:
         * 1) TreeTableView.items() is null
         * 2) TreeTableView columns have no cell factory set
         */
        
        if (treeTableView.getRoot() != null) {
            result = false;
        } else {
            final List<TreeTableColumn<?, ?>> columns = new ArrayList<>();
            columns.addAll(treeTableView.getColumns());
            while (columns.isEmpty() == false) {
                final TreeTableColumn<?,?> tc = columns.get(0);
                if (tc.getCellValueFactory() == null) {
                    columns.remove(0);
                    columns.addAll(tc.getColumns());
                } else {
                    break;
                }
            }
            
            result = columns.isEmpty();
        }
        
        return result;
    }
    
    
    /*
     * AbstractSampleData
     */
    
    
    @Override
    public void applyTo(Object sceneGraphObject) {
        assert sceneGraphObject instanceof TreeTableView;
        
        @SuppressWarnings("unchecked")        
        final TreeTableView<SampleDataItem> tableView = (TreeTableView<SampleDataItem>) sceneGraphObject;
        
        tableView.setRoot(sampleRoot);
        
        final List<TreeTableColumn<SampleDataItem, ?>> columns = new ArrayList<>(tableView.getColumns());
        while (columns.isEmpty() == false) {
            @SuppressWarnings("unchecked")        
            final TreeTableColumn<SampleDataItem,String> ttc 
                    = (TreeTableColumn<SampleDataItem,String>)columns.get(0);
            ttc.setCellValueFactory(SampleDataItem.FACTORY);
            columns.remove(0);
            columns.addAll(ttc.getColumns());
        }
    }

    @Override
    public void removeFrom(Object sceneGraphObject) {
        assert sceneGraphObject instanceof TreeTableView;
        
        @SuppressWarnings("unchecked")        
        final TreeTableView<SampleDataItem> tableView = TreeTableView.class.cast(sceneGraphObject);
        tableView.setRoot(null);
        
        final List<TreeTableColumn<SampleDataItem, ?>> columns = new ArrayList<>();
        columns.addAll(tableView.getColumns());
        while (columns.isEmpty() == false) {
            @SuppressWarnings("unchecked")        
            final TreeTableColumn<SampleDataItem,String> tc 
                    = (TreeTableColumn<SampleDataItem,String>)columns.get(0);
            tc.setCellValueFactory(null);
            columns.remove(0);
            columns.addAll(tc.getColumns());
        }
    }
  
    
    /*
     * Private
     */
    
    
    public static class SampleDataItem {
        int index;
        
        public final static TreeItemPropertyValueFactory<SampleDataItem, String> FACTORY
                = new TreeItemPropertyValueFactory<>("prop"); //NOI18N
        
        public SampleDataItem(int index) {
            this.index = index;
        }
        
        public String getProp() {
            return TreeTableViewSampleData.lorem(index);
        }
    }
}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/content/driver/pring/AbstractPring.java<|end_filename|>
/*
 * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.kit.editor.panel.content.driver.pring;

import com.oracle.javafx.scenebuilder.kit.editor.panel.content.AbstractDecoration;
import com.oracle.javafx.scenebuilder.kit.editor.panel.content.ContentPanelController;
import com.oracle.javafx.scenebuilder.kit.editor.panel.content.gesture.AbstractGesture;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMObject;
import javafx.scene.Node;
import javafx.scene.paint.Paint;

/**
 *
 * 
 */
public abstract class AbstractPring<T> extends AbstractDecoration<T> {
    
    public static final String PARENT_RING_CLASS = "parent-ring"; //NOI18N
    
    public AbstractPring(ContentPanelController contentPanelController,
            FXOMObject fxomObject, Class<T> sceneGraphClass) {
        super(contentPanelController, fxomObject, sceneGraphClass);
    }
    
    public abstract void changeStroke(Paint stroke);
    public abstract AbstractGesture findGesture(Node node);
    
    private static final String PRING = "PRING"; //NOI18N
    
    public static AbstractPring<?> lookupPring(Node node) {
        assert node != null;
        assert node.isMouseTransparent() == false;
        
        final AbstractPring<?> result;
        final Object value = node.getProperties().get(PRING);
        if (value instanceof AbstractPring) {
            result = (AbstractPring<?>) value;
        } else {
            assert value == null;
            result = null;
        }
        
        return result;
    }
    
    public static void attachPring(Node node, AbstractPring<?> pring) {
        assert node != null;
        assert node.isMouseTransparent() == false;
        assert lookupPring(node) == null;
        
        if (pring == null) {
            node.getProperties().remove(PRING);
        } else {
            node.getProperties().put(PRING, pring);
        }
    }
}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/i18n/I18NControl.java<|end_filename|>
package com.oracle.javafx.scenebuilder.kit.i18n;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Locale;
import java.util.PropertyResourceBundle;
import java.util.ResourceBundle;

public class I18NControl extends ResourceBundle.Control {
    @Override
    public ResourceBundle newBundle(String baseName, Locale locale, String format, ClassLoader loader, boolean reload) {
        String bundleName = toBundleName(baseName, locale);
        String resourceName = toResourceName(bundleName, "properties");
        try (InputStream is = loader.getResourceAsStream(resourceName);
             InputStreamReader isr = new InputStreamReader(is, "UTF-8");
             BufferedReader reader = new BufferedReader(isr)) {
            return new PropertyResourceBundle(reader);
        } catch (IOException ex) {
            ex.printStackTrace();
            return null;
        }
    }
}


<|start_filename|>app/src/main/java/com/oracle/javafx/scenebuilder/app/ResourceController.java<|end_filename|>
/*
 * Copyright (c) 2016, Gluon and/or its affiliates.
 * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.app;

import com.oracle.javafx.scenebuilder.app.i18n.I18N;
import com.oracle.javafx.scenebuilder.kit.editor.EditorController;
import com.oracle.javafx.scenebuilder.kit.editor.EditorPlatform;
import com.oracle.javafx.scenebuilder.kit.editor.panel.util.dialog.ErrorDialog;
import javafx.stage.FileChooser;

import java.io.*;
import java.nio.charset.Charset;
import java.util.PropertyResourceBundle;
import java.util.ResourceBundle;

/**
 *
 */
class ResourceController {

    private final DocumentWindowController documentWindowController;
    private File resourceFile;

    public ResourceController(DocumentWindowController dwc) {
        this.documentWindowController = dwc;
    }

    public File getResourceFile() {
        return resourceFile;
    }
    
    public void setResourceFile(File file) {
        if (file != null) {
            if (readPropertyResourceBundle(file) == null) {
                // Property file syntax is probably incorrect

            } else {
                resourceFile = file;
                resourceFileDidChange();
            }
        }
    }
    
    public void performSetResource() {
        // Open a file chooser for *.properties & *.bss
        FileChooser fileChooser = new FileChooser();
        fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter(I18N.getString("resource.filechooser.filter.msg"),
                "*.properties")); //NOI18N
        fileChooser.setInitialDirectory(EditorController.getNextInitialDirectory());
        File newResourceFile = fileChooser.showOpenDialog(documentWindowController.getStage());
        // Keep track of the user choice for next time
        if (newResourceFile != null) {
            EditorController.updateNextInitialDirectory(newResourceFile);
        }

        setResourceFile(newResourceFile);
    }

    public void performRemoveResource() {
        assert resourceFile != null;
        resourceFile = null;
        resourceFileDidChange();
    }

    public void performRevealResource() {
        assert resourceFile != null;
        try {
            EditorPlatform.revealInFileBrowser(resourceFile);
        } catch (IOException ioe) {
            final ErrorDialog errorDialog = new ErrorDialog(null);
            errorDialog.setTitle(I18N.getString("error.file.reveal.title"));
            errorDialog.setMessage(I18N.getString("error.file.reveal.message"));
            errorDialog.setDetails(I18N.getString("error.filesystem.details"));
            errorDialog.setDebugInfoWithThrowable(ioe);
            errorDialog.showAndWait();
        }
    }

    public void performReloadResource() {
        assert resourceFile != null;
        resourceFileDidChange();
    }
    
    /*
     * Private
     */
    
    private void resourceFileDidChange() {
        ResourceBundle resources;
        
        if (resourceFile != null) {
            resources = readPropertyResourceBundle(resourceFile);
            assert resources != null;
        } else {
            resources = null;
        }
        
        documentWindowController.getEditorController().setResources(resources);
        documentWindowController.getWatchingController().update();
    }
    
    
    private static PropertyResourceBundle readPropertyResourceBundle(File f) {
        PropertyResourceBundle result;
        try( Reader reader = new InputStreamReader(new FileInputStream(f), Charset.forName("UTF-8")) ) {
            result = new PropertyResourceBundle(reader); //NOI18N
        } catch (IOException ex) {
            result = null;
        }
        return result;
    }
}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/inspector/editors/AnchorPaneConstraintsEditor.java<|end_filename|>
/*
 * Copyright (c) 2016, Gluon and/or its affiliates.
 * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.kit.editor.panel.inspector.editors;

import com.oracle.javafx.scenebuilder.kit.fxom.FXOMInstance;
import com.oracle.javafx.scenebuilder.kit.metadata.property.ValuePropertyMetadata;
import com.oracle.javafx.scenebuilder.kit.metadata.property.value.DoublePropertyMetadata;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

import javafx.beans.value.ChangeListener;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.control.TextField;
import javafx.scene.control.ToggleButton;
import javafx.scene.layout.Region;

/**
 * Editor for AnchorPane constraints.
 *
 *
 */
public class AnchorPaneConstraintsEditor extends PropertiesEditor {

    private static final String ANCHOR_ENABLED_COLOR = "-sb-line-art-accent";
    private static final String ANCHOR_DISABLED_COLOR = "-sb-line-art";

    @FXML
    private ToggleButton bottomTb;
    @FXML
    private TextField bottomTf;
    @FXML
    private Region innerR;
    @FXML
    private ToggleButton leftTb;
    @FXML
    private TextField leftTf;
    @FXML
    private Region outerR;
    @FXML
    private ToggleButton rightTb;
    @FXML
    private TextField rightTf;
    @FXML
    private ToggleButton topTb;
    @FXML
    private TextField topTf;

    private Parent root = null;
    private final ArrayList<ConstraintEditor> contraintEditors = new ArrayList<>();
    private ChangeListener<Object> constraintListener;

    public AnchorPaneConstraintsEditor(String name, ValuePropertyMetadata topPropMeta,
            ValuePropertyMetadata rightPropMeta,
            ValuePropertyMetadata bottomPropMeta,
            ValuePropertyMetadata leftPropMeta,
            Set<FXOMInstance> selectedInstances) {
        super(name);
        initialize(topPropMeta, rightPropMeta, bottomPropMeta, leftPropMeta, selectedInstances);
        propertyChanged();
        styleRegions();
    }

    // Method to please findBugs
    private void initialize(ValuePropertyMetadata topPropMeta, ValuePropertyMetadata rightPropMeta,
            ValuePropertyMetadata bottomPropMeta, ValuePropertyMetadata leftPropMeta, Set<FXOMInstance> selectedInstances) {
        root = EditorUtils.loadFxml("AnchorPaneConstraintsEditor.fxml", this);

        constraintListener = (ov, prevValue, newValue) -> {
            propertyChanged();
            styleRegions();
        };

        contraintEditors.add(
                new ConstraintEditor(topTf, topTb, selectedInstances, topPropMeta, constraintListener));
        contraintEditors.add(
                new ConstraintEditor(rightTf, rightTb, selectedInstances, rightPropMeta, constraintListener));
        contraintEditors.add(
                new ConstraintEditor(bottomTf, bottomTb, selectedInstances, bottomPropMeta, constraintListener));
        contraintEditors.add(
                new ConstraintEditor(leftTf, leftTb, selectedInstances, leftPropMeta, constraintListener));

        // Select all text when this editor textfield is selected
        topTf.setOnMousePressed(event -> topTf.selectAll());
        topTf.focusedProperty().addListener(((observable, oldValue, newValue) -> {
            if (newValue) {
                topTf.selectAll();
            }
        }));
        rightTf.setOnMousePressed(event -> rightTf.selectAll());
        rightTf.focusedProperty().addListener(((observable, oldValue, newValue) -> {
            if (newValue) {
                rightTf.selectAll();
            }
        }));
        bottomTf.setOnMousePressed(event -> rightTf.selectAll());
        bottomTf.focusedProperty().addListener(((observable, oldValue, newValue) -> {
            if (newValue) {
                rightTf.selectAll();
            }
        }));
        leftTf.setOnMousePressed(event -> rightTf.selectAll());
        leftTf.focusedProperty().addListener(((observable, oldValue, newValue) -> {
            if (newValue) {
                rightTf.selectAll();
            }
        }));
    }

    @Override
    public List<PropertyEditor> getPropertyEditors() {
        List<PropertyEditor> propertyEditors = new ArrayList<>();
        for (ConstraintEditor constraintEditor : contraintEditors) {
            propertyEditors.add(constraintEditor);
        }
        return propertyEditors;
    }

    public void reset(ValuePropertyMetadata topPropMeta,
            ValuePropertyMetadata rightPropMeta,
            ValuePropertyMetadata bottomPropMeta,
            ValuePropertyMetadata leftPropMeta,
            Set<FXOMInstance> selectedInstances) {
        contraintEditors.get(0).reset(selectedInstances, topPropMeta);
        contraintEditors.get(1).reset(selectedInstances, rightPropMeta);
        contraintEditors.get(2).reset(selectedInstances, bottomPropMeta);
        contraintEditors.get(3).reset(selectedInstances, leftPropMeta);
        for (int ii = 0; ii < 4; ii++) {
            contraintEditors.get(ii).addValueListener(constraintListener);
        }
        styleRegions();
    }

    @Override
    public Node getValueEditor() {
        return root;
    }

    private void styleRegions() {
        StringBuilder styleString = new StringBuilder();
        for (int ii = 0; ii < 4; ii++) {
            if (contraintEditors.get(ii).isAnchorEnabled()) {
                styleString.append(ANCHOR_ENABLED_COLOR);
                styleString.append(" ");
            } else {
                styleString.append(ANCHOR_DISABLED_COLOR);
                styleString.append(" ");
            }
        }
        String style = "-fx-border-color: " + styleString;
        innerR.setStyle(style);
        outerR.setStyle(style);
    }

    /*
     * Editor for a single constraint (e.g. topAnchor)
     */
    private static class ConstraintEditor extends PropertyEditor {

        private ToggleButton toggleButton;
        private TextField textField;
        private Set<FXOMInstance> selectedInstances;
        private ValuePropertyMetadata propMeta;

        private boolean updateFromTextField = false;

        public ConstraintEditor(TextField textField, ToggleButton toggleButton, Set<FXOMInstance> selectedInstances,
                ValuePropertyMetadata propMeta, ChangeListener<Object> listener) {
            super(propMeta, null);
            super.addValueListener(listener);
            this.textField = textField;
            this.toggleButton = toggleButton;
            this.selectedInstances = selectedInstances;
            this.propMeta = propMeta;

            initialize();
        }

        private void initialize() {
            //
            // Text field
            //
            // For SQE tests
            textField.setId(EditorUtils.toDisplayName(propMeta.getName().getName()) + " Value"); //NOI18N
            EventHandler<ActionEvent> valueListener = event -> {
                if (isHandlingError()) {
                    // Event received because of focus lost due to error dialog
                    return;
                }
                String valStr = textField.getText();
                if (valStr == null || valStr.isEmpty()) {
                    if (toggleButton.isSelected()) {
                        updateFromTextField = true;
                        toggleButton.setSelected(false);
                        updateFromTextField = false;
                    }
                    userUpdateValueProperty(null);
                    return;
                }
                textField.selectAll();
                double valDouble;
                try {
                    valDouble = Double.parseDouble(valStr);
                } catch (NumberFormatException e) {
                    handleInvalidValue(valStr, textField);
                    return;
                }
                if (!((DoublePropertyMetadata) getPropertyMeta()).isValidValue(valDouble)) {
                    handleInvalidValue(valDouble, textField);
                    return;
                }
                if (!toggleButton.isSelected()) {
                    updateFromTextField = true;
                    toggleButton.setSelected(true);
                    updateFromTextField = false;
                }
                userUpdateValueProperty(valDouble);
            };
            setNumericEditorBehavior(this, textField, valueListener, false);
            // Override default promptText
            textField.setPromptText(""); //NOI18N

            textField.setOnMouseClicked(t -> ConstraintEditor.this.toggleButton.setSelected(true));

            //
            // Toggle button
            //
            assert propMeta instanceof DoublePropertyMetadata;

            toggleButton.selectedProperty().addListener((ChangeListener<Boolean>) (ov, prevSel, newSel) -> {
//                System.out.println("toggleButton : selectedProperty changed!");
                if (isUpdateFromModel() || updateFromTextField) {
                    // nothing to do
                    return;
                }

                // Update comes from toggleButton.
                if (newSel) {
                    // Anchor selected : compute its value from the selected node
                    double anchor = 0;
                    String propName = ConstraintEditor.this.propMeta.getName().toString();
                    switch (propName) {
                        // For the moment, we don't support multi-selection with different anchors:
                        // the first instance anchor only is used.
                        case topAnchorPropName:
                            anchor = EditorUtils.computeTopAnchor(getFirstInstance());
                            break;
                        case rightAnchorPropName:
                            anchor = EditorUtils.computeRightAnchor(getFirstInstance());
                            break;
                        case bottomAnchorPropName:
                            anchor = EditorUtils.computeBottomAnchor(getFirstInstance());
                            break;
                        case leftAnchorPropName:
                            anchor = EditorUtils.computeLeftAnchor(getFirstInstance());
                            break;
                        default:
                            assert false;
                    }
                    textField.setText(EditorUtils.valAsStr(anchor));
                    userUpdateValueProperty(getValue());
                } else {
                    // Anchor unselected
                    textField.setText(null);
                    userUpdateValueProperty(null);
                }
            });
        }

        @Override
        public Node getValueEditor() {
            // Should not be called
            assert false;
            return null;
        }

        @Override
        public Object getValue() {
            String valStr = textField.getText();
            if (valStr == null || valStr.isEmpty()) {
                return null;
            }
            return Double.valueOf(valStr);
        }

        @Override
        public void setValue(Object value) {
            setValueGeneric(value);
            if (isSetValueDone()) {
                return;
            }

            if (value == null) {
                toggleButton.setSelected(false);
                textField.setText(null);
            } else {
                assert (value instanceof Double);
                toggleButton.setSelected(true);
                textField.setText(EditorUtils.valAsStr(value));
                if (textField.isFocused()) {
                    textField.positionCaret(textField.getLength());
                }
            }
        }

        public void reset(Set<FXOMInstance> selectedInstances, ValuePropertyMetadata propMeta) {
            super.reset(propMeta, null);
            this.selectedInstances = selectedInstances;
            this.propMeta = propMeta;
            textField.setPromptText(null);
        }

        @Override
        protected void valueIsIndeterminate() {
            handleIndeterminate(textField);
        }

        public boolean isAnchorEnabled() {
            return valueProperty().getValue() != null;
        }

        @Override
        public void requestFocus() {
            EditorUtils.doNextFrame(() -> textField.requestFocus());
        }
        
        private FXOMInstance getFirstInstance() {
            return (FXOMInstance) selectedInstances.toArray()[0];
        }
    }
}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/job/IncludeFileJob.java<|end_filename|>
/*
 * Copyright (c) 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.kit.editor.job;

import com.oracle.javafx.scenebuilder.kit.editor.EditorController;
import com.oracle.javafx.scenebuilder.kit.i18n.I18N;
import com.oracle.javafx.scenebuilder.kit.editor.selection.AbstractSelectionGroup;
import com.oracle.javafx.scenebuilder.kit.editor.selection.ObjectSelectionGroup;
import com.oracle.javafx.scenebuilder.kit.editor.selection.Selection;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMDocument;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMIntrinsic;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMNodes;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMObject;
import com.oracle.javafx.scenebuilder.kit.metadata.util.DesignHierarchyMask;
import com.oracle.javafx.scenebuilder.kit.util.URLUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

/**
 *
 */
public class IncludeFileJob extends BatchSelectionJob {

    private final File file;
    private FXOMObject targetObject;
    private FXOMIntrinsic newInclude;

    public IncludeFileJob(File file, EditorController editorController) {
        super(editorController);

        assert file != null;
        this.file = file;
    }

    public FXOMObject getTargetObject() {
        return targetObject;
    }

    @Override
    protected List<Job> makeSubJobs() {
        final List<Job> result = new ArrayList<>();

        try {
            final FXOMDocument targetDocument = getEditorController().getFxomDocument();
            final URL documentURL = getEditorController().getFxmlLocation();
            final URL fileURL = file.toURI().toURL();

            // Cannot include in non saved document
            // Cannot include same file as document one which will create cyclic reference
            if (documentURL != null && URLUtils.equals(documentURL, fileURL) == false) {
                newInclude = FXOMNodes.newInclude(targetDocument, file);

                // newInclude is null when file is empty
                if (newInclude != null) {

                    // Cannot include as root
                    final FXOMObject rootObject = targetDocument.getFxomRoot();
                    if (rootObject != null) {
                        // We include the new object under the common parent
                        // of the selected objects.
                        final Selection selection = getEditorController().getSelection();
                        if (selection.isEmpty() || selection.isSelected(rootObject)) {
                            // No selection or root is selected -> we insert below root
                            targetObject = rootObject;
                        } else {
                            // Let's use the common parent of the selected objects.
                            // It might be null if selection holds some non FXOMObject entries
                            targetObject = selection.getAncestor();
                        }
                        // Build InsertAsSubComponent jobs
                        final DesignHierarchyMask targetMask = new DesignHierarchyMask(targetObject);
                        if (targetMask.isAcceptingSubComponent(newInclude)) {
                            result.add(new InsertAsSubComponentJob(
                                    newInclude,
                                    targetObject,
                                    targetMask.getSubComponentCount(),
                                    getEditorController()));
                        }
                    }
                }
            }
        } catch (IOException ex) {
            result.clear();
        }

        return result;
    }

    @Override
    protected String makeDescription() {
        return I18N.getString("include.file", file.getName());
    }

    @Override
    protected AbstractSelectionGroup getNewSelectionGroup() {
        final List<FXOMObject> fxomObjects = new ArrayList<>();
        fxomObjects.add(newInclude);
        return new ObjectSelectionGroup(fxomObjects, newInclude, null);
    }
}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/skeleton/SkeletonFileNameProposal.java<|end_filename|>
/*
 * Copyright (c) 2021, Gluon and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation and Gluon nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.kit.skeleton;

import java.io.File;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;

/**
 * 
 * Provides logic to create a proposal for a controller skeleton file for a
 * given language.
 * 
 * The proposal is created based on the actual fxmlLocation and the 
 * fxController name.
 *
 * <ol>
 * <li>undefined fxmlLocation and fxControllerName: - user directory with
 * PleaseProvideControllerClassName.java or PleaseProvideControllerClassName.kt.
 * 
 * <li>undefined fxControllerName but known fxmlLocation: - the name of the FXML
 * file (e.g. MyView.fxml) changed to MyViewController.fxml.
 * 
 * <li>undefined fxControllerName but known fxmlLocation, FXML stored in
 * src/main/resources: - for MyView.fxml and Java, the result would be
 * src/main/java/MyViewController.java, for Kotlin it would be
 * src/main/kotlin/MyViewController.kt
 *   
 * <li>controller name (e.g. MyController) defined and known fxmlLocation: - the
 * file would be MyController.java or MyController.kt at the place where the
 * FXML is stored - the proposal is also adjusted to reside in src/main/language
 * if possible.
 * <li>A provided controller name is always preferred over an adjusted FXML file
 * name.
 * </ol>
 * 
 * If the language specific directory does not exist, the original location is 
 * used with an adjusted file name.
 * 
 */
class SkeletonFileNameProposal {

    private static final String DEFAULT_CONTROLLER_CLASS_NAME = "PleaseProvideControllerClassName";
    private final SkeletonSettings.LANGUAGE language;

    public SkeletonFileNameProposal(SkeletonSettings.LANGUAGE language) {
        this.language = language;
    }

    /**
     * Creates a controller skeleton file name proposal depending on values for
     * fxmlLocation and fxControllerName.
     * 
     * @param fxmlLocation     The location of the FXML file in works. In case the
     *                         file has not been saved, this location will be null.
     * @param fxControllerName This is usually the name of the controller as defined
     *                         in the FXML file. If no controller name has been
     *                         given, this value might be null
     * @return file name proposal
     */
    public File create(URL fxmlLocation, String fxControllerName) {
        if (fxControllerName == null || fxControllerName.isBlank()) {
            if (null != fxmlLocation) {
                File controllerAtFxmlLocation = createFileAccordingToFxml(fxmlLocation);
                return adjustToSrcMainDirWhenPossible(controllerAtFxmlLocation);
            }
        } else {
            File fromControllerName = createFileFromControllerName(fxmlLocation, fxControllerName);
            return adjustToSrcMainDirWhenPossible(fromControllerName);
        }
        return createFileInUserDir();
    }

    private File adjustToSrcMainDirWhenPossible(File controllerAtFxmlLocation) {
        String location = controllerAtFxmlLocation.toPath().toString().replace('\\', '/');
        List<Path> sourcePackages = List.of(Paths.get("src/main"));
        for (Path sourcePackage : sourcePackages) {
            String resources = resolvePath(sourcePackage, "resources");
            String java = resolvePath(sourcePackage, "java");
            String kotlin = resolvePath(sourcePackage, "kotlin");
            if (location.contains(resources)) {
                switch (language) {
                case JAVA:
                    location = location.replace(resources, java);
                    break;
                case KOTLIN:
                    location = location.replace(resources, kotlin);
                    break;
                }
            }
        }
        File adjustedLocation = new File(location); 
        if (Files.exists(adjustedLocation.toPath().getParent())) {
            return adjustedLocation;
        }
        return controllerAtFxmlLocation;
    }
    
    private String resolvePath(Path source, String child) {
        return source.resolve(child)
                     .toString()
                     .replace('\\', '/');
    }

    private File createFileFromControllerName(URL fxmlLocation, String fxControllerName) {
        String directory = obtainUserDirectory();
        if (null != fxmlLocation) {
            URI uri = resolveURI(fxmlLocation);
            Path location = Paths.get(uri).toAbsolutePath().getParent();
            if (Files.exists(location)) {
                directory = location.toString();
            }
        }
        String simpleControllerClassName = extractSimpleControllerName(fxControllerName);
        String controllerFileName = simpleControllerClassName + language.getExtension();
        return new File(directory, controllerFileName);
    }

    private URI resolveURI(URL fxmlLocation) {
        try {
            return fxmlLocation.toURI();
        } catch (URISyntaxException e) {
            File userDir = new File(obtainUserDirectory()).getAbsoluteFile();
            return userDir.toURI();
        }
    }

    private File createFileAccordingToFxml(URL fxmlLocation) {
        URI  uri = resolveURI(fxmlLocation);
        Path fxmlFile = Paths.get(uri);
        String fxmlResource = fxmlFile.toString();
        String controllerSuffix = "Controller" + language.getExtension();
        int lastDot = fxmlResource.lastIndexOf('.');
        if (lastDot > -1) {
            String newFileName = fxmlResource.substring(0, lastDot) + controllerSuffix;
            return Paths.get(newFileName).toFile();
        }

        return Paths.get(fxmlResource + controllerSuffix).toFile();

    }

    private File createFileInUserDir() {
        String fileName = DEFAULT_CONTROLLER_CLASS_NAME + language.getExtension();
        String userDirectory = obtainUserDirectory();
        return buildFileName(userDirectory, fileName);
    }

    private String obtainUserDirectory() {
        return System.getProperty("user.home");
    }

    private File buildFileName(String userDirectory, String fileName) {
        return Paths.get(userDirectory, fileName).normalize().toAbsolutePath().toFile();
    }

    /*
     * TODO: Consider moving this into a shared place as this code snippet is used multiple times.
     * 
     */
    private String extractSimpleControllerName(String fxControllerName) {
        String simpleName = fxControllerName.replace("$", "."); // NOI18N
        int dot = simpleName.lastIndexOf('.');
        if (dot > -1) {
            simpleName = simpleName.substring(dot + 1);
        }
        return simpleName;
    }

}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/library/maven/search/NexusSearch.java<|end_filename|>
/*
 * Copyright (c) 2016, Gluon and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation and Gluon nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.kit.editor.panel.library.maven.search;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import javax.json.Json;
import javax.json.JsonArray;
import javax.json.JsonObject;
import javax.json.JsonReader;
import static org.apache.commons.codec.binary.Base64.encodeBase64;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.HttpClients;
import org.eclipse.aether.artifact.DefaultArtifact;

public class NexusSearch implements Search {

    // nexus
    private static final String URL_PREFIX = "/service/local/data_index?q=";
    private static final String URL_PREFIX_CLASS = "https://nexus.gluonhq.com/nexus/service/local/data_index?cn=";
    private static final String URL_SUFFIX = "&from=";
    
    private final HttpClient client;
    private final String name;
    private final String domain;
    private final String username;
    private final String password;
            
    private static boolean first;
    private static int iteration;
    private int totalCount = 0;
    private static final int ITEMS_ITERATION = 200;
    private static final int MAX_RESULTS = 2000;
    
    public NexusSearch(String name, String domain, String username, String password) {
        client = HttpClients.createDefault();
        this.name = name;
        this.domain = domain;
        this.username = username;
        this.password = password;
        
        iteration = 0;
        first = true;
    }
    
    @Override
    public List<DefaultArtifact> getCoordinates(String query) {
        try {
            HttpGet request = new HttpGet(domain + URL_PREFIX + query + (first ? "" : URL_SUFFIX + iteration * ITEMS_ITERATION));
            if (!username.isEmpty() && !password.isEmpty()) {
                String authStringEnc = new String(encodeBase64((username + ":" + password).getBytes()));
                request.addHeader("Authorization", "Basic " + authStringEnc);
            }
            request.setHeader("Accept", "application/json");
            HttpResponse response = client.execute(request);
            try (JsonReader rdr = Json.createReader(response.getEntity().getContent())) {
                JsonObject obj = rdr.readObject();
                if (first && obj != null && !obj.isEmpty() && obj.containsKey("totalCount")) {
                    first = false;
                    totalCount = Math.min(obj.getInt("totalCount", 0), MAX_RESULTS);
                    if (totalCount > ITEMS_ITERATION) {
                        List<DefaultArtifact> coordinates = new ArrayList<>(processRequest(obj));
                        while (totalCount > ITEMS_ITERATION) {
                            iteration += 1;
                            coordinates.addAll(getCoordinates(query)
                                    .stream()
                                    .filter(ga -> coordinates.stream()
                                            .noneMatch(ar -> ar.getGroupId().equals(ga.getGroupId()) &&
                                                             ar.getArtifactId().equals(ga.getArtifactId())))
                                    .collect(Collectors.toList()));
                            
                            totalCount -= ITEMS_ITERATION;
                        }
                        return coordinates;
                    }
                }
                return processRequest(obj);
            }
        } catch (IOException ex) {
            Logger.getLogger(NexusSearch.class.getName()).log(Level.SEVERE, null, ex);
        }
        return null;
    }
    
    private List<DefaultArtifact> processRequest(JsonObject obj) {
        if (obj != null && !obj.isEmpty() && obj.containsKey("data")) {
            JsonArray docResults = obj.getJsonArray("data");
            return docResults.getValuesAs(JsonObject.class)
                    .stream()
                    .map(doc -> {
                        final Map<String, String> map = new HashMap<>();
                        map.put("Repository", name + " (" + doc.getString("repoId", "") + ")");
                        return new DefaultArtifact(doc.getString("groupId", "") + ":" + 
                                doc.getString("artifactId", "") + ":" + MIN_VERSION, map);
                    })
                    .distinct()
                    .collect(Collectors.toList());
        }
        return null;
    }
}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/util/control/paintpicker/PaintPicker.java<|end_filename|>
/*
 * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.kit.util.control.paintpicker;

import java.io.IOException;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.ReadOnlyBooleanProperty;
import javafx.fxml.FXMLLoader;
import javafx.scene.Node;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Paint;

/**
 * Paint editor control.
 */
public class PaintPicker extends Pane {

    public enum Mode {

        COLOR, LINEAR, RADIAL
    }

    private final PaintPickerController controller;

    public PaintPicker(Delegate delegate) {        
        final FXMLLoader loader = new FXMLLoader();
        loader.setLocation(PaintPicker.class.getResource("PaintPicker.fxml")); //NOI18N

        try {
            // Loading
            final Object rootObject = loader.load();
            assert rootObject instanceof Node;
            final Node rootNode = (Node) rootObject;
            getChildren().add(rootNode);

            // Retrieving the controller
            final Object ctl = loader.getController();
            assert ctl instanceof PaintPickerController;
            this.controller = (PaintPickerController) ctl;
            this.controller.setDelegate(delegate);
        } catch (IOException ex) {
            throw new IllegalStateException(ex);
        }
    }

    public PaintPicker(Delegate delegate, Mode mode) {
        this(delegate);
        controller.setSingleMode(mode);
    }
    
    public final ObjectProperty<Paint> paintProperty() {
        return controller.paintProperty();
    }

    public final void setPaintProperty(Paint value) {
        // Update model
        controller.setPaintProperty(value);
        // Update UI
        controller.updateUI(value);
    }

    public final Paint getPaintProperty() {
        return controller.getPaintProperty();
    }
    
    public final ReadOnlyBooleanProperty liveUpdateProperty() {
        return controller.liveUpdateProperty();
    }

    public boolean isLiveUpdate() {
        return controller.isLiveUpdate();
    }
    
    public static interface Delegate {
        public void handleError(String warningKey, Object... arguments);
    }
}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/content/AbstractDecoration.java<|end_filename|>
/*
 * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.kit.editor.panel.content;

import com.oracle.javafx.scenebuilder.kit.fxom.FXOMObject;
import com.oracle.javafx.scenebuilder.kit.util.Deprecation;

import javafx.beans.value.ChangeListener;
import javafx.geometry.Bounds;
import javafx.geometry.Point2D;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.SubScene;
import javafx.scene.transform.NonInvertibleTransformException;
import javafx.scene.transform.Transform;

/**
 * @treatAsPrivate
 */
public abstract class AbstractDecoration<T> {
    
    /**
     * @treatAsPrivate
     */
    public enum State {
        CLEAN,
        NEEDS_RECONCILE,
        NEEDS_REPLACE
    }
    
    private final ContentPanelController contentPanelController;
    private final FXOMObject fxomObject;
    private final Class<T> sceneGraphClass;
    private final Group rootNode = new Group();
    private T sceneGraphObject;

    
    public AbstractDecoration(ContentPanelController contentPanelController,
            FXOMObject fxomObject, Class<T> sceneGraphClass) {
        assert contentPanelController != null;
        assert fxomObject != null;
        assert fxomObject.getSceneGraphObject() != null;
        assert fxomObject.getFxomDocument() == contentPanelController.getEditorController().getFxomDocument();
        assert sceneGraphClass != null;
        
        this.contentPanelController = contentPanelController;
        this.fxomObject = fxomObject;
        this.sceneGraphClass = sceneGraphClass;
        this.sceneGraphObject = sceneGraphClass.cast(fxomObject.getSceneGraphObject());
        
        this.rootNode.sceneProperty().addListener((ChangeListener<Scene>) (ov, v1, v2) -> rootNodeSceneDidChange());
    }

    public ContentPanelController getContentPanelController() {
        return contentPanelController;
    }
    
    public FXOMObject getFxomObject() {
        return fxomObject;
    }
    
    public T getSceneGraphObject() {
        return sceneGraphObject;
    }
    
    public Group getRootNode() {
        return rootNode;
    }
    
    public State getState() {
        final State result;
        
        if (fxomObject.getSceneGraphObject() == sceneGraphObject) {
            result = State.CLEAN;
        } else if (fxomObject.getSceneGraphObject() == null) {
            // Scene graph object became unresolved !
            result = State.NEEDS_REPLACE;
        } else if (fxomObject.getSceneGraphObject().getClass() == sceneGraphClass) {
            result = State.NEEDS_RECONCILE;
        } else {
            result = State.NEEDS_REPLACE;
        }
        
        return result;
    }
    
    public void reconcile() {
        assert getState() == State.NEEDS_RECONCILE;
        
        stopListeningToSceneGraphObject();
        updateSceneGraphObject();
        startListeningToSceneGraphObject();
        layoutDecoration();
    }
    
    public Point2D sceneGraphObjectToDecoration(double x, double y, boolean snapToPixel) {
        Point2D result = sceneGraphObjectToDecoration(x, y);
        if (snapToPixel) {
            final double rx = Math.round(result.getX());
            final double ry = Math.round(result.getY());
            result = new Point2D(rx, ry);
        }
        return result;
    }
    
    public Transform getSceneGraphObjectToDecorationTransform() {
        final Node proxy = getSceneGraphObjectProxy();
        final SubScene contentSubScene = contentPanelController.getContentSubScene();
        final Transform t0 = proxy.getLocalToSceneTransform();
        final Transform t1 = contentSubScene.getLocalToSceneTransform();
        final Transform t2 = getRootNode().getLocalToSceneTransform();
        final Transform result;
        
        try {
            final Transform i2 = t2.createInverse();
            result = i2.createConcatenation(t1).createConcatenation(t0);
        } catch(NonInvertibleTransformException x) {
            throw new RuntimeException(x);
        }
        
        return result;
    }
    
    public abstract Bounds getSceneGraphObjectBounds();
    public abstract Node getSceneGraphObjectProxy();
    protected abstract void startListeningToSceneGraphObject();
    protected abstract void stopListeningToSceneGraphObject();
    protected abstract void layoutDecoration();
    
    
    /*
     * Utilities for subclasses
     */
    
    public Point2D sceneGraphObjectToDecoration(double x, double y) {
        final Node proxy = getSceneGraphObjectProxy();
        return Deprecation.localToLocal(proxy, x, y, getRootNode());
    }
            
    protected void startListeningToLayoutBounds(Node node) {
        assert node != null;
        node.layoutBoundsProperty().addListener(layoutBoundsListener);
    }
    
    protected void stopListeningToLayoutBounds(Node node) {
        assert node != null;
        node.layoutBoundsProperty().removeListener(layoutBoundsListener);
    }

    protected void startListeningToBoundsInParent(Node node) {
        assert node != null;
        node.boundsInParentProperty().addListener(boundsInParentListener);
    }
    
    protected void stopListeningToBoundsInParent(Node node) {
        assert node != null;
        node.boundsInParentProperty().removeListener(boundsInParentListener);
    }

    protected void startListeningToLocalToSceneTransform(Node node) {
        assert node != null;
        node.localToSceneTransformProperty().addListener(localToSceneTransformListener);
        node.sceneProperty().addListener(sceneListener);
        final SubScene contentSubScene = contentPanelController.getContentSubScene();
        contentSubScene.localToSceneTransformProperty().addListener(localToSceneTransformListener);
    }
    
    protected void stopListeningToLocalToSceneTransform(Node node) {
        assert node != null;
        node.localToSceneTransformProperty().removeListener(localToSceneTransformListener);
        node.sceneProperty().removeListener(sceneListener);
        final SubScene contentSubScene = contentPanelController.getContentSubScene();
        contentSubScene.localToSceneTransformProperty().removeListener(localToSceneTransformListener);
    }
    
    /*
     * Protected
     */

    protected void rootNodeSceneDidChange() {
        if (rootNode.getScene() == null) {
            stopListeningToSceneGraphObject();
        } else {
            startListeningToSceneGraphObject();
            layoutDecoration();
        }
    }
    
    protected void updateSceneGraphObject() {
        this.sceneGraphObject = sceneGraphClass.cast(fxomObject.getSceneGraphObject());
    }
    
    /*
     * Private
     */
    
    private final ChangeListener<Bounds> layoutBoundsListener
        = (ov, v1, v2) -> layoutDecoration();
    
    private final ChangeListener<Bounds> boundsInParentListener
        = (ov, v1, v2) -> layoutDecoration();
    
    private final ChangeListener<Transform> localToSceneTransformListener
        = (ov, v1, v2) -> layoutDecoration(); 
    
    private final ChangeListener<Scene> sceneListener
        = (ov, v1, v2) -> layoutDecoration(); 
}


<|start_filename|>version.json<|end_filename|>
{
  "announcement": {
    "url": "https://gluonhq.com/scene-builder-16-release/",
    "text": "Scene Builder 16 has been released. Download and install it now, or select 'Learn more' to visit https://gluonhq.com/scene-builder-16-release/ for more information."
  }
}

<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/job/RelocateSelectionJob.java<|end_filename|>
/*
 * Copyright (c) 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */

package com.oracle.javafx.scenebuilder.kit.editor.job;

import com.oracle.javafx.scenebuilder.kit.editor.job.atomic.RelocateNodeJob;
import com.oracle.javafx.scenebuilder.kit.editor.EditorController;
import com.oracle.javafx.scenebuilder.kit.i18n.I18N;
import com.oracle.javafx.scenebuilder.kit.editor.selection.ObjectSelectionGroup;
import com.oracle.javafx.scenebuilder.kit.editor.selection.Selection;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMInstance;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMObject;
import com.oracle.javafx.scenebuilder.kit.metadata.util.DesignHierarchyMask;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javafx.geometry.Point2D;

/**
 *
 */
public class RelocateSelectionJob extends BatchDocumentJob {
    
    private static final long MERGE_PERIOD = 1000; //  milliseconds
    
    private final Map<FXOMObject, Point2D> locationMap = new HashMap<>();
    private long time = System.currentTimeMillis();

    public RelocateSelectionJob(Map<FXOMObject, Point2D> locationMap,
            EditorController editorController) {
        super(editorController);
        this.locationMap.putAll(locationMap);
    }
    
    public boolean canBeMergedWith(Job other) {
        
        /*
         * This job is collapsible with other if:
         *      0) other is a RelocateSelectionJob instance
         *      1) other is younger than this of 1000 ms no more
         *      2) other and this have the same location map keys
         */
        
        final boolean result;
        if (other instanceof RelocateSelectionJob) {
            final RelocateSelectionJob otherRelocate = (RelocateSelectionJob)other;
            final long timeDifference = otherRelocate.time - this.time;
            if ((0 <= timeDifference) && (timeDifference < MERGE_PERIOD)) {
                final Set<FXOMObject> thisKeys = this.locationMap.keySet();
                final Set<FXOMObject> otherKeys = otherRelocate.locationMap.keySet();
                result = thisKeys.equals(otherKeys);
            } else {
                result = false;
            }
        } else {
            result = false;
        }
        
        return result;
    }
    
    
    public void mergeWith(Job younger) {
        assert canBeMergedWith(younger); // (1)
        assert younger instanceof RelocateSelectionJob; // Because (1)
        
        final RelocateSelectionJob youngerSelection = (RelocateSelectionJob) younger;
        for (Job subJob : getSubJobs()) {
            assert subJob instanceof RelocateNodeJob;
            final RelocateNodeJob thisRelocateJob 
                    = (RelocateNodeJob) subJob;
            final RelocateNodeJob youngerRelocateJob 
                    = youngerSelection.lookupSubJob(thisRelocateJob.getFxomInstance());
            thisRelocateJob.mergeWith(youngerRelocateJob);
        }
        
        this.time = youngerSelection.time;
    }
    
    
    public RelocateNodeJob lookupSubJob(FXOMObject fxomObject) {
        RelocateNodeJob result = null;
        
        for (Job subJob : getSubJobs()) {
            assert subJob instanceof RelocateNodeJob;
            final RelocateNodeJob relocateJob = (RelocateNodeJob) subJob;
            if (relocateJob.getFxomInstance() == fxomObject) {
                result = relocateJob;
                break;
            }
        }
        
        return result;
    }
    
    public static boolean isSelectionMovable(EditorController editorController) {
        /*
         * Selection can be moved if:
         * 1) it's an object selection (group instanceof ObjectSelectionGroup)
         * 2) selected objects have a single parent
         * 3) single parent supports free child positioning
         * 
         * => all selected items are Node.
         */
        
        final boolean result;
        
        final Selection selection = editorController.getSelection();
        if (selection.getGroup() instanceof ObjectSelectionGroup) {
            final ObjectSelectionGroup osg = (ObjectSelectionGroup) selection.getGroup();
            if (osg.hasSingleParent()) {
                final FXOMObject parent = osg.getAncestor();
                final DesignHierarchyMask m = new DesignHierarchyMask(parent);
                result = m.isFreeChildPositioning();
            } else {
                result = false;
            }
        } else {
            result = false;
        }
        
        return result;
    }
    
    @Override
    protected List<Job> makeSubJobs() {
        final List<Job> result = new ArrayList<>();
        
        for (Map.Entry<FXOMObject, Point2D> entry : locationMap.entrySet()) {
            assert entry.getKey() instanceof FXOMInstance;
            final FXOMInstance fxomInstance = (FXOMInstance) entry.getKey();
            final Point2D layoutXY = entry.getValue();
            final Job relocateJob = new RelocateNodeJob(fxomInstance,
                layoutXY.getX(), layoutXY.getY(), getEditorController());
            result.add(relocateJob);
        }
        
        return result;
    }

    @Override
    protected String makeDescription() {
        final String result;
        
        final Set<FXOMObject> movedObjects = locationMap.keySet();
        if (locationMap.size() == 1) {
            final FXOMObject movedObject = movedObjects.iterator().next();
            final Object sceneGraphObject = movedObject.getSceneGraphObject();
            if (sceneGraphObject == null) {
                result = I18N.getString("drop.job.move.single.unresolved");
            } else {
                result = I18N.getString("drop.job.move.single.resolved",
                        sceneGraphObject.getClass().getSimpleName());
            }
        } else {
            final Set<Class<?>> classes = new HashSet<>();
            int unresolvedCount = 0;
            for (FXOMObject o : movedObjects) {
                if (o.getSceneGraphObject() != null) {
                    classes.add(o.getSceneGraphObject().getClass());
                } else {
                    unresolvedCount++;
                }
            }
            final boolean homogeneous = (classes.size() == 1) && (unresolvedCount == 0);
            
            if (homogeneous) {
                final Class<?> singleClass = classes.iterator().next();
                result = I18N.getString("drop.job.move.multiple.homogeneous",
                        movedObjects.size(),
                        singleClass.getSimpleName());
            } else {
                result = I18N.getString("drop.job.move.multiple.heterogeneous",
                        movedObjects.size());
            }
        }
        
        return result;
    }
    
}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/hierarchy/treeview/HierarchyTreeViewUtils.java<|end_filename|>
/*
 * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.kit.editor.panel.hierarchy.treeview;

import static com.oracle.javafx.scenebuilder.kit.editor.panel.hierarchy.treeview.HierarchyTreeCell.HIERARCHY_TREE_CELL;
import com.oracle.javafx.scenebuilder.kit.util.Deprecation;
import java.util.List;
import java.util.Set;
import javafx.scene.Node;
import javafx.scene.control.TreeCell;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;

/**
 *
 * p
 */
public abstract class HierarchyTreeViewUtils {

    /**
     * Returns the TreeCells for the specified TreeView.
     *
     * @param <T>
     * @param treeView the TreeView owner
     * @return the TreeCells for the specified TreeView.
     */
    public static <T> Set<Node> getTreeCells(final TreeView<T> treeView) {
        assert treeView != null;
        // Looks for the sub nodes which match the CSS selector
        return treeView.lookupAll("." + HIERARCHY_TREE_CELL); //NOI18N
    }

    /**
     * Returns the TreeCell object corresponding to the specified TreeItem.
     *
     * @param treeView the TreeView owner
     * @param treeItem the TreeItem instance
     * @return the TreeCell object corresponding to the specified TreeItem.
     */
    public static TreeCell<?> getTreeCell(final TreeView<?> treeView, final TreeItem<?> treeItem) {
        return getTreeCell(getTreeCells(treeView), treeItem);
    }

    /**
     * Returns the TreeCell object corresponding to the specified index.
     *
     * @param treeView the TreeView owner
     * @param index the TreeCell index
     * @return the TreeCell object corresponding to the specified index.
     */
    public static TreeCell<?> getTreeCell(final TreeView<?> treeView, final int index) {
        return getTreeCell(getTreeCells(treeView), index);
    }

    /**
     * Returns the TreeCell object corresponding to the specified TreeItem.
     *
     * @param treeCells the set of TreeCells
     * @param treeItem the TreeItem instance
     * @return the TreeCell object corresponding to the specified TreeItem.
     */
    public static TreeCell<?> getTreeCell(final Set<Node> treeCells, final TreeItem<?> treeItem) {
        assert treeCells != null;
        assert treeItem != null;
        for (Node node : treeCells) {
            assert node instanceof TreeCell;
            final TreeCell<?> treeCell = (TreeCell<?>) node;
            if (treeItem.getValue() != null
                    && treeItem.getValue().equals(treeCell.getItem())) {
                return treeCell;
            }
        }
        return null;
    }

    /**
     * Returns the TreeCell object corresponding to the specified index.
     *
     * @param treeCells the set of TreeCells
     * @param index the TreeCell index
     * @return the TreeCell object corresponding to the specified index.
     */
    public static TreeCell<?> getTreeCell(final Set<Node> treeCells, final int index) {
        assert treeCells != null;
        for (Node node : treeCells) {
            assert node instanceof TreeCell;
            final TreeCell<?> treeCell = (TreeCell<?>) node;
            if (treeCell.getIndex() == index) {
                return treeCell;
            }
        }
        return null;
    }

    /**
     * Return true if the specified child TreeItem can be reparented to the
     * specified parent TreeItem. This means that the child TreeItem object is
     * not in the parent chain of the parent TreeItem.
     *
     * @param <T>
     * @param child
     * @param parent
     * @return
     */
    public static <T> boolean canReparentTreeItem(final TreeItem<T> child, TreeItem<T> parent) {
        if (child == parent) {
            return false;
        }
        int childLevel = Deprecation.getNodeLevel(child);
        int parentLevel = Deprecation.getNodeLevel(parent);
        while (parentLevel >= childLevel) {
            if (parent == child) {
                return false;
            }
            parent = parent.getParent();
            parentLevel--;
        }
        return true;
    }

    /**
     * Return the common parent TreeItem of the specified TreeItems.
     *
     * @param <T>
     * @param treeItems
     * @return
     */
    public static <T> TreeItem<T> getCommonParentTreeItem(
            final List<TreeItem<T>> treeItems) {

        assert treeItems != null && !treeItems.isEmpty();

        // TreeItems contains ROOT 
        // => return ROOT as the common parent
        for (TreeItem<T> treeItem : treeItems) {
            if (Deprecation.getNodeLevel(treeItem) == 0) {
                return treeItem;
            }
        }

        // TreeItem single selection
        // => the common parent is the single TreeItem parent
        if (treeItems.size() == 1) {
            return treeItems.get(0).getParent();
        } //
        // TreeItem multi selection
        else {
            assert treeItems.size() >= 2;
            TreeItem<T> parent = null;
            TreeItem<T> child = treeItems.get(0);
            for (int index = 1; index < treeItems.size(); index++) {
                parent = getCommonParentTreeItem(child, treeItems.get(index));
                // We reached the ROOT level
                // => common parent is ROOT TreeItem
                if (Deprecation.getNodeLevel(parent) == 0) {
                    break;
                } else {
                    child = parent;
                }
            }
            return parent;
        }
    }

    private static <T> TreeItem<T> getCommonParentTreeItem(
            final TreeItem<T> child1,
            final TreeItem<T> child2) {

        assert child1 != null && child2 != null;

        int child1Level = Deprecation.getNodeLevel(child1);
        int child2Level = Deprecation.getNodeLevel(child2);
        // Neither child1 nor child2 is ROOT TreeItem
        assert child1Level > 0 && child2Level > 0;

        TreeItem<T> parent1 = child1.getParent();
        TreeItem<T> parent2 = child2.getParent();

        if (child1Level < child2Level) {
            while (child1Level < child2Level) {
                parent2 = parent2.getParent();
                child2Level--;
            }
            // We reached the common parent TreeItem
            if (parent1 == parent2) {
                return parent1;
            } else {
                // At this step, parent1 and parent2 have same node level 
                // within the TreeView
                while (parent1 != parent2) {
                    parent1 = parent1.getParent();
                    parent2 = parent2.getParent();
                }
                return parent1;
            }
        } else {
            while (child1Level > child2Level) {
                parent1 = parent1.getParent();
                child1Level--;
            }
            // We reached the common parent TreeItem
            if (parent1 == parent2) {
                return parent1;
            } else {
                // At this step, parent1 and parent2 have same node level 
                // within the TreeView
                while (parent1 != parent2) {
                    parent1 = parent1.getParent();
                    parent2 = parent2.getParent();
                }
                return parent1;
            }
        }
    }
}


<|start_filename|>app/src/main/java/com/oracle/javafx/scenebuilder/app/SplitController.java<|end_filename|>
/*
 * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.app;

import java.util.ArrayList;
import java.util.List;
import javafx.beans.property.DoubleProperty;
import javafx.scene.Node;
import javafx.scene.control.SplitPane;
import javafx.scene.control.SplitPane.Divider;

/**
 *
 */
public class SplitController {

    public enum Target {

        FIRST, LAST
    };

    private final SplitPane splitPane;
    private final Target target;
    private final Node targetNode;
    private double dividerPosition = -1.0;

    public SplitController(SplitPane splitPane, Target target) {
        assert splitPane != null;
        assert splitPane.getItems().size() >= 1;

        this.splitPane = splitPane;
        this.target = target;

        final List<Node> children = splitPane.getItems();
        final int targetIndex = (target == Target.FIRST) ? 0 : children.size() - 1;
        this.targetNode = children.get(targetIndex);
    }

    public DoubleProperty position() {
        final Divider divider = getTargetDivider();
        return divider == null ? null : divider.positionProperty();
    }

    public double getPosition() {
        final Divider divider = getTargetDivider();
        return divider == null ? -1.0 : divider.getPosition();
    }

    public void setPosition(double value) {
        final Divider divider = getTargetDivider();
        if (divider != null) {
            divider.setPosition(value);
        }
        dividerPosition = value;
    }

    public void showTarget() {
        if (isTargetVisible() == false) {
            // Put the target node back in the split pane items
            if (target == Target.FIRST) {
                splitPane.getItems().add(0, targetNode);
            } else {
                splitPane.getItems().add(targetNode);
            }

            // Restore the target divider position (if any)
            final List<Divider> dividers = splitPane.getDividers();
            if ((dividers.isEmpty() == false) && (dividerPosition != -1)) { // (1)
                final Divider divider = getTargetDivider();
                assert divider != null; // Because of (1)
                divider.setPosition(dividerPosition);
            }
        }
    }

    public void hideTarget() {

        if (isTargetVisible()) {

            final List<Divider> dividers = splitPane.getDividers();
            final List<Double> positionsList = asList(splitPane.getDividerPositions());

            // Backup the target divider positions (if any)
            // so we can restore it on showing
            final Divider targetDivider = getTargetDivider();
            if (targetDivider != null) {
                dividerPosition = targetDivider.getPosition();
                int targetDividerIndex = target == Target.FIRST ? 0 : dividers.size() - 1;
                positionsList.remove(targetDividerIndex);
            }

            // Removes the target node from the split pane items
            splitPane.getItems().remove(targetNode);

            // Set back remaining dividers positions if any
            if (positionsList.isEmpty() == false) {
                double[] positionsArray = toArray(positionsList);
                splitPane.setDividerPositions(positionsArray);
            }
        }
    }

    public void toggleTarget() {
        if (isTargetVisible()) {
            hideTarget();
        } else {
            showTarget();
        }
    }

    public void setTargetVisible(boolean visible) {
        if (visible) {
            showTarget();
        } else {
            hideTarget();
        }
    }

    public boolean isTargetVisible() {
        return splitPane.getItems().contains(targetNode);
    }

    private Divider getTargetDivider() {
        final Divider divider;
        final List<Divider> dividers = splitPane.getDividers();
        if (dividers.isEmpty() == false) {
            if (target == Target.FIRST) {
                divider = dividers.get(0);
            } else {
                divider = dividers.get(dividers.size() - 1);
            }
        } else {
            divider = null;
        }
        return divider;
    }

    // Arrays.asList does not work with primitive types
    private static List<Double> asList(double[] array) {
        final List<Double> list = new ArrayList<>(array.length);
        for (double d : array) {
            list.add(d);
        }
        return list;
    }

    // List.toArray does not work with primitive types
    private static double[] toArray(List<Double> list) {
        final double[] array = new double[list.size()];
        for (int i = 0; i < list.size(); i++) {
            array[i] = list.get(i);
        }
        return array;
    }
}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/css/CssValueConverter.java<|end_filename|>
/*
 * Copyright (c) 2017 Gluon and/or its affiliates.
 * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.kit.editor.panel.css;

import com.oracle.javafx.scenebuilder.kit.editor.panel.inspector.editors.EditorUtils;
import com.oracle.javafx.scenebuilder.kit.metadata.util.ColorEncoder;
import com.oracle.javafx.scenebuilder.kit.util.MathUtils;
import javafx.css.Declaration;
import javafx.css.Rule;
import javafx.css.Size;
import javafx.css.converter.PaintConverter;
import javafx.css.converter.PaintConverter.LinearGradientConverter;
import javafx.css.converter.DeriveColorConverter;
import javafx.css.converter.DeriveSizeConverter;
import javafx.css.converter.LadderConverter;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import javafx.css.ParsedValue;
import javafx.geometry.Insets;
import javafx.geometry.Side;
import javafx.scene.effect.Effect;
import javafx.scene.image.Image;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.layout.BackgroundImage;
import javafx.scene.layout.BackgroundSize;
import javafx.scene.layout.Border;
import javafx.scene.layout.BorderImage;
import javafx.scene.layout.BorderStroke;
import javafx.scene.layout.BorderWidths;
import javafx.scene.layout.CornerRadii;
import javafx.scene.paint.Color;
import javafx.scene.paint.Paint;
import javafx.scene.text.Font;

/**
 *
 * @treatAsPrivate
 */
public class CssValueConverter {

    private static final List<String> STRING_VALUE = new ArrayList<>();
    private static final List<String> SINGLE_WHEN_EQUALITY = new ArrayList<>();

    private CssValueConverter() {
        assert false;
    }

    @SuppressWarnings("rawtypes")
    public static Object convert(ParsedValue pv) {
        Object value = null;
        if (pv == null) {
            return null;
        }

        if (pv.getConverter() != null) {
            try {
                @SuppressWarnings("unchecked")
                Object converted = pv.getConverter().convert(pv, null);
                value = converted;
            } catch (RuntimeException ex) {
                // OK, can't be resolved, a lookup possibly
            }
        } else {
            value = pv.getValue();
        }
        if (value instanceof ParsedValue) {
            value = convert((ParsedValue) value);
        }
        return value;
    }

    // Retrieve a CSS String from a value set thanks to CSS
    public static String toCssString(String property, Rule rule, Object fxValue) {
        try {
            return getValue(property, rule, fxValue);
        } catch (IllegalArgumentException ex) {
            return getValue(property, null, fxValue);
        }
    }

    public static String toCssString(String property, Object fxValue) {
        return getValue(property, null, fxValue);
    }

    public static String toCssString(Object fxValue) {
        return getValue(null, null, fxValue);
    }

    // Retrieve the value for a sub property.
    public static Object getSubPropertyValue(String property, Object value) {
        if (value instanceof Collection) {
            Collection<?> values = (Collection<?>) value;
            List<Object> subValues = new ArrayList<>();
            for (Object bf : values) {
                subValues.add(getSubPropertyValue(property, bf));
            }
            return subValues;
        } else if (value != null && value.getClass().isArray()) {
            Object newArray = Array.newInstance(value.getClass().getComponentType(), Array.getLength(value));
            for (int i = 0; i < Array.getLength(value); i++) {
                Array.set(newArray, i, getSubPropertyValue(property, Array.get(value, i)));
            }
            return newArray;

            //
            // Background
            //
        } else if (value instanceof Background) {
            Background background = (Background) value;
            if (background.getFills() != null) {
                return getSubPropertyValue(property, background.getFills());
            } else if (background.getImages() != null) {
                return getSubPropertyValue(property, background.getImages());
            }
        } else if (value instanceof BackgroundFill) {
            return subBackgroundFill(property, (BackgroundFill) value);
        } else if (value instanceof BackgroundImage) {
            return subBackgroundImage(property, (BackgroundImage) value);

            //
            // Border
            //
        } else if (value instanceof Border) {
            Border border = (Border) value;
            if (border.getStrokes() != null) {
                return getSubPropertyValue(property, border.getStrokes());
            } else if (border.getImages() != null) {
                return getSubPropertyValue(property, border.getImages());
            }
        } else if (value instanceof BorderStroke) {
            return subBorderStroke(property, (BorderStroke) value);
        } else if (value instanceof BorderImage) {
            return subBorderImage(property, (BorderImage) value);

            //
            // Font
            //
        } else if (value instanceof Font) {
            return subFont(property, (Font) value);
        }
        return getValue(property, null, value);
    }

    static {
        STRING_VALUE.add("-fx-skin");//NOI18N
        STRING_VALUE.add("-fx-shape");//NOI18N
    }

    private static String format(String property, String value) {
        if (STRING_VALUE.contains(property)) {
            return "\"" + value + "\"";//NOI18N
        } else {
            return value;
        }
    }

    // FX to String value transformation entry point.
    private static String getValue(String property, Rule r, Object eventValue) throws IllegalArgumentException {
        if (r == null) {
            return format(property, retrieveValue(property, eventValue));
        }

        for (Declaration d : r.getDeclarations()) {
            if (d.getProperty().equals(property)) {
                if (property.equals("-fx-background-radius") || property.equals("-fx-border-radius")) { //NOI18N
                    return format(property, getRadiusCssString(property, d.getParsedValue()));
                } else {
                    return format(property, getCssString(property, d.getParsedValue()));
                }
            }
        }
        throw new IllegalArgumentException("Can't compute a value");//NOI18N
    }

    static {
        SINGLE_WHEN_EQUALITY.add("-fx-padding"); //NOI18N
        SINGLE_WHEN_EQUALITY.add("-fx-background-radius"); //NOI18N
        SINGLE_WHEN_EQUALITY.add("-fx-background-insets"); //NOI18N
        SINGLE_WHEN_EQUALITY.add("-fx-border-color"); //NOI18N
        SINGLE_WHEN_EQUALITY.add("-fx-border-radius"); //NOI18N
        SINGLE_WHEN_EQUALITY.add("-fx-border-insets"); //NOI18N
        SINGLE_WHEN_EQUALITY.add("-fx-border-image-insets"); //NOI18N
        SINGLE_WHEN_EQUALITY.add("-fx-border-image-slice"); //NOI18N
        SINGLE_WHEN_EQUALITY.add("-fx-border-image-width"); //NOI18N

    }

    private static boolean singleForEquality(String prop) {
        return SINGLE_WHEN_EQUALITY.contains(prop);
    }

    // The difference between retrieveValue and getCssStringValue
    // is that the ParsedValue is converted in the retrieveValue case, and is not converted in the 
    // getCssStringValue
    // When converting, we loose the CSS textual format present in the CSS source,
    // for instance the lookup information, or 'em' unit.
    @SuppressWarnings("rawtypes")
    private static String getCssString(String property, ParsedValue value) {

        // TODO : this method should be rewritten in a cleaner way...
        if (value == null) {
            return "null"; //NOI18N
        }

        // I don't like that but this is needed to only have a conversion for gradient.
        // The gradient.toString is much better than the ParsedValue.toString.
        if (value.getConverter() instanceof LinearGradientConverter
                || value.getConverter() instanceof PaintConverter.RadialGradientConverter) {

            try {
                @SuppressWarnings("unchecked")//NOI18N
                Object converted = value.getConverter().convert(value, null);

                return toCssString(converted);
            } catch (RuntimeException ex) {
            }
        }

        Object obj = value.getValue();
        if (obj instanceof ParsedValue) {
            return getCssString(property, (ParsedValue) obj);
        }
        StringBuilder builder = new StringBuilder();
        boolean isDerive = value.getConverter() instanceof DeriveColorConverter || value.getConverter() instanceof DeriveSizeConverter;
        boolean isLadder = value.getConverter() instanceof LadderConverter;
        if (isDerive) {
            builder.append("derive("); //NOI18N
        }
        if (isLadder) {
            builder.append("ladder("); //NOI18N
        }
        if (obj instanceof ParsedValue[]) {
            ParsedValue[] array = (ParsedValue[]) obj;
            boolean isArrayValue = false;
            if (array.length >= 1) {
                ParsedValue pval = array[0];
                Object val = null;
                if (pval != null) {
                    if (pval.getConverter() instanceof LinearGradientConverter
                            || pval.getConverter() instanceof PaintConverter.RadialGradientConverter) {
                        val = null;
                    } else {
                        val = pval.getValue();
                    }
                }
                isArrayValue = val != null && val.getClass().isArray();
            }
            boolean singleForEquality = singleForEquality(property) && !isArrayValue;
            StringBuilder b = new StringBuilder();
            if (singleForEquality) {
                String latest = null;
                boolean areEquals = true;
                List<String> values = new ArrayList<>(array.length);
                for (ParsedValue v : array) {
                    String current = getCssString(property, v);
                    values.add(current);
                    areEquals &= (latest == null || current.equals(latest));
                    latest = current;
                }
                if (areEquals) {
                    String val = values.get(0);
                    val = removeDotZeroPxPercent(val);
                    b.append(val);
                } else {
                    for (int i = 0; i < values.size(); i++) {
                        b.append(values.get(i));
                        if (i < array.length - 1) {
                            b.append(" "); //NOI18N
                        }
                    }
                }
            } else {
                for (int i = 0; i < array.length; i++) {
                    ParsedValue v = array[i];
                    String val = getCssString(property, v);
                    val = removeDotZeroPxPercent(val);
                    b.append(val);
                    if ((i < array.length - 1) && val.length() > 0) {
                        b.append(", "); //NOI18N
                    }
                }
            }

            builder.append(b.toString());
        } else {
            if (obj instanceof ParsedValue[][]) {
                ParsedValue[][] arr = (ParsedValue[][]) obj;
                for (int i = 0; i < arr.length; i++) {
                    String val = retrieveValue(property, arr[i]);
                    builder.append(val);
                    if ((i < arr.length - 1) && val.length() > 0) {
                        builder.append(", "); //NOI18N
                    }
                }
            } else {
                builder.append(retrieveValue(property, obj));
            }
        }
        if (isDerive || isLadder) {
            builder.append(")"); //NOI18N
        }
        return builder.toString();
    }

    @SuppressWarnings("rawtypes")
    private static String getRadiusCssString(String property, ParsedValue value) {
        // TODO : Ideally should be included in the generic getCssString() method
        
        // See  http://www.w3.org/TR/css3-background/#the-border-radius 
        
        assert property.equals("-fx-background-radius") || property.equals("-fx-border-radius"); //NOI18N
        StringBuilder sbAll = new StringBuilder();
        Object obj = value.getValue();
        if (!(obj instanceof ParsedValue[])) {
            return null;
        }
        ParsedValue[] pvArray = (ParsedValue[]) obj;
        int index = 0;
        for (ParsedValue pvItem : pvArray) {
            // We have a CornerRadii representation here:
            // double dimension array:
            // 1- horizontal/vertical radii
            // 2- value per corner
            // If all the values are identical, only a single value is used.
            obj = pvItem.getValue();
            if (!(obj instanceof ParsedValue[][])) {
                return null;
            }
            ParsedValue[][] pvArray2 = (ParsedValue[][]) obj;
            StringBuilder sbCornerRadii = new StringBuilder();
            Size initSize = null;
            boolean areEquals = true;
            int index2 = 0;
            for (ParsedValue[] pvArray1 : pvArray2) {
                // horizontal or vertical list 
                for (ParsedValue pvItem2 : pvArray1) {
                    obj = pvItem2.getValue();
                    if (!(obj instanceof Size)) {
                        return null;
                    }
                    Size size = (Size) obj;
                    sbCornerRadii.append(size).append(" "); //NOI18N
                    if (initSize == null) {
                        initSize = size;
                    } else if (!initSize.equals(size)) {
                        areEquals = false;
                    }
                }
                if (index2 != pvArray2.length - 1) {
                    // Separator between the horizontal / vertical lists
                    sbCornerRadii.append(" / "); //NOI18N
                }
                index2++;
            }
            if (areEquals) {
                sbAll.append(initSize);
            } else {
                sbAll.append(sbCornerRadii.toString().trim());
            }
            if (index != pvArray.length - 1) {
                sbAll.append(", "); //NOI18N
            }
            index++;
        }
        return removeDotZeroPxPercent(sbAll.toString());
    }

    private static String retrieveValue(String property, Object eventValue) {
        if (eventValue instanceof ParsedValue) {
            eventValue = convert((ParsedValue<?, ?>) eventValue);
        }

        if (eventValue == null) {
            return "null"; //NOI18N
        }
        StringBuilder builder = new StringBuilder();
        if (eventValue instanceof List) {
            List<?> values = (List<?>) eventValue;
            int length = values.size();
            for (int i = 0; i < length; i++) {
                String val = retrieveValue(property, values.get(i));
                builder.append(val);
                if ((i < length - 1) && val.length() > 0) {
                    builder.append(", "); //NOI18N
                }
            }
        } else if (eventValue.getClass().isArray()) {
            int length = Array.getLength(eventValue);
            for (int i = 0; i < length; i++) {
                String val = retrieveValue(property, Array.get(eventValue, i));
                builder.append(val);
                if ((i < length - 1) && val.length() > 0) {
                    builder.append(", "); //NOI18N
                }
            }
        } else if (eventValue instanceof Background) {
            Background background = (Background) eventValue;
            if (background.getFills() != null) {
                return retrieveValue(property, background.getFills());
            } else if (background.getImages() != null) {
                return retrieveValue(property, background.getImages());
            }
        } else if (eventValue instanceof Border) {
            Border border = (Border) eventValue;
            if (border.getStrokes() != null) {
                return retrieveValue(property, border.getStrokes());
            } else if (border.getImages() != null) {
                return retrieveValue(property, border.getImages());
            }
        } else if (eventValue instanceof BackgroundFill) {
            builder.append(backgroundFillToString(property, (BackgroundFill) eventValue));
        } else if (eventValue instanceof CornerRadii) {
            builder.append(cornerRadiiToString(property, (CornerRadii) eventValue));
        } else if (eventValue instanceof BackgroundImage) {
            builder.append(backgroundImageToString(property, (BackgroundImage) eventValue));
        } else if (eventValue instanceof BorderStroke) {
            builder.append(borderStrokeToString(property, (BorderStroke) eventValue));
        } else if (eventValue instanceof BorderImage) {
            builder.append(borderImageToString(property, (BorderImage) eventValue));
        } else if (eventValue instanceof Font) {
            builder.append(fontToString(property, (Font) eventValue));
        } else if (eventValue instanceof Paint) {
            builder.append(paintToString((Paint) eventValue).toLowerCase(Locale.ROOT));
        } else if (eventValue instanceof Insets) {
            builder.append(insetsValue((Insets) eventValue));
        } else if (eventValue instanceof Effect) {
            builder.append(effectValue((Effect) eventValue));
        } else {
            String str = EditorUtils.valAsStr(eventValue);
            if (str == null) {
                str = "null"; //NOI18N
            } else {
                str = str.replaceAll("\n", " ");//NOI18N
                // Remove memory address if any
                str = str.split("@")[0]; //NOI18N
                str = removeDotZeroPxPercent(str);
            }
            builder.append(str);
        }
        return builder.toString();
    }

    private static String getColorAsWebString(Color c) {
        int red = (int) Math.round(c.getRed() * 255.0);
        int green = (int) Math.round(c.getGreen() * 255.0);
        int blue = (int) Math.round(c.getBlue() * 255.0);
        int alpha = (int) Math.round(c.getOpacity() * 255.0);
        if (alpha == 255) {
            return String.format("#%02x%02x%02x", red, green, blue); //NOI18N
        } else {
            return String.format("#%02x%02x%02x%02x", red, green, blue, alpha); //NOI18N
        }
    }

    private static String getColorAsString(Color color) {
        if (isStandardColor(color)) {
            return getStandardColorAsString(color);
        } else {
            return getColorAsWebString(color);
        }
    }

    private static boolean isStandardColor(Color c) {
        return standardColors.containsKey(c);
    }
    static Map<Color, String> standardColors = ColorEncoder.getStandardColorNames();

    private static String getStandardColorAsString(Color c) {
        return standardColors.get(c);
    }

    private static String backgroundFillToString(String property, BackgroundFill bf) {
        if (property == null) {
            return bf.toString();
        }
        StringBuilder builder = new StringBuilder();
        if (property.equals("-fx-background-color")) { //NOI18N
            Paint p = bf.getFill();
            builder.append(paintToString(p));
        } else {
            if (property.equals("-fx-background-insets")) { //NOI18N
                builder.append(insetsValue(bf.getInsets()));
            } else {
                //the top right, bottom right, bottom left, and top left
                if (property.equals("-fx-background-radius")) { //NOI18N
                    handleCornerRadii(bf.getRadii(), builder);
                }
            }
        }
        return builder.toString();
    }

    private static String cornerRadiiToString(String property, CornerRadii cr) {
        if (property == null) {
            return cr.toString();
        }
        StringBuilder builder = new StringBuilder();
        handleCornerRadii(cr, builder);
        return builder.toString();
    }

    private static String backgroundImageToString(String property, BackgroundImage bi) {
        if (property == null) {
            return bi.toString();
        }
        StringBuilder builder = new StringBuilder();
        if (property.equals("-fx-background-image")) { //NOI18N
            Image p = bi.getImage();
            builder.append(p.getUrl());
        } else {
            if (property.equals("-fx-background-position")) {             //NOI18N
                double left = 0, right = 0, top = 0, bottom = 0;
                if (bi.getPosition().getHorizontalSide() == Side.LEFT) {
                    left = bi.getPosition().getHorizontalPosition();
                } else {
                    right = bi.getPosition().getHorizontalPosition();
                }
                if (bi.getPosition().getVerticalSide() == Side.TOP) {
                    top = bi.getPosition().getVerticalPosition();
                } else {
                    bottom = bi.getPosition().getVerticalPosition();
                }
                builder.append("left:"); //NOI18N
                builder.append(EditorUtils.valAsStr(left));
                builder.append(" right:"); //NOI18N
                builder.append(EditorUtils.valAsStr(right));
                builder.append(" top:"); //NOI18N
                builder.append(EditorUtils.valAsStr(top));
                builder.append(" bottom:"); //NOI18N
                builder.append(EditorUtils.valAsStr(bottom));
            } else {
                if (property.equals("-fx-background-repeat")) {          //NOI18N
                    if (bi.getRepeatX() != null) {
                        builder.append(bi.getRepeatX().toString());
                    } else {
                        if (bi.getRepeatY() != null) {
                            builder.append(bi.getRepeatY().toString());
                        } else {
                            builder.append("unknown repeat"); //NOI18N
                        }
                    }
                } else {
                    if (property.equals("-fx-background-size")) { //NOI18N
                        BackgroundSize bs = bi.getSize();
                        if (bs.isContain()) {
                            builder.append("contain"); //NOI18N
                        } else {
                            if (bs.isCover()) {
                                builder.append("cover"); //NOI18N
                            } else {
                                if (bs.getWidth() == BackgroundSize.AUTO) {
                                    builder.append("width: auto"); //NOI18N
                                } else {
                                    builder.append("width: ").append(EditorUtils.valAsStr(bs.getWidth())); //NOI18N
                                }
                                if (bs.getHeight() == BackgroundSize.AUTO) {
                                    builder.append("height: auto"); //NOI18N
                                } else {
                                    builder.append("height: ").append(EditorUtils.valAsStr(bs.getHeight())); //NOI18N
                                }
                            }
                        }
                    }
                }
            }
        }
        return builder.toString();
    }

    private static String borderImageToString(String property, BorderImage bi) {
        if (property == null) {
            return bi.toString();
        }
        StringBuilder builder = new StringBuilder();
        if (property.equals("-fx-border-image")) { //NOI18N
            Image p = bi.getImage();
            builder.append(p.getUrl());
        } else {
            if (property.equals("-fx-background-position")) {             //NOI18N

            } else {
                if (property.equals("-fx-border-image-repeat")) {          //NOI18N
                    if (bi.getRepeatX() != null) {
                        builder.append(bi.getRepeatX().toString());
                    } else {
                        if (bi.getRepeatY() != null) {
                            builder.append(bi.getRepeatY().toString());
                        } else {
                            builder.append("unknown repeat"); //NOI18N
                        }
                    }
                } else {
                    if (property.equals("-fx-border-image-insets")) { //NOI18N
                        builder.append(insetsValue(bi.getInsets()));
                    } else {
                        if (property.equals("-fx-border-image-width")) { //NOI18N
                            BorderWidths bw = bi.getWidths();
                            if (MathUtils.equals(bw.getTop(), bw.getBottom())
                                    && MathUtils.equals(bw.getLeft(), bw.getRight())) {
                                builder.append(EditorUtils.valAsStr(bw.getTop()));
                            } else {
                                builder.append(EditorUtils.valAsStr(bw.getTop())).append(" "). //NOI18N
                                        append(EditorUtils.valAsStr(bw.getRight())).append(" "). //NOI18N
                                        append(EditorUtils.valAsStr(bw.getBottom())).append(" "). //NOI18N
                                        append(EditorUtils.valAsStr(bw.getLeft()));
                            }
                        } else {
                            if (property.equals("-fx-border-image-slice")) { //NOI18N
                                BorderWidths bw = bi.getSlices();
                                if (MathUtils.equals(bw.getTop(), bw.getBottom())
                                        && MathUtils.equals(bw.getLeft(), bw.getRight())) {
                                    builder.append(EditorUtils.valAsStr(bw.getTop()));
                                } else {
                                    builder.append(EditorUtils.valAsStr(bw.getTop())).append(" "). //NOI18N
                                            append(EditorUtils.valAsStr(bw.getRight())).append(" "). //NOI18N
                                            append(EditorUtils.valAsStr(bw.getBottom())).append(" "). //NOI18N
                                            append(EditorUtils.valAsStr(bw.getLeft()));
                                }
                            }
                        }
                    }
                }
            }
        }
        return builder.toString();
    }

    private static String borderStrokeToString(String property, BorderStroke bs) {
        if (property == null) {
            return bs.toString();
        }
        StringBuilder builder = new StringBuilder();
        //top, right, bottom, and left 
        if (property.equals("-fx-border-color")) { //NOI18N
            if (bs.getTopStroke().equals(bs.getBottomStroke())
                    && bs.getRightStroke().equals(bs.getBottomStroke())
                    && bs.getLeftStroke().equals(bs.getBottomStroke())) {
                builder.append(paintToString(bs.getBottomStroke()));
            } else {
                builder.append(paintToString(bs.getTopStroke())).append(" "); //NOI18N
                builder.append(paintToString(bs.getRightStroke())).append(" "); //NOI18N
                builder.append(paintToString(bs.getBottomStroke())).append(" "); //NOI18N
                builder.append(paintToString(bs.getLeftStroke()));
            }
        } else {
            if (property.equals("-fx-border-insets")) { //NOI18N
                builder.append(insetsValue(bs.getInsets()));
            } else {
                //the top right, bottom right, bottom left, and top left
                if (property.equals("-fx-border-radius")) { //NOI18N
                    handleCornerRadii(bs.getRadii(), builder);
                } else {
                    if (property.equals("-fx-border-style")) { //NOI18N
                        builder.append(bs.getTopStyle().toString()).append(", "); //NOI18N
                        builder.append(bs.getRightStyle().toString()).append(", "); //NOI18N
                        builder.append(bs.getBottomStyle().toString()).append(", "); //NOI18N
                        builder.append(bs.getLeftStyle().toString());
                    } else {
                        if (property.equals("-fx-border-width")) { //NOI18N
                            BorderWidths bw = bs.getWidths();
                            if (MathUtils.equals(bw.getTop(), bw.getBottom())
                                    && MathUtils.equals(bw.getRight(), bw.getBottom())
                                    && MathUtils.equals(bw.getLeft(), bw.getBottom())) {
                                builder.append(EditorUtils.valAsStr(bw.getBottom()));
                            } else {
                                builder.append(EditorUtils.valAsStr(bw.getTop())).append(" "). //NOI18N
                                        append(EditorUtils.valAsStr(bw.getRight())).append(" "). //NOI18N
                                        append(EditorUtils.valAsStr(bw.getBottom())).append(" "). //NOI18N
                                        append(EditorUtils.valAsStr(bw.getLeft()));
                            }
                        }
                    }
                }
            }
        }
        return builder.toString();
    }

    private static String paintToString(Paint p) {
        if (p instanceof Color) {
            return getColorAsString((Color) p).toLowerCase(Locale.ROOT);
        } else {
            String gradient = p.toString();
            // Workaround for RT-22910
            gradient = gradient.replaceAll("0x", "#");//NOI18N
            gradient = removeDotZeroPxPercent(gradient);
            return gradient;
        }
    }

    private static String fontToString(String property, Font font) {
        if (property == null) {
            return removeAllDotZero(font.toString());
        }
        StringBuilder builder = new StringBuilder();
        if (property.equals("-fx-font")) { //NOI18N
            String size = EditorUtils.valAsStr(font.getSize()); //NOI18N
            String previewStr = font.getFamily() + " " + size + "px" //NOI18N
                    + (!font.getName().equals(font.getFamily())
                    && !"Regular".equals(font.getStyle()) //NOI18N
                    ? " (" + font.getStyle() + ")" : ""); //NOI18N
            builder.append(previewStr);
        } else {
            if (property.equals("-fx-font-size")) { //NOI18N
                double p = font.getSize();
                builder.append(EditorUtils.valAsStr(p)).append("px"); //NOI18N

            } else {
                if (property.equals("-fx-font-family")) { //NOI18N
                    builder.append(font.getFamily());
                } else {
                    if (property.equals("-fx-font-weight")) { //NOI18N
                        // There is no such property.
                        builder.append(removeAllDotZero(font.toString()));
                    } else {
                        if (property.equals("-fx-font-style")) { //NOI18N
                            builder.append(font.getStyle());
                        }
                    }
                }
            }
        }
        return builder.toString();
    }

    private static String insetsValue(Insets insets) {
        if (MathUtils.equals(insets.getBottom(), insets.getLeft())
                && MathUtils.equals(insets.getRight(), insets.getLeft())
                && MathUtils.equals(insets.getTop(), insets.getLeft())) {
            return EditorUtils.valAsStr(insets.getLeft());
        } else {
            return EditorUtils.valAsStr(insets.getTop()) + " " + EditorUtils.valAsStr(insets.getRight()) //NOI18N
                    + " " + EditorUtils.valAsStr(insets.getBottom()) + " " + EditorUtils.valAsStr(insets.getLeft()); //NOI18N
        }
    }

    private static String effectValue(Effect effect) {
        StringBuilder strBuild = new StringBuilder();
        Effect adding = effect;
        while (adding != null) {
            strBuild.append(adding.getClass().getSimpleName());
            adding = getEffectInput(adding);
            if (adding != null) {
                strBuild.append(", "); //NOI18N
            }
        }
        return strBuild.toString();
    }

    private static Object subBackgroundFill(String property, BackgroundFill bf) {
        if (property == null) {
            return bf;
        }
        if (property.equals("-fx-background-color")) { //NOI18N
            return bf.getFill();
        } else {
            if (property.equals("-fx-background-insets")) { //NOI18N
                return bf.getInsets();
            } else {
                return backgroundFillToString(property, bf);
            }
        }
    }

    private static Object subBackgroundImage(String property, BackgroundImage bi) {
        if (property == null) {
            return bi;
        }
        if (property.equals("-fx-background-image")) { //NOI18N
            return bi.getImage();
        } else {
            return backgroundImageToString(property, bi);
        }
    }

    private static Object subBorderImage(String property, BorderImage bi) {
        if (property == null) {
            return bi;
        }
        if (property.equals("-fx-border-image")) { //NOI18N
            return bi.getImage();
        } else {
            return borderImageToString(property, bi);
        }
    }

    private static Object subBorderStroke(String property, BorderStroke bs) {
        if (property == null) {
            return bs;
        }
        //top, right, bottom, and left 
        if (property.equals("-fx-border-color")) { //NOI18N
            if (bs.getTopStroke().equals(bs.getBottomStroke())
                    && bs.getRightStroke().equals(bs.getBottomStroke())
                    && bs.getLeftStroke().equals(bs.getBottomStroke())) {
                return bs.getBottomStroke();
            } else {
                Paint[] p = new Paint[4];
                p[0] = bs.getTopStroke();
                p[1] = bs.getRightStroke();
                p[2] = bs.getBottomStroke();
                p[3] = bs.getLeftStroke();
                return p;
            }
        } else {
            if (property.equals("-fx-border-insets")) { //NOI18N
                return bs.getInsets();
            } else {
                return borderStrokeToString(property, bs);
            }
        }
    }

    private static Object subFont(String property, Font font) {
        if (property == null) {
            return font;
        }
        if (property.equals("-fx-font-size")) { //NOI18N
            return EditorUtils.valAsStr(font.getSize());
        } else {
            if (property.equals("-fx-font-style")) { //NOI18N
                return font.getStyle();
            } else {
                if (property.equals("-fx-font-family")) { //NOI18N
                    return font.getFamily();
                } else {
                    if (property.equals("-fx-font-weight")) { //NOI18N
                        // No font weight
                        return font.getFamily() + " " + font.getStyle(); //NOI18N
                    } else {
                        return font;
                    }
                }
            }
        }
    }

    private static String removeDotZeroPxPercent(String str) {
        // Remove ".0" in strings, for "px" and "%" notations
        str = str.replaceAll("\\.0px", "px"); //NOI18N
        str = str.replaceAll("\\.0em", "em"); //NOI18N
        str = str.replaceAll("\\.0\\%", "%"); //NOI18N
        return str;
    }

    private static String removeAllDotZero(String str) {
        // Remove all ".0" in string
        str = str.replaceAll("\\.0", ""); //NOI18N
        return str;
    }

    private static void handleCornerRadii(CornerRadii cr, StringBuilder builder) {
        // Each radius has a vertical and horizontal radius
        // See  http://www.w3.org/TR/css3-background/#the-border-radius 

        double topLeftH = cr.getTopLeftHorizontalRadius();
        double topLeftV = cr.getTopLeftVerticalRadius();
        double topRightH = cr.getTopRightHorizontalRadius();
        double topRightV = cr.getTopRightVerticalRadius();
        double bottomLeftH = cr.getBottomLeftHorizontalRadius();
        double bottomLeftV = cr.getBottomLeftVerticalRadius();
        double bottomRightH = cr.getBottomRightHorizontalRadius();
        double bottomRightV = cr.getBottomRightVerticalRadius();

        if (MathUtils.equals(topLeftH, topLeftV) && MathUtils.equals(topRightH, topRightV)
                && MathUtils.equals(bottomLeftH, bottomLeftV) && MathUtils.equals(bottomRightH, bottomRightV)) {
            if (MathUtils.equals(topLeftH, topRightH) && MathUtils.equals(topRightH, bottomLeftH)
                    && MathUtils.equals(bottomLeftH, bottomRightH)) {
                // Same radius for all => single value
                builder.append(EditorUtils.valAsStr(topLeftH));
            } else {
                // Same value for vertical and horizontal radii 
                // => 4 values for topLeft, topRight, bottomLeft, bottomRight
                builder.append(EditorUtils.valAsStr(topLeftH)).append(" "). //NOI18N
                        append(EditorUtils.valAsStr(topRightH)).append(" "). //NOI18N
                        append(EditorUtils.valAsStr(bottomRightH)).append(" "). //NOI18N
                        append(EditorUtils.valAsStr(bottomLeftH));
            }
        } else {
            // Separate value for each.
            // Syntax: "horizontal values / vertical values"
            builder.append(EditorUtils.valAsStr(topLeftH)).append(" "). //NOI18N
                    append(EditorUtils.valAsStr(topRightH)).append(" "). //NOI18N
                    append(EditorUtils.valAsStr(bottomRightH)).append(" "). //NOI18N
                    append(EditorUtils.valAsStr(bottomLeftH)).
                    append(" / ").//NOI18N
                    append(EditorUtils.valAsStr(topLeftV)).append(" "). //NOI18N
                    append(EditorUtils.valAsStr(topRightV)).append(" "). //NOI18N
                    append(EditorUtils.valAsStr(bottomRightV)).append(" "). //NOI18N
                    append(EditorUtils.valAsStr(bottomLeftV));
        }
    }

//    @SuppressWarnings({"BroadCatchBlock", "TooBroadCatch"}) //NOI18N
    private static Effect getEffectInput(Effect effect) {
        Effect found = null;
        try {
            found = (Effect) effect.getClass().getMethod("getInput").invoke(effect); //NOI18N
        } catch (Throwable e) {
            // DO NOT use multi-catch syntax here, this generates a FindBugs Warning (because of SecurityException catching)
//                e.printStackTrace();
            try {
                found = (Effect) effect.getClass().getMethod("getContentInput").invoke(effect); //NOI18N
            } catch (Throwable ee) {
                // DO NOT use multi-catch syntax here, this generates a FindBugs Warning (because of SecurityException catching)
//                    ee.printStackTrace();
            }
        }
        return found;
    }
}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/fxom/glue/GlueDocument.java<|end_filename|>
/*
 * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.kit.fxom.glue;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 *
 * 
 */
public class GlueDocument extends GlueNode {
    
    private GlueElement rootElement;
    private final List<GlueAuxiliary> header = new ArrayList<>();
    
    public GlueDocument() {
    }
    
    public GlueDocument(String xmlText) throws IOException {
        assert xmlText != null;
        if (isEmptyXmlText(xmlText) == false) {
            final GlueLoader loader = new GlueLoader(this);
            loader.load(xmlText);
            adjustRootElementIndentation();
        }
    }
    
    public GlueElement getRootElement() {
        return rootElement;
    }

    public void setRootElement(GlueElement newRootElement) {
        if ((newRootElement != null) && (newRootElement.getParent() != null)) {
            newRootElement.removeFromParent();
        }
        this.rootElement = newRootElement;
    }

    public List<GlueAuxiliary> getHeader() {
        return header;
    }
    
    public void updateIndent() {
        if (rootElement != null) {
            rootElement.updateIndent(0);
        }
    }
    
    
    /*
     * Utilities
     */

    public List<GlueInstruction> collectInstructions(String target) {
        final List<GlueInstruction> result = new ArrayList<>();
        
        assert target != null;
        
        for (GlueAuxiliary auxiliary : header) {
            if (auxiliary instanceof GlueInstruction) {
                final GlueInstruction i = (GlueInstruction) auxiliary;
                if (target.equals(i.getTarget())) {
                    result.add(i);
                }
            }
        }
        
        return result;
    }
    
    public static boolean isEmptyXmlText(String xmlText) {
        assert xmlText != null;
        return xmlText.trim().isEmpty();
    }
    
    /*
     * Object
     */
    
    @Override
    public String toString() {
        final String result;
        if (rootElement == null) {
            result = ""; //NOI18N
        } else {
            final GlueSerializer serializer = new GlueSerializer(this);
            result = serializer.toString();
        }
        return result;
    }
    
    
    /*
     * Private
     */
    
    private void adjustRootElementIndentation() {
        /*
         * By default, if a root element is empty and expressed like this:
         *     <AnchorPane />
         * indentation logic would keep the upcoming children on the same line:
         *     <AnchorPane> <children> <Button/> </children> </AnchorPane>.
         * 
         * With the adjustment below, indentation logic will produce:
         *     <AnchorPane> 
         *        <children>
         *           <Button />
         *        </children>
         *     </AnchorPane>
         */
        
        if ((rootElement != null) && rootElement.getChildren().isEmpty()) {
            if (rootElement.getFront().isEmpty()) {
                rootElement.getFront().add(new GlueCharacters(this, GlueCharacters.Type.TEXT, "\n")); //NOI18N
            }
            if (rootElement.getTail().isEmpty()) {
                rootElement.getTail().add(new GlueCharacters(this, GlueCharacters.Type.TEXT, "\n")); //NOI18N
            }
        }
    }
}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/job/gridpane/AddColumnConstraintsJob.java<|end_filename|>
/*
 * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.kit.editor.job.gridpane;

import com.oracle.javafx.scenebuilder.kit.editor.EditorController;
import com.oracle.javafx.scenebuilder.kit.editor.job.BatchDocumentJob;
import com.oracle.javafx.scenebuilder.kit.editor.job.Job;
import com.oracle.javafx.scenebuilder.kit.editor.job.JobUtils;
import com.oracle.javafx.scenebuilder.kit.editor.job.gridpane.GridPaneJobUtils.Position;
import com.oracle.javafx.scenebuilder.kit.editor.job.atomic.AddPropertyJob;
import com.oracle.javafx.scenebuilder.kit.editor.job.atomic.AddPropertyValueJob;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMDocument;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMInstance;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMObject;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMProperty;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMPropertyC;
import com.oracle.javafx.scenebuilder.kit.metadata.util.DesignHierarchyMask;
import com.oracle.javafx.scenebuilder.kit.metadata.util.PropertyName;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javafx.scene.layout.ColumnConstraints;

/**
 * Job invoked when adding column constraints.
 */
public class AddColumnConstraintsJob extends BatchDocumentJob {

    // Key = target GridPane instance
    // Value = list of target column indexes for this GridPane
    private final Map<FXOMObject, Set<Integer>> targetGridPanes;
    private final Position position;
    // If the selected column is associated to an existing constraints, 
    // we duplicate the existing constraints.
    // Otherwise, we use the default values below.
    private static final double defaultMinWidth = 10.0;
    private static final double defaultPrefWidth = 100.0;

    public AddColumnConstraintsJob(
            final EditorController editorController,
            final Position position,
            final Map<FXOMObject, Set<Integer>> targetGridPanes) {
        super(editorController);
        this.position = position;
        this.targetGridPanes = targetGridPanes;
    }

    @Override
    protected List<Job> makeSubJobs() {

        final List<Job> result = new ArrayList<>();

        // Add column constraints job
        assert targetGridPanes.isEmpty() == false;
        for (FXOMObject targetGridPane : targetGridPanes.keySet()) {
            assert targetGridPane instanceof FXOMInstance;
            final Set<Integer> targetIndexes = targetGridPanes.get(targetGridPane);
            result.addAll(addColumnConstraints((FXOMInstance) targetGridPane, targetIndexes));
        }
        
        return result;
    }
    
    @Override
    protected String makeDescription() {
        return "Add Column Constraints"; //NOI18N
    }
    
    private Set<Job> addColumnConstraints(
            final FXOMInstance targetGridPane,
            final Set<Integer> targetIndexes) {

        final Set<Job> result = new LinkedHashSet<>();
        final FXOMDocument fxomDocument = getEditorController().getFxomDocument();

        // Retrieve the constraints property for the specified target GridPane
        final PropertyName propertyName = new PropertyName("columnConstraints"); //NOI18N
        FXOMProperty constraintsProperty = targetGridPane.getProperties().get(propertyName);
        if (constraintsProperty == null) {
            constraintsProperty = new FXOMPropertyC(fxomDocument, propertyName);
        }
        assert constraintsProperty instanceof FXOMPropertyC;

        final DesignHierarchyMask mask = new DesignHierarchyMask(targetGridPane);

        int shiftIndex = 0;
        int constraintsSize = mask.getColumnsConstraintsSize();
        for (int targetIndex : targetIndexes) {

            // Retrieve the index for the new constraints to be added
            int addedIndex = targetIndex + shiftIndex;
            if (position == Position.AFTER) {
                addedIndex++;
            }

            final FXOMObject targetConstraints
                    = mask.getColumnConstraintsAtIndex(targetIndex);
            // The target index is associated to an existing constraints value :
            // we add a new constraints using the values of the existing one
            if (targetConstraints != null) {
                assert targetConstraints instanceof FXOMInstance;
                // Create new constraints instance with same values as the target one
                final FXOMInstance addedConstraints = makeColumnConstraintsInstance(
                        (FXOMInstance) targetConstraints);

                final Job addValueJob = new AddPropertyValueJob(
                        addedConstraints,
                        (FXOMPropertyC) constraintsProperty,
                        addedIndex, getEditorController());
                result.add(addValueJob);
            } //
            // The target index is not associated to an existing constraints value :
            // - we add new empty constraints from the last existing one to the added index (excluded)
            // - we add a new constraints with default values for the added index
            else {
                for (int index = constraintsSize; index < addedIndex; index++) {
                    // Create new empty constraints for the exisiting columns
                    final FXOMInstance addedConstraints = makeColumnConstraintsInstance();
                    final Job addValueJob = new AddPropertyValueJob(
                            addedConstraints,
                            (FXOMPropertyC) constraintsProperty,
                            index, getEditorController());
                    result.add(addValueJob);
                }
                // Create new constraints with default values for the new added column
                final FXOMInstance addedConstraints = makeColumnConstraintsInstance();
                JobUtils.setMinWidth(addedConstraints, ColumnConstraints.class, defaultMinWidth);
                JobUtils.setPrefWidth(addedConstraints, ColumnConstraints.class, defaultPrefWidth);
                final Job addValueJob = new AddPropertyValueJob(
                        addedConstraints,
                        (FXOMPropertyC) constraintsProperty,
                        addedIndex, getEditorController());
                result.add(addValueJob);
                constraintsSize = addedIndex + 1;
            }
            shiftIndex++;
        }

        // Add the constraints property to the target GridPane if not already there.
        // IMPORTANT :
        // Note that the AddPropertyJob must be called after the AddPropertyValueJob.
        if (constraintsProperty.getParentInstance() == null) {
            final Job addPropertyJob = new AddPropertyJob(
                    constraintsProperty,
                    targetGridPane,
                    -1, getEditorController());
            result.add(addPropertyJob);
        }
        
        return result;
    }

    private FXOMInstance makeColumnConstraintsInstance() {

        // Create new constraints instance
        final FXOMDocument newDocument = new FXOMDocument();
        final FXOMInstance result
                = new FXOMInstance(newDocument, ColumnConstraints.class);
        newDocument.setFxomRoot(result);
        result.moveToFxomDocument(getEditorController().getFxomDocument());

        return result;
    }

    private FXOMInstance makeColumnConstraintsInstance(final FXOMInstance constraints) {

        assert constraints != null;
        assert constraints.getDeclaredClass() == ColumnConstraints.class;

        // Create new constraints instance
        final FXOMInstance result = makeColumnConstraintsInstance();

        // Set the new column constraints values with the values of the specified instance
        final boolean fillWidth = JobUtils.getFillWidth(constraints, ColumnConstraints.class);
        final double maxWidth = JobUtils.getMaxWidth(constraints, ColumnConstraints.class);
        final double minWidth = JobUtils.getMinWidth(constraints, ColumnConstraints.class);
        final double percentWidth = JobUtils.getPercentWidth(constraints, ColumnConstraints.class);
        final double prefWidth = JobUtils.getPrefWidth(constraints, ColumnConstraints.class);
        final String halignment = JobUtils.getHAlignment(constraints, ColumnConstraints.class);
        final String hgrow = JobUtils.getHGrow(constraints, ColumnConstraints.class);

        JobUtils.setFillWidth(result, ColumnConstraints.class, fillWidth);
        JobUtils.setMaxWidth(result, ColumnConstraints.class, maxWidth);
        // If the existing constraints minWidth is too small, we use the default one
        JobUtils.setMinWidth(result, ColumnConstraints.class, Math.max(minWidth, defaultMinWidth));
        JobUtils.setPercentWidth(result, ColumnConstraints.class, percentWidth);
        // If the existing constraints prefWidth is too small, we use the default one
        JobUtils.setPrefWidth(result, ColumnConstraints.class, Math.max(prefWidth, defaultPrefWidth));
        JobUtils.setHAlignment(result, ColumnConstraints.class, halignment);
        JobUtils.setHGrow(result, ColumnConstraints.class, hgrow);

        return result;
    }
}


<|start_filename|>kit/src/test/java/com/oracle/javafx/scenebuilder/kit/skeleton/SkeletonBufferTest.java<|end_filename|>
/*
 * Copyright (c) 2016, 2021, Gluon and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation and Gluon nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.kit.skeleton;

import com.oracle.javafx.scenebuilder.kit.editor.EditorController;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMDocument;
import org.junit.Test;

import java.io.IOException;
import java.net.URL;

import static org.junit.Assert.assertEquals;

/**
 * Unit test for {@link SkeletonBuffer#toString()}.
 */
public class SkeletonBufferTest {

    @Test
    public void testControllerWithoutPackageName() throws IOException {
        // given
        SkeletonBuffer skeletonBuffer = load("ControllerWithoutPackage.fxml");

        // when
        String skeleton = skeletonBuffer.toString();

        // then
        String firstLine = skeleton.substring(0, skeleton.indexOf(AbstractSkeletonCreator.NL));
        assertEquals("", firstLine);
    }

    @Test
    public void testControllerWithSimplePackageName() throws IOException {
        // given
        SkeletonBuffer skeletonBuffer = load("ControllerWithSimplePackage.fxml");

        // when
        String skeleton = skeletonBuffer.toString();

        // then
        String firstLine = skeleton.substring(0, skeleton.indexOf(AbstractSkeletonCreator.NL));
        assertEquals("package com;", firstLine);
    }

    @Test
    public void testControllerWithAdvancedPackageName() throws IOException {
        // given
        SkeletonBuffer skeletonBuffer = load("ControllerWithAdvancedPackage.fxml");

        // when
        String skeleton = skeletonBuffer.toString();

        // then
        String firstLine = skeleton.substring(0, skeleton.indexOf(AbstractSkeletonCreator.NL));
        assertEquals("package com.example.app.view;", firstLine);
    }

    private SkeletonBuffer load(String fxmlFile) throws IOException {
        EditorController editorController = new EditorController();
        final URL fxmlURL = SkeletonBufferTest.class.getResource(fxmlFile);
        final String fxmlText = FXOMDocument.readContentFromURL(fxmlURL);
        editorController.setFxmlTextAndLocation(fxmlText, fxmlURL, false);
        return new SkeletonBuffer(editorController.getFxomDocument(), "test");
    }
}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/inspector/editors/Point3DEditor.java<|end_filename|>
/*
 * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.kit.editor.panel.inspector.editors;

import com.oracle.javafx.scenebuilder.kit.metadata.property.ValuePropertyMetadata;

import java.util.Set;

import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.geometry.Point3D;
import javafx.scene.Node;
import javafx.scene.Parent;

/**
 * Point3D editor (for x/y/z fields).
 * (used for instance by Transforms.rotationAxis property)
 *
 */
public class Point3DEditor extends PropertyEditor {

    private Parent root;
    @FXML
    private DoubleField xDf;
    @FXML
    private DoubleField yDf;
    @FXML
    private DoubleField zDf;
    DoubleField[] doubleFields = new DoubleField[3];

    public Point3DEditor(ValuePropertyMetadata propMeta, Set<Class<?>> selectedClasses) {
        super(propMeta, selectedClasses);
        initialize();
    }

    //Method to please FindBugs
    private void initialize() {
        root = EditorUtils.loadFxml("Point3DEditor.fxml", this);//NOI18N

        doubleFields[0] = xDf;
        doubleFields[1] = yDf;
        doubleFields[2] = zDf;
        for (DoubleField doubleField : doubleFields) {
            EventHandler<ActionEvent> valueListener = event -> userUpdateValueProperty(getValue());
            setNumericEditorBehavior(this, doubleField, valueListener, false);
        }
        setLayoutFormat(PropertyEditor.LayoutFormat.SIMPLE_LINE_BOTTOM);
    }

    @Override
    public Node getValueEditor() {
        return super.handleGenericModes(root);
    }

    @Override
    public Object getValue() {
        Double[] values = new Double[3];
        int index = 0;
        for (DoubleField doubleField : doubleFields) {
            String val = doubleField.getText();
            if (val.isEmpty()) {
                val = "0"; //NOI18N
                doubleField.setText(val);
            } else {
                try {
                    Double.parseDouble(val);
                } catch (NumberFormatException e) {
                    // should not happen, DoubleField should prevent any error
                    return null;
                }
            }
            values[index] = Double.valueOf(val);
            index++;
        }
        return new Point3D(values[0], values[1], values[2]);
    }

    @Override
    public void setValue(Object value) {
        assert value != null;
        assert value instanceof Point3D;

        setValueGeneric(value);
        if (isSetValueDone()) {
            return;
        }

        Point3D point3D = (Point3D) value;
        xDf.setText(EditorUtils.valAsStr(point3D.getX()));
        yDf.setText(EditorUtils.valAsStr(point3D.getY()));
        zDf.setText(EditorUtils.valAsStr(point3D.getZ()));
    }

    @Override
    public void reset(ValuePropertyMetadata propMeta, Set<Class<?>> selectedClasses) {
        super.reset(propMeta, selectedClasses);
        setLayoutFormat(PropertyEditor.LayoutFormat.SIMPLE_LINE_BOTTOM);
    }

    @Override
    protected void valueIsIndeterminate() {
        for (DoubleField doubleField : doubleFields) {
            handleIndeterminate(doubleField);
        }
    }

    @Override
    public void requestFocus() {
        EditorUtils.doNextFrame(() -> xDf.requestFocus());
    }

}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/job/TrimSelectionJob.java<|end_filename|>
/*
 * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.kit.editor.job;

import com.oracle.javafx.scenebuilder.kit.editor.job.atomic.ToggleFxRootJob;
import com.oracle.javafx.scenebuilder.kit.editor.job.atomic.ModifyFxControllerJob;
import com.oracle.javafx.scenebuilder.kit.editor.EditorController;
import com.oracle.javafx.scenebuilder.kit.i18n.I18N;
import com.oracle.javafx.scenebuilder.kit.editor.job.atomic.RemoveObjectJob;
import com.oracle.javafx.scenebuilder.kit.editor.selection.AbstractSelectionGroup;
import com.oracle.javafx.scenebuilder.kit.editor.selection.ObjectSelectionGroup;
import com.oracle.javafx.scenebuilder.kit.editor.selection.Selection;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMDocument;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMFxIdIndex;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMInstance;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMObject;
import java.util.ArrayList;
import java.util.List;

/**
 *
 */
public class TrimSelectionJob extends BatchSelectionJob {

    public TrimSelectionJob(EditorController editorController) {
        super(editorController);
    }

    @Override
    protected List<Job> makeSubJobs() {
        final List<Job> result = new ArrayList<>();

        if (canTrim()) {

            final Selection selection = getEditorController().getSelection();
            assert selection.getGroup() instanceof ObjectSelectionGroup; // Because (1)
            final ObjectSelectionGroup osg = (ObjectSelectionGroup) selection.getGroup();
            assert osg.getItems().size() == 1;
            final FXOMObject oldRoot = getEditorController().getFxomDocument().getFxomRoot();
            final FXOMObject candidateRoot = osg.getItems().iterator().next();

            /*
             *  This job is composed of subjobs:
             *      0) Remove fx:controller/fx:root (if defined) from the old root object if any
             *      1) Unselect the candidate
             *          => ClearSelectionJob
             *      2) Disconnect the candidate from its existing parent
             *          => DeleteObjectJob
             *      3) Set the candidate as the root of the document
             *          => SetDocumentRootJob
             *      4) Add fx:controller/fx:root (if defined) to the new root object
             */
            assert oldRoot instanceof FXOMInstance;
            boolean isFxRoot = ((FXOMInstance) oldRoot).isFxRoot();
            final String fxController = oldRoot.getFxController();
            // First remove the fx:controller/fx:root from the old root object
            if (isFxRoot) {
                final ToggleFxRootJob fxRootJob = new ToggleFxRootJob(getEditorController());
                result.add(fxRootJob);
            }
            if (fxController != null) {
                final ModifyFxControllerJob fxControllerJob
                        = new ModifyFxControllerJob(oldRoot, null, getEditorController());
                result.add(fxControllerJob);
            }

            final Job deleteNewRoot = new RemoveObjectJob(candidateRoot, getEditorController());
            result.add(deleteNewRoot);

            final Job setDocumentRoot = new SetDocumentRootJob(candidateRoot, getEditorController());
            result.add(setDocumentRoot);

            // Finally add the fx:controller/fx:root to the new root object
            if (isFxRoot) {
                final ToggleFxRootJob fxRootJob = new ToggleFxRootJob(getEditorController());
                result.add(fxRootJob);
            }
            if (fxController != null) {
                final ModifyFxControllerJob fxControllerJob
                        = new ModifyFxControllerJob(candidateRoot, fxController, getEditorController());
                result.add(fxControllerJob);
            }
        }

        return result;
    }

    @Override
    protected String makeDescription() {
        return I18N.getString("label.action.edit.trim");
    }

    @Override
    protected AbstractSelectionGroup getNewSelectionGroup() {
        // Selection unchanged
        return getOldSelectionGroup();
    }

    private boolean canTrim() {
        final Selection selection = getEditorController().getSelection();
        final boolean result;

        if (selection.getGroup() instanceof ObjectSelectionGroup) {
            final ObjectSelectionGroup osg = (ObjectSelectionGroup) selection.getGroup();
            if (osg.getItems().size() == 1) {
                // We can trim if:
                //  - object is an FXOMInstance
                //  - object is not already the root
                //  - object is self contained
                final FXOMObject fxomObject = osg.getItems().iterator().next();
                if (fxomObject instanceof FXOMInstance) {
                    final FXOMDocument fxomDocument = fxomObject.getFxomDocument();
                    result = (fxomObject != fxomDocument.getFxomRoot())
                            && FXOMFxIdIndex.isSelfContainedObject(fxomObject);
                } else {
                    result = false;
                }
            } else {
                // Cannot trim when multiple objects are selected
                result = false;
            }
        } else {
            // selection.getGroup() instanceof GridSelectionGroup
            //      => cannot trim a selected row/column in a grid pane
            result = false;
        }

        return result;
    }
}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/library/util/JarExplorer.java<|end_filename|>
/*
 * Copyright (c) 2017, 2020, Gluon and/or its affiliates.
 * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.kit.library.util;

import com.oracle.javafx.scenebuilder.kit.library.util.JarReportEntry.Status;

import java.io.IOException;
import java.nio.file.Path;
import java.util.Enumeration;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

/**
 *
 * 
 */
public class JarExplorer extends ExplorerBase {
    
    private final Path jar;
    
    public JarExplorer(Path jar) {
        assert jar != null;
        assert jar.isAbsolute();
        
        this.jar = jar;
    }
    
    public JarReport explore(ClassLoader classLoader) throws IOException {
        final JarReport result = new JarReport(jar);
        
        try (JarFile jarFile = new JarFile(jar.toFile())) {
            final Enumeration<JarEntry> e = jarFile.entries();
            while (e.hasMoreElements()) {
                final JarEntry entry = e.nextElement();
                JarReportEntry explored = exploreEntry(entry, classLoader);
                if (explored.getStatus() != Status.IGNORED)
                    result.getEntries().add(explored);
            }
        }
        
        return result;
    }
    
    /*
     * Private
     */
    
    private JarReportEntry exploreEntry(JarEntry entry, ClassLoader classLoader) {
        if (entry.isDirectory()) {
            return new JarReportEntry(entry.getName(), JarReportEntry.Status.IGNORED, null, null, null);
        } else {
            String className = makeClassName(entry.getName(), "/");
            return super.exploreEntry(entry.getName(), classLoader, className);
        }
    }

}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/job/wrap/WrapInStageJob.java<|end_filename|>
package com.oracle.javafx.scenebuilder.kit.editor.job.wrap;

import com.oracle.javafx.scenebuilder.kit.editor.EditorController;

import javafx.stage.Stage;

/**
 * Job used to wrap selection in a Stage.
 */
public class WrapInStageJob extends AbstractWrapInWindowJob {

    public WrapInStageJob(EditorController editorController) {
        super(editorController);
        newContainerClass = Stage.class;
    }

}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/job/reference/ObjectDeleter.java<|end_filename|>
/*
 * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */

package com.oracle.javafx.scenebuilder.kit.editor.job.reference;

import com.oracle.javafx.scenebuilder.kit.editor.EditorController;
import com.oracle.javafx.scenebuilder.kit.editor.job.Job;
import com.oracle.javafx.scenebuilder.kit.editor.job.atomic.RemoveNodeJob;
import com.oracle.javafx.scenebuilder.kit.editor.job.atomic.RemoveObjectJob;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMCollection;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMDocument;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMInstance;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMNode;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMNodes;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMObject;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMProperty;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMPropertyC;
import java.util.LinkedList;
import java.util.List;

/**
 */

public class ObjectDeleter {
    
    private final EditorController editorController;
    private final FXOMDocument fxomDocument;
    private final List<Job> executedJobs = new LinkedList<>();
    
    public ObjectDeleter(EditorController editorController) {
        assert editorController != null;
        assert editorController.getFxomDocument() != null;
        this.editorController = editorController;
        this.fxomDocument = editorController.getFxomDocument();
    }
    
    public void delete(FXOMObject target) {
        final FXOMNode node = prepareDeleteObject(target, target);
        
        if (node == target) {
            final RemoveObjectJob removeJob = new RemoveObjectJob(target, editorController);
            removeJob.execute();
            executedJobs.add(removeJob);
        }
    }
    
    public void prepareDelete(FXOMObject target) {
        assert target != null;
        assert target.getFxomDocument() == fxomDocument;
        assert fxomDocument.getFxomRoot() != null; // At least target
        
        prepareDeleteObject(target, target);
    }
    
    public List<Job> getExecutedJobs() {
        return new LinkedList<>(executedJobs);
    }
    
    
    /*
     * Private
     */
    
    private FXOMNode prepareDeleteObject(FXOMObject node, FXOMObject target) {
        final FXOMNode result;
        
        final String nodeFxId = node.getFxId();
        if (nodeFxId == null) {
            // node has no fx:id : it can be deleted safely
            result = node;
        } else {
            final FXOMObject fxomRoot = fxomDocument.getFxomRoot();
            final List<FXOMNode> references = fxomRoot.collectReferences(nodeFxId, target);
            if (references.isEmpty()) {
                // node has an fx:id but this one is not referenced
                // outside of the delete target : it can be deleted safely
                result = node;
            } else {
                // node has an fx:id referenced outside of the delete target
                // => we find the first strong reference R to it
                // => we remove all the weak references between node and R
                // => we combine node with R
                FXOMNode firstReference = null;
                for (FXOMNode r : references) {
                    if (FXOMNodes.isWeakReference(r)) {
                        // This weak reference will become a forward reference
                        // after the deletion => we remove it.
                        final Job clearJob = new RemoveNodeJob(r, editorController);
                        clearJob.execute();
                        executedJobs.add(clearJob);
                    } else {
                        firstReference = r;
                        break;
                    }
                }
                
                if (firstReference == null) {
                    // node has only weak references ; those references have
                    // been removed => node can be delete safely
                    result = node;
                } else {
                    // we combine firstReference with node ie node is 
                    // disconnected from its parent and put in place of
                    // firstReference
                    final Job combineJob = new CombineReferenceJob(firstReference, editorController);
                    combineJob.execute();
                    executedJobs.add(combineJob);
                    result = null;
                }
            }
        }
        
        if (result == node) {
            if (node instanceof FXOMInstance) {
                final FXOMInstance fxomInstance = (FXOMInstance) node;
                for (FXOMProperty p : new LinkedList<>(fxomInstance.getProperties().values())) {
                    if (p instanceof FXOMPropertyC) {
                        final FXOMPropertyC cp = (FXOMPropertyC) p;
                        for (FXOMObject value : new LinkedList<>(cp.getValues())) {
                            prepareDeleteObject(value, target);
                        }
                    }
                }
            } else if (result instanceof FXOMCollection) {
                final FXOMCollection fxomCollection = (FXOMCollection) result;
                for (FXOMObject i : new LinkedList<>(fxomCollection.getItems())) {
                    prepareDeleteObject(i, target);
                }
            } // else no prework needed
        }
        
        return result;
    }
}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/util/eventnames/EventNames.java<|end_filename|>
package com.oracle.javafx.scenebuilder.kit.util.eventnames;

/**
 * Collects all the event names possible to be set in the Code section.
 */
public class EventNames {

    // Main
    public static final String ACTION_EVENT = "ActionEvent";

    // DragDrop
    public static final String MOUSE_EVENT = "MouseEvent";
    public static final String DRAG_EVENT = "DragEvent";
    public static final String MOUSE_DRAG_EVENT = "MouseDragEvent";

    // Keyboard
    public static final String INPUT_METHOD_EVENT = "InputMethodEvent";
    public static final String KEY_EVENT = "KeyEvent";

    // Mouse
    public static final String CONTEXT_MENU_EVENT = "ContextMenuEvent";
    public static final String SCROLL_EVENT = "ScrollEvent";

    // Rotation
    public static final String ROTATE_EVENT = "RotateEvent";

    // Swipe
    public static final String SWIPE_EVENT = "SwipeEvent";

    // Touch
    public static final String TOUCH_EVENT = "TouchEvent";

    // Zoom
    public static final String ZOOM_EVENT = "ZoomEvent";

    // should be used in a static way
    private EventNames() {
    }
}

<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/selectionbar/SelectionBarController.java<|end_filename|>
/*
 * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.kit.selectionbar;

import java.net.URL;

import com.oracle.javafx.scenebuilder.kit.i18n.I18N;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.scene.Node;
import javafx.scene.control.Hyperlink;
import javafx.scene.control.Label;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.Region;
import javafx.scene.layout.StackPane;

import com.oracle.javafx.scenebuilder.kit.editor.EditorController;
import com.oracle.javafx.scenebuilder.kit.editor.panel.util.AbstractFxmlPanelController;
import com.oracle.javafx.scenebuilder.kit.editor.selection.ObjectSelectionGroup;
import com.oracle.javafx.scenebuilder.kit.editor.selection.Selection;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMDocument;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMObject;
import com.oracle.javafx.scenebuilder.kit.metadata.util.DesignHierarchyMask;

/**
 *
 */
public class SelectionBarController extends AbstractFxmlPanelController {

    @FXML
    private HBox pathBox;

    private final Image selectionChevronImage;

    public SelectionBarController(EditorController editorController) {
        super(SelectionBarController.class.getResource("SelectionBar.fxml"), I18N.getBundle(), editorController); //NOI18N

        // Initialize selection chevron image
        final URL selectionChevronURL = SelectionBarController.class.getResource("selection-chevron.png"); //NOI18N
        assert selectionChevronURL != null;
        selectionChevronImage = new Image(selectionChevronURL.toExternalForm());
    }

    /*
     * AbstractPanelController
     */
    @Override
    protected void fxomDocumentDidChange(FXOMDocument oldDocument) {
        if (pathBox != null) {
            updateSelectionBar();
        }
    }

    @Override
    protected void sceneGraphRevisionDidChange() {
        if (pathBox != null) {
            updateSelectionBar();
        }
    }

    @Override
    protected void cssRevisionDidChange() {
        // Nothing to do here
    }

    @Override
    protected void jobManagerRevisionDidChange() {
        sceneGraphRevisionDidChange();
    }

    @Override
    protected void editorSelectionDidChange() {
        if (pathBox != null) {
            updateSelectionBar();
        }
    }

    /*
     * AbstractFxmlPanelController
     */
    @Override
    protected void controllerDidLoadFxml() {
        
        // Sanity checks
        assert pathBox != null;

        // Update
        updateSelectionBar();
    }

    /*
     * Private
     */
    private void updateSelectionBar() {
        final Selection selection = getEditorController().getSelection();

        pathBox.getChildren().clear();

        if (selection.isEmpty()) {
            pathBox.getChildren().add(new Label(I18N.getString("selectionbar.no.selected")));
        } else {
            if (selection.getGroup() instanceof ObjectSelectionGroup) {
                final ObjectSelectionGroup osg = (ObjectSelectionGroup) selection.getGroup();
                assert osg.getItems().isEmpty() == false;

                FXOMObject fxomObject = osg.getItems().iterator().next();
                // Recursive error report for the leaf object only
//                boolean recursive = true;
                while (fxomObject != null) {
                    final DesignHierarchyMask mask = new DesignHierarchyMask(fxomObject);
                    final String entryText = makeEntryText(mask);
                    final Hyperlink boxItem = new Hyperlink();
                    boxItem.setText(entryText);
                    final Node graphic;
                    // Do not display warning icon anymore :
                    // See DTL-6535 : Should we show warnings in the selection bar ?
//                    final List<ErrorReportEntry> entries = getErrorReportEntries(fxomObject, recursive);
//                    if (entries != null) {
//                        assert !entries.isEmpty();
//                        final ImageView classNameImageView
//                                = new ImageView(mask.getClassNameIcon());
//                        final ImageView warningBadgeImageView
//                                = new ImageView(warningBadgeImage);
//                        final StackPane iconsStack = new StackPane();
//                        iconsStack.getChildren().setAll(classNameImageView, warningBadgeImageView);
//                        // Update tooltip with the first entry
//                        final Tooltip iconsTooltip = new Tooltip(entries.get(0).toString());
//
//                        // We use a label to set a tooltip over the node icon 
//                        // (StackPane does not allow to set tooltips)
//                        graphic = new Label();
//                        ((Label) graphic).setGraphic(iconsStack);
//                        ((Label) graphic).setTooltip(iconsTooltip);
//                    } else {
                        graphic = new ImageView(mask.getClassNameIcon());
//                    }
                    boxItem.setGraphic(graphic);
                    boxItem.setFocusTraversable(false);
                    boxItem.setUserData(fxomObject);
                    boxItem.setOnAction(hyperlinkHandler);
                    pathBox.getChildren().add(0, boxItem);

                    // The last 2 box item should never show ellipsis
                    if (pathBox.getChildren().size() <= 3) {
                        boxItem.setMinWidth(Region.USE_PREF_SIZE);
                        HBox.setHgrow(boxItem, Priority.ALWAYS);
                    } else {
                        boxItem.setMinWidth(graphic.getBoundsInLocal().getWidth());
                    }

                    fxomObject = mask.getParentFXOMObject();
                    // Add selection chevron if needed
                    if (fxomObject != null) {
                        // We cannot share the image view to avoid 
                        // Children: duplicate children added
                        ImageView img = new ImageView(selectionChevronImage);
                        StackPane sp = new StackPane();
                        sp.getChildren().add(img);
                        sp.setMinWidth(selectionChevronImage.getWidth());
                        pathBox.getChildren().add(0, sp);
                    }
                    // Non recursive error report for the parent
//                    recursive = false;
                }

            } else {
                pathBox.getChildren().add(new Label(I18N.getString("selectionbar.not.object")));
            }
        }
    }

    private String makeEntryText(DesignHierarchyMask mask) {
        final StringBuilder result = new StringBuilder();

        result.append(mask.getClassNameInfo());
        final String description = mask.getSingleLineDescription();
        if (description != null) {
            result.append(" : "); //NOI18N
            result.append(description);
        }
        return result.toString();
    }

    private final EventHandler<ActionEvent> hyperlinkHandler = t -> {
        assert t.getSource() instanceof Hyperlink;
        final Hyperlink hyperlink = (Hyperlink) t.getSource();
        assert hyperlink.getUserData() instanceof FXOMObject;
        handleSelect((FXOMObject) hyperlink.getUserData());
        hyperlink.setVisited(false);
    };

    private void handleSelect(FXOMObject fxomObject) {
        final Selection selection = getEditorController().getSelection();

        assert fxomObject.getFxomDocument() == getEditorController().getFxomDocument();

        selection.select(fxomObject);
    }

//    private List<ErrorReportEntry> getErrorReportEntries(FXOMObject fxomObject, boolean recursive) {
//        assert fxomObject != null;
//        final ErrorReport errorReport = getEditorController().getErrorReport();
//        return errorReport.query(fxomObject, recursive);
//    }
}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/library/BuiltinSectionComparator.java<|end_filename|>
/*
 * Copyright (c) 2016, Gluon and/or its affiliates.
 * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.kit.library;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

/**
 *
 * @treatAsPrivate
 */
public class BuiltinSectionComparator implements Comparator<String> {
    
    private static final List<String> orderedSections = new ArrayList<>();
    
    static {
        orderedSections.add(BuiltinLibrary.TAG_CONTAINERS);
        orderedSections.add(BuiltinLibrary.TAG_CONTROLS);
        orderedSections.add(BuiltinLibrary.TAG_GLUON);
        orderedSections.add(BuiltinLibrary.TAG_MENU);
        orderedSections.add(BuiltinLibrary.TAG_MISCELLANEOUS);
        orderedSections.add(BuiltinLibrary.TAG_SHAPES);
        orderedSections.add(BuiltinLibrary.TAG_CHARTS);
        orderedSections.add(BuiltinLibrary.TAG_3D);
    }
    
    
    /*
     * Comparator
     */

    @Override
    public int compare(String section1, String section2) {
        assert section1 != null;
        assert section2 != null;
        
        final int index1 = orderedSections.indexOf(section1);
        final int index2 = orderedSections.indexOf(section2);
        final int result;
        
        if ((index1 != -1) && (index2 != -1)) {
            // section1 and section2 are both predefined names
            result = Integer.compare(index1, index2);
        } else if (index1 != -1) {
            // only section1 is predefined -> goes before section2
            result = +1;
        } else if (index2 != -1) {
            // only section2 is predefined -> goes before section1
            result = -1;
        } else {
            // section1 and section2 are both custom
            result = section1.compareTo(section2);
        }
        
        return result;
    }
    
}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/inspector/editors/EnumEditor.java<|end_filename|>
/*
 * Copyright (c) 2016, Gluon and/or its affiliates.
 * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation and Gluon nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */

package com.oracle.javafx.scenebuilder.kit.editor.panel.inspector.editors;

import com.oracle.javafx.scenebuilder.kit.metadata.property.ValuePropertyMetadata;
import com.oracle.javafx.scenebuilder.kit.metadata.property.value.EnumerationPropertyMetadata;
import javafx.beans.InvalidationListener;
import javafx.collections.ObservableList;
import javafx.scene.Node;
import javafx.scene.control.ComboBox;

import java.util.Set;

public class EnumEditor extends PropertyEditor {
    private ComboBox<String> comboBox;

    public EnumEditor(ValuePropertyMetadata propMeta, Set<Class<?>> selectedClasses) {
        super(propMeta, selectedClasses);
        comboBox = new ComboBox<String>();
        comboBox.disableProperty().bind(disableProperty());
        EditorUtils.makeWidthStretchable(comboBox);
        comboBox.getSelectionModel().selectedItemProperty().addListener((InvalidationListener) o -> {
            if (!isUpdateFromModel()) {
                userUpdateValueProperty(getValue());
            }
        });
        initialize();
    }

    private void initialize() {
        updateItems();
    }

    @Override
    public Object getValue() {
        return comboBox.getSelectionModel().getSelectedItem();
    }

    @Override
    public void setValue(Object value) {
        setValueGeneric(value);
        if (isSetValueDone()) {
            return;
        }

        if (value != null) {
            comboBox.getSelectionModel().select(value.toString());
        } else {
            comboBox.getSelectionModel().clearSelection();
        }
    }

    @Override
    public void reset(ValuePropertyMetadata propMeta, Set<Class<?>> selectedClasses) {
        super.reset(propMeta, selectedClasses);
        // ComboBox items have to be updated, since this editor may have been used by a different Enum...
        updateItems();
    }

    @Override
    public Node getValueEditor() {
        return super.handleGenericModes(comboBox);
    }

    @Override
    protected void valueIsIndeterminate() {
        handleIndeterminate(comboBox);
    }

    protected ComboBox<String> getComboBox() {
        return comboBox;
    }

    protected void updateItems() {
        updateItems(comboBox.getItems());
    }

    protected void updateItems(ObservableList<String> itemsList) {
        assert getPropertyMeta() instanceof EnumerationPropertyMetadata;
        final EnumerationPropertyMetadata enumPropMeta
                = (EnumerationPropertyMetadata) getPropertyMeta();
        itemsList.clear();
        for (Object val : enumPropMeta.getValidValues()) {
            itemsList.add(val.toString());
        }
    }

    @Override
    public void requestFocus() {
        EditorUtils.doNextFrame(() -> comboBox.requestFocus());
    }

}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/metadata/property/value/CallbackPropertyMetadata.java<|end_filename|>
/*
 * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.kit.metadata.property.value;

import com.oracle.javafx.scenebuilder.kit.fxom.FXOMDocument;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMInstance;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMObject;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMProperty;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMPropertyC;
import com.oracle.javafx.scenebuilder.kit.metadata.property.ValuePropertyMetadata;
import com.oracle.javafx.scenebuilder.kit.metadata.util.InspectorPath;
import com.oracle.javafx.scenebuilder.kit.metadata.util.PropertyName;
import java.util.Objects;
import javafx.util.Callback;

/**
 *
 */

public abstract class CallbackPropertyMetadata extends ValuePropertyMetadata {

    private final Object defaultValue;

    public CallbackPropertyMetadata(PropertyName name, boolean readWrite, Object defaultValue, InspectorPath inspectorPath) {
        super(name, readWrite, inspectorPath);
        this.defaultValue = defaultValue;
    }

    public Object getDefaultValue() {
        return defaultValue;
    }
    
    public Object getValue(FXOMInstance fxomInstance) {
        final Object result;
        
        if (isReadWrite()) {
            final FXOMProperty fxomProperty = fxomInstance.getProperties().get(getName());
            if (fxomProperty instanceof FXOMPropertyC) {
                
                final FXOMPropertyC fxomPropertyC = (FXOMPropertyC) fxomProperty;
                assert fxomPropertyC.getValues().size() == 1;

                final FXOMObject valueFxomObject = fxomPropertyC.getValues().get(0);
                final Object sceneGraphObject = valueFxomObject.getSceneGraphObject();

                result = castValue(sceneGraphObject);
            } else {
                assert fxomProperty == null;
                
                // propertyName is not specified in the fxom instance.
                // We return the default value specified in the metadata of the
                // property
                result = defaultValue;
            }
        } else {
            result = castValue(getName().getValue(fxomInstance.getSceneGraphObject()));
        }
        
        return result;
    }

    public void setValue(FXOMInstance fxomInstance, Object value) {
        assert isReadWrite();
        
        final FXOMProperty fxomProperty = fxomInstance.getProperties().get(getName());

        if (Objects.equals(value, getDefaultValueObject())) {
            // We must remove the fxom property if any
            if (fxomProperty != null) {
                fxomProperty.removeFromParentInstance();
            }
        } else {
            if (fxomProperty == null) {
                // propertyName is not specified in the fxom instance.
                // We insert a new fxom property
                final FXOMProperty newProperty
                        = makeFxomPropertyFromValue(fxomInstance, value);
                newProperty.addToParentInstance(-1, fxomInstance);
            } else {
                updateFxomPropertyWithValue(fxomProperty, value);
            }
        }
    }
    
    
    protected abstract void updateFxomInstanceWithValue(FXOMInstance valueInstance, Object value);
    protected abstract Class<?> getFxConstantClass();
    protected abstract Object castValue(Object value);
    
    
    
    
    /*
     * ValuePropertyMetadata
     */
    
    @Override
    public Class<?> getValueClass() {
        return Callback.class;
    }

    @Override
    public Object getDefaultValueObject() {
        return defaultValue;
    }

    @Override
    public Object getValueObject(FXOMInstance fxomInstance) {
        return getValue(fxomInstance);
    }

    @Override
    public void setValueObject(FXOMInstance fxomInstance, Object valueObject) {
        setValue(fxomInstance, castValue(valueObject));
    }
    
    
    /*
     * Private
     */
    
    protected FXOMProperty makeFxomPropertyFromValue(FXOMInstance fxomInstance, Object value) {
        assert fxomInstance != null;
        assert value != null;
        
        final FXOMDocument fxomDocument = fxomInstance.getFxomDocument();
        final FXOMInstance valueInstance = new FXOMInstance(fxomDocument, getFxConstantClass());
        updateFxomInstanceWithValue(valueInstance, value);
        return new FXOMPropertyC(fxomDocument, getName(), valueInstance);
    }

    protected void updateFxomPropertyWithValue(FXOMProperty fxomProperty, Object value) {
        assert value != null;
        assert fxomProperty instanceof FXOMPropertyC; // Because Callback are expressed using fx:constant
        
        final FXOMPropertyC fxomPropertyC = (FXOMPropertyC) fxomProperty;
        assert fxomPropertyC.getValues().size() == 1;
        
        FXOMObject valueObject = fxomPropertyC.getValues().get(0);
        if (valueObject instanceof FXOMInstance) {
            updateFxomInstanceWithValue((FXOMInstance) valueObject, value);
        } else {
            final FXOMDocument fxomDocument = fxomProperty.getFxomDocument();
            final FXOMInstance valueInstance = new FXOMInstance(fxomDocument, getFxConstantClass());
            updateFxomInstanceWithValue(valueInstance, value);
            valueInstance.addToParentProperty(0, fxomPropertyC);
            valueObject.removeFromParentProperty();
        }
    }

}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/content/driver/handles/WindowHandles.java<|end_filename|>
package com.oracle.javafx.scenebuilder.kit.editor.panel.content.driver.handles;

import com.oracle.javafx.scenebuilder.kit.editor.panel.content.ContentPanelController;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMInstance;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMObject;
import com.oracle.javafx.scenebuilder.kit.metadata.util.DesignHierarchyMask;

import javafx.geometry.BoundingBox;
import javafx.geometry.Bounds;
import javafx.scene.Node;
import javafx.scene.layout.Pane;
import javafx.stage.Window;

public class WindowHandles extends AbstractGenericHandles<Window> {
    private final Pane dummyPane = new Pane();
    private Node sceneGraphObject;

    public WindowHandles(ContentPanelController contentPanelController,
                         FXOMInstance fxomInstance) {
        super(contentPanelController, fxomInstance, Window.class);

        final DesignHierarchyMask designHierarchyMask = new DesignHierarchyMask(fxomInstance);
        FXOMObject scene = designHierarchyMask.getAccessory(DesignHierarchyMask.Accessory.SCENE);
        if (scene == null) {
            sceneGraphObject = null;
        } else {
            DesignHierarchyMask sceneDesignHierarchyMask = new DesignHierarchyMask(scene);
            FXOMObject root = sceneDesignHierarchyMask.getAccessory(DesignHierarchyMask.Accessory.ROOT);
            assert root != null;
            assert root instanceof FXOMInstance;
            assert root.getSceneGraphObject() instanceof Node;
            sceneGraphObject = (Node) root.getSceneGraphObject();
        }
    }

    @Override
    public Bounds getSceneGraphObjectBounds() {
        if (sceneGraphObject != null) {
            return sceneGraphObject.getLayoutBounds();
        } else {
            return new BoundingBox(0, 0, 0, 0);
        }
    }

    @Override
    public Node getSceneGraphObjectProxy() {
        return sceneGraphObject != null ? sceneGraphObject : dummyPane;
    }

    @Override
    protected void startListeningToSceneGraphObject() {
        if (sceneGraphObject == null) {
            return;
        }
        startListeningToLayoutBounds(sceneGraphObject);
        startListeningToLocalToSceneTransform(sceneGraphObject);
    }

    @Override
    protected void stopListeningToSceneGraphObject() {
        if (sceneGraphObject == null) {
            return;
        }
        stopListeningToLayoutBounds(sceneGraphObject);
        stopListeningToLocalToSceneTransform(sceneGraphObject);
    }

}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/fxom/TransientClassLoader.java<|end_filename|>
/*
 * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */

package com.oracle.javafx.scenebuilder.kit.fxom;

import java.net.MalformedURLException;
import java.net.URL;

/**
 * This class loader delegates all its tasks to its parent class loader but
 * overrides ClassLoader.getResource().
 * 
 * The getResource() invokes the parent class loader implementation and checks
 * if the result is null or not. When null (ie no existing resource match), it
 * returns a dummy URL in place of null.
 * 
 * This class loader is instantiated by FXOMLoader and passed to FXMLLoader.
 * It avoids FXMLLoader to break and interrupt loading when a classpath relative
 * URL is unresolved.
 */
class TransientClassLoader extends ClassLoader {
    
    public TransientClassLoader(ClassLoader parentClassLoader) {
        super(parentClassLoader);
    }
    
    /*
     * ClassLoader
     */
    
    @Override
    public URL getResource(String name) {
        URL  result = super.getResource(name);
        if (result == null) {
            try {
                result = new URL("file", null, name); //NOI18N
            } catch(MalformedURLException x) {
                throw new RuntimeException("Bug", x); //NOI18N
            }
        }
        return result;
    }
}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/content/util/Picker.java<|end_filename|>
/*
 * Copyright (c) 2018, Gluon and/or its affiliates.
 * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.kit.editor.panel.content.util;

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

import javafx.geometry.Bounds;
import javafx.geometry.Point2D;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.shape.Line;
import javafx.scene.shape.Shape;

/**
 * This class allows to pick objects at a given position in scene graph.
 * 
 * 
 */
public class Picker {

    private final static double THRESHOLD = 3;
    
    private final Set<Node> excludes = new HashSet<>();
    private final List<Node> matches = new ArrayList<>();
    
    /**
     * Returns the list of nodes below (sceneX, sceneY).
     * Topmost node is at index 0.
     * Search starts from startNode.
     * 
     * @param startNode
     * @param sceneX
     * @param sceneY
     * @return the list of nodes below (sceneX, sceneY).
     */
    public List<Node> pick(Node startNode, double sceneX, double sceneY) {
        assert startNode != null;
        assert startNode.getScene() != null;
        assert Double.isNaN(sceneX) == false;
        assert Double.isNaN(sceneY) == false;
        
        final Point2D localXY = startNode.sceneToLocal(sceneX, sceneY, true /* rootScene */);
        return pickInLocal(startNode, localXY.getX(), localXY.getY());
    }
    
    public List<Node> pickInLocal(Node startNode, double localX, double localY) {
        assert startNode != null;
        assert startNode.getScene() != null;
        assert Double.isNaN(localX) == false;
        assert Double.isNaN(localY) == false;
        
        this.matches.clear();
        performPick(startNode, localX, localY);
        return matches.isEmpty() ? null : Collections.unmodifiableList(matches);
    }

    public Set<Node> getExcludes() {
        return excludes;
    }
    

    private void performPick(Node startNode, double localX, double localY) {

        if ((excludes.contains(startNode) == false) && startNode.isVisible()){
            if (match(startNode, localX, localY)) {
                matches.add(0, startNode);
            }
        
            if (startNode instanceof Parent) {
                final Parent startParent = (Parent) startNode;
                for (Node child : startParent.getChildrenUnmodifiable()) {
                    final Point2D childLocalXY = child.parentToLocal(localX, localY);
                    // Note : childLocalXY may be null.
                    // For example, child is a Button with scaleX == 0.
                    if (childLocalXY != null) {
                        performPick(child, childLocalXY.getX(), childLocalXY.getY());
                    }
                }
            }
        }
    }

    private boolean match(Node node, double x, double y) {
        assert node != null;
        
        final Bounds bounds = node.getLayoutBounds();
        if (bounds.isEmpty())
            return false;
        final boolean result;
        if (node instanceof Line) {
            final Line line = (Line) node;
            final Point2D point = new Point2D(x, y);          
            result = DistanceUtils.getDistFromPointToLine(point, line) < THRESHOLD;
        } else if (node instanceof Shape) {
            result = ((Shape) node).contains(x, y);
        } else {
            result = bounds.contains(x, y);
        }
        return result;
    }
    
}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/report/ErrorReport.java<|end_filename|>
/*
 * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.kit.editor.report;

import com.oracle.javafx.scenebuilder.kit.fxom.FXOMAssetIndex;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMCollection;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMDocument;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMInstance;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMIntrinsic;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMNode;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMNodes;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMObject;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMProperty;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMPropertyC;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMPropertyT;
import com.oracle.javafx.scenebuilder.kit.metadata.util.PrefixedValue;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;

/**
 *
 * 
 */
public class ErrorReport {
    
    private final Map<FXOMNode, List<ErrorReportEntry>> entries = new HashMap<>();
    private final Map<Path, CSSParsingReport> cssParsingReports = new HashMap<>();
    private FXOMDocument fxomDocument;
    private boolean dirty = true;
    
    public void setFxomDocument(FXOMDocument fxomDocument) {
        this.fxomDocument = fxomDocument;
        forget();
    }
    
    public void forget() {
        this.entries.clear();
        this.dirty = true;
    }
    
    public List<ErrorReportEntry> query(FXOMObject fxomObject, boolean recursive) {
        final List<ErrorReportEntry> result;
        
        updateReport();
        
        final List<ErrorReportEntry> collected = new ArrayList<>();
        if (recursive) {
            collectEntries(fxomObject, collected);
        } else {
            if (entries.get(fxomObject) != null) {
                collected.addAll(entries.get(fxomObject));
            }
            if (fxomObject instanceof FXOMInstance) {
                final FXOMInstance fxomInstance = (FXOMInstance) fxomObject;
                for (FXOMProperty fxomProperty : fxomInstance.getProperties().values()) {
                    if (entries.get(fxomProperty) != null) {
                        collected.addAll(entries.get(fxomProperty));
                    }
                }
            }
        }
        
        if (collected.isEmpty()) {
            result = null;
        } else {
            result = collected;
        }
        
        assert (result == null) || (result.size() >= 1);
        
        return result;
    }
    
    public Map<FXOMNode, List<ErrorReportEntry>> getEntries() {
        updateReport();
        return Collections.unmodifiableMap(entries);
    }
    
    public void cssFileDidChange(Path cssPath) {
        if (cssParsingReports.containsKey(cssPath)) {
            cssParsingReports.remove(cssPath);
            forget();
        }
    }
    
    
    /*
     * Private
     */
    
    
    private void updateReport() {
        if (dirty) {
            assert entries.isEmpty();
            if (fxomDocument != null) {
                verifyAssets();
                verifyUnresolvedObjects();
                verifyBindingExpressions();
            }
            dirty = false;
        }
    }
    
    
    private void verifyAssets() {
        final FXOMAssetIndex assetIndex = new FXOMAssetIndex(fxomDocument);
        for (Map.Entry<Path, FXOMNode> e : assetIndex.getFileAssets().entrySet()) {
            final Path assetPath = e.getKey();
            if (assetPath.toFile().canRead() == false) {
                final ErrorReportEntry newEntry 
                        = new ErrorReportEntry(e.getValue(), ErrorReportEntry.Type.UNRESOLVED_LOCATION);
                addEntry(e.getValue(), newEntry);
            } else {
                final String assetPathName = assetPath.toString();
                if (assetPathName.toLowerCase(Locale.ROOT).endsWith(".css")) { //NOI18N
                    // assetPath is a CSS file : check its parsing report
                    final CSSParsingReport r = getCSSParsingReport(assetPath);
                    assert r != null;
                    if (r.isEmpty() == false) {
                        final ErrorReportEntry newEntry 
                                = new ErrorReportEntry(e.getValue(), ErrorReportEntry.Type.INVALID_CSS_CONTENT, r);
                        addEntry(e.getValue(), newEntry);
                    }
                }
            }
        }
    }
    
    private void verifyUnresolvedObjects() {
        for (FXOMObject fxomObject : FXOMNodes.serializeObjects(fxomDocument.getFxomRoot())) {
            final Object sceneGraphObject;
            if (fxomObject instanceof FXOMIntrinsic) {
                final FXOMIntrinsic fxomIntrinsic = (FXOMIntrinsic) fxomObject;
                sceneGraphObject = fxomIntrinsic.getSourceSceneGraphObject();
            } else {
                sceneGraphObject = fxomObject.getSceneGraphObject();
            }
            if (sceneGraphObject == null) {
                final ErrorReportEntry newEntry 
                        = new ErrorReportEntry(fxomObject, ErrorReportEntry.Type.UNRESOLVED_CLASS);
                addEntry(fxomObject, newEntry);
            }
        }
    }
    
    private void verifyBindingExpressions() {
        for (FXOMPropertyT p : fxomDocument.getFxomRoot().collectPropertiesT()) {
            final PrefixedValue pv = new PrefixedValue(p.getValue());
            if (pv.isBindingExpression()) {
                final ErrorReportEntry newEntry 
                        = new ErrorReportEntry(p, ErrorReportEntry.Type.UNSUPPORTED_EXPRESSION);
                addEntry(p, newEntry);
            }
        }
    }
    
    private void addEntry(FXOMNode fxomNode, ErrorReportEntry newEntry) {
        List<ErrorReportEntry> nodeEntries = entries.get(fxomNode);
        if (nodeEntries == null) {
            nodeEntries = new ArrayList<> ();
            entries.put(fxomNode, nodeEntries);
        }
        nodeEntries.add(newEntry);
    }
    
    private CSSParsingReport getCSSParsingReport(Path assetPath) {
        CSSParsingReport result = cssParsingReports.get(assetPath);
        if (result == null) {
            result = new CSSParsingReport(assetPath);
            cssParsingReports.put(assetPath, result);
        }
        return result;
    }
    
    private void collectEntries(FXOMNode fxomNode, List<ErrorReportEntry> collected) {
        assert fxomNode != null;
        assert collected != null;
        
        final List<ErrorReportEntry> nodeEntries = entries.get(fxomNode);
        if (nodeEntries != null) {
            collected.addAll(nodeEntries);
        }
        
        if (fxomNode instanceof FXOMCollection) {
            final FXOMCollection fxomCollection = (FXOMCollection) fxomNode;
            for (FXOMObject item : fxomCollection.getItems()) {
                collectEntries(item, collected);
            }
        } else if (fxomNode instanceof FXOMInstance) {
            final FXOMInstance fxomInstance = (FXOMInstance) fxomNode;
            for (FXOMProperty fxomProperty : fxomInstance.getProperties().values()) {
                collectEntries(fxomProperty, collected);
            }
        } else if (fxomNode instanceof FXOMPropertyC) {
            final FXOMPropertyC fxomPropertyC = (FXOMPropertyC) fxomNode;
            for (FXOMObject value : fxomPropertyC.getValues()) {
                collectEntries(value, collected);
            }
        }
    }
}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/content/driver/curve/LineEditor.java<|end_filename|>
/*
 * Copyright (c) 2018, Gluon and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation and Gluon nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */

package com.oracle.javafx.scenebuilder.kit.editor.panel.content.driver.curve;

import com.oracle.javafx.scenebuilder.kit.editor.panel.content.gesture.mouse.EditCurveGesture;
import com.oracle.javafx.scenebuilder.kit.editor.panel.content.guides.EditCurveGuideController;
import com.oracle.javafx.scenebuilder.kit.metadata.util.PropertyName;
import com.oracle.javafx.scenebuilder.kit.util.MathUtils;
import javafx.geometry.Point2D;
import javafx.scene.shape.Line;

import java.util.ArrayList;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class LineEditor extends AbstractCurveEditor<Line> {

    private final double originalStartX;
    private final double originalStartY;
    private final double originalEndX;
    private final double originalEndY;
    
    private final PropertyName startXName = new PropertyName("startX"); //NOI18N
    private final PropertyName startYName = new PropertyName("startY"); //NOI18N
    private final PropertyName endXName = new PropertyName("endX"); //NOI18N
    private final PropertyName endYName = new PropertyName("endY"); //NOI18N
    private final List<PropertyName> propertyNames = new ArrayList<>();

    public LineEditor(Line sceneGraphObject) {
        super(sceneGraphObject);

        originalStartX = sceneGraphObject.getStartX();
        originalStartY = sceneGraphObject.getStartY();
        originalEndX = sceneGraphObject.getEndX();
        originalEndY = sceneGraphObject.getEndY();

        propertyNames.add(startXName);
        propertyNames.add(startYName);
        propertyNames.add(endXName);
        propertyNames.add(endYName);
    }
    
    @Override
    public EditCurveGuideController createController(EnumMap<EditCurveGesture.Tunable, Integer> tunableMap) {

        final EditCurveGuideController result;
        if (tunableMap.containsKey(EditCurveGesture.Tunable.START)) {
            result = new EditCurveGuideController();
            Point2D point = sceneGraphObject.localToScene(sceneGraphObject.getEndX(), sceneGraphObject.getEndY(), true);
            result.addCurvePoint(point);
        } else if (tunableMap.containsKey(EditCurveGesture.Tunable.END)) {
            result = new EditCurveGuideController();
            Point2D point = sceneGraphObject.localToScene(sceneGraphObject.getStartX(), sceneGraphObject.getStartY(), true);
            result.addCurvePoint(point);
        } else {
            // Emergency code
            result = null;
        }

        return result;
    }
    
    @Override
    public void moveTunable(EnumMap<EditCurveGesture.Tunable, Integer> tunableMap, double newX, double newY) {
        if (tunableMap.containsKey(EditCurveGesture.Tunable.START)) {
            sceneGraphObject.setStartX(newX);
            sceneGraphObject.setStartY(newY);            
        } else if (tunableMap.containsKey(EditCurveGesture.Tunable.END)) {
            sceneGraphObject.setEndX(newX);
            sceneGraphObject.setEndY(newY);            
        }
    }

    @Override
    public void revertToOriginalState() {
        sceneGraphObject.setStartX(originalStartX);
        sceneGraphObject.setStartY(originalStartY);
        sceneGraphObject.setEndX(originalEndX);
        sceneGraphObject.setEndY(originalEndY);
    }

    @Override
    public List<PropertyName> getPropertyNames() {
        return propertyNames;
    }

    @Override
    public Object getValue(PropertyName propertyName) {
        assert propertyName != null;
        assert propertyNames.contains(propertyName);

        final Object result;
        if (propertyName.equals(startXName)) {
            result = sceneGraphObject.getStartX();
        } else if (propertyName.equals(startYName)) {
            result = sceneGraphObject.getStartY();
        } else if (propertyName.equals(endXName)) {
            result = sceneGraphObject.getEndX();
        } else if (propertyName.equals(endYName)) {
            result = sceneGraphObject.getEndY();
        } else {
            // Emergency code
            result = null;
        }

        return result;
    }

    @Override
    public Map<PropertyName, Object> getChangeMap() {
        final Map<PropertyName, Object> result = new HashMap<>();
        if (!MathUtils.equals(sceneGraphObject.getStartX(), originalStartX)) {
            result.put(startXName, sceneGraphObject.getStartX());
        }
        if (!MathUtils.equals(sceneGraphObject.getStartY(), originalStartY)) {
            result.put(startYName, sceneGraphObject.getStartY());
        }
        if (!MathUtils.equals(sceneGraphObject.getEndX(), originalEndX)) {
            result.put(endXName, sceneGraphObject.getEndX());
        }
        if (!MathUtils.equals(sceneGraphObject.getEndY(), originalEndY)) {
            result.put(endYName, sceneGraphObject.getEndY());
        }
        return result;
    }

    @Override
    public List<Double> getPoints() {
        return null;
    }

    @Override
    public void addPoint(EnumMap<EditCurveGesture.Tunable, Integer> tunableMap, double newX, double newY) {
    }

    @Override
    public void removePoint(EnumMap<EditCurveGesture.Tunable, Integer> tunableMap) {
    }
    
}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/template/TemplatesWindowController.java<|end_filename|>
/*
 * Copyright (c) 2017, Gluon and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation and Gluon nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */

package com.oracle.javafx.scenebuilder.kit.template;

import com.oracle.javafx.scenebuilder.kit.i18n.I18N;
import javafx.stage.Modality;
import javafx.stage.Stage;
import javafx.stage.Window;
import javafx.stage.WindowEvent;

public class TemplatesWindowController extends TemplatesBaseWindowController {
    private final Window owner;

    public TemplatesWindowController(Stage owner) {
        super(TemplatesWindowController.class.getResource("TemplatesWindow.fxml"), //NOI18N
                I18N.getBundle(), owner);
        this.owner = owner;
    }

    @Override
    protected void controllerDidLoadFxml() {
        super.controllerDidLoadFxml();

        setupTemplateButtonHandlers();
    }


    @Override
    public void onCloseRequest(WindowEvent event) {
        getStage().hide();
    }

    @Override
    protected void controllerDidCreateStage() {
        super.controllerDidCreateStage();
        getStage().setTitle(I18N.getString("template.dialog.title"));

        if (this.owner == null) {
            // Window will be application modal
            getStage().initModality(Modality.APPLICATION_MODAL);
        } else {
            // Window will be window modal
            getStage().initOwner(this.owner);
            getStage().initModality(Modality.WINDOW_MODAL);
        }
    }
}



<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/job/JobUtils.java<|end_filename|>
/*
 * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.kit.editor.job;

import com.oracle.javafx.scenebuilder.kit.fxom.FXOMDocument;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMInstance;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMProperty;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMPropertyC;
import com.oracle.javafx.scenebuilder.kit.metadata.Metadata;
import com.oracle.javafx.scenebuilder.kit.metadata.klass.ComponentClassMetadata;
import com.oracle.javafx.scenebuilder.kit.metadata.property.PropertyMetadata;
import com.oracle.javafx.scenebuilder.kit.metadata.property.value.BooleanPropertyMetadata;
import com.oracle.javafx.scenebuilder.kit.metadata.property.value.DoublePropertyMetadata;
import com.oracle.javafx.scenebuilder.kit.metadata.property.value.EnumerationPropertyMetadata;
import com.oracle.javafx.scenebuilder.kit.metadata.util.PropertyName;

/**
 * This class defines getter and setter on FXOM objects without using the modify job.
 * Theses methods are used by jobs that creates new FXOM objects and sets some of their properties.
 * No need to use a job in this case as the undo action will remove the new added FXOM object.
 */
public class JobUtils {

    public static void addColumnConstraints(
            final FXOMDocument fxomDocument,
            final FXOMInstance gridPane,
            final FXOMInstance constraints, int index) {
        final PropertyName propertyName = new PropertyName("columnConstraints"); //NOI18N
        FXOMProperty property = gridPane.getProperties().get(propertyName);
        if (property == null) {
            property = new FXOMPropertyC(fxomDocument, propertyName);
        }
        if (property.getParentInstance() == null) {
            property.addToParentInstance(-1, gridPane);
        }
        assert property instanceof FXOMPropertyC;
        constraints.addToParentProperty(index, (FXOMPropertyC) property);
    }

    public static void addRowConstraints(
            final FXOMDocument fxomDocument,
            final FXOMInstance gridPane,
            final FXOMInstance constraints, int index) {
        final PropertyName propertyName = new PropertyName("rowConstraints"); //NOI18N
        FXOMProperty property = gridPane.getProperties().get(propertyName);
        if (property == null) {
            property = new FXOMPropertyC(fxomDocument, propertyName);
        }
        if (property.getParentInstance() == null) {
            property.addToParentInstance(-1, gridPane);
        }
        assert property instanceof FXOMPropertyC;
        constraints.addToParentProperty(index, (FXOMPropertyC) property);
    }

    public static boolean getFillHeight(
            final FXOMInstance instance, final Class<?> clazz) {
        assert instance != null && clazz != null;
        assert clazz.isAssignableFrom(instance.getDeclaredClass());
        final PropertyMetadata propertyMeta
                = getPropertyMetadata(clazz, "fillHeight"); //NOI18N
        assert propertyMeta instanceof BooleanPropertyMetadata;
        return ((BooleanPropertyMetadata) propertyMeta).getValue(instance);
    }

    public static void setFillHeight(
            final FXOMInstance instance, final Class<?> clazz, final boolean value) {
        assert instance != null && clazz != null;
        assert clazz.isAssignableFrom(instance.getDeclaredClass());
        final PropertyMetadata propertyMeta
                = getPropertyMetadata(clazz, "fillHeight"); //NOI18N
        assert propertyMeta instanceof BooleanPropertyMetadata;
        ((BooleanPropertyMetadata) propertyMeta).setValue(instance, value);
    }

    public static boolean getFillWidth(
            final FXOMInstance instance, final Class<?> clazz) {
        assert instance != null && clazz != null;
        assert clazz.isAssignableFrom(instance.getDeclaredClass());
        final PropertyMetadata propertyMeta
                = getPropertyMetadata(clazz, "fillWidth"); //NOI18N
        assert propertyMeta instanceof BooleanPropertyMetadata;
        return ((BooleanPropertyMetadata) propertyMeta).getValue(instance);
    }

    public static void setFillWidth(
            final FXOMInstance instance, final Class<?> clazz, final boolean value) {
        assert instance != null && clazz != null;
        assert clazz.isAssignableFrom(instance.getDeclaredClass());
        final PropertyMetadata propertyMeta
                = getPropertyMetadata(clazz, "fillWidth"); //NOI18N
        assert propertyMeta instanceof BooleanPropertyMetadata;
        ((BooleanPropertyMetadata) propertyMeta).setValue(instance, value);
    }

    public static String getHAlignment(
            final FXOMInstance instance, final Class<?> clazz) {
        assert instance != null && clazz != null;
        assert clazz.isAssignableFrom(instance.getDeclaredClass());
        final PropertyName propertyName = new PropertyName("halignment"); //NOI18N
        final FXOMProperty property = instance.getProperties().get(propertyName);
        if (property == null) {
            return null;
        } else {
            final PropertyMetadata propertyMeta
                    = getPropertyMetadata(clazz, "halignment"); //NOI18N
            assert propertyMeta instanceof EnumerationPropertyMetadata;
            return ((EnumerationPropertyMetadata) propertyMeta).getValue(instance);
        }
    }

    public static void setHAlignment(
            final FXOMInstance instance, final Class<?> clazz, final String value) {
        assert instance != null && clazz != null;
        assert clazz.isAssignableFrom(instance.getDeclaredClass());
        final PropertyMetadata propertyMeta
                = getPropertyMetadata(clazz, "halignment"); //NOI18N
        assert propertyMeta instanceof EnumerationPropertyMetadata;
        if (value != null) {
            ((EnumerationPropertyMetadata) propertyMeta).setValue(instance, value);
        }
    }

    public static String getHGrow(
            final FXOMInstance instance, final Class<?> clazz) {
        assert instance != null && clazz != null;
        assert clazz.isAssignableFrom(instance.getDeclaredClass());
        final PropertyName propertyName = new PropertyName("hgrow"); //NOI18N
        final FXOMProperty property = instance.getProperties().get(propertyName);
        if (property == null) {
            return null;
        } else {
            final PropertyMetadata propertyMeta
                    = getPropertyMetadata(clazz, "hgrow"); //NOI18N
            assert propertyMeta instanceof EnumerationPropertyMetadata;
            return ((EnumerationPropertyMetadata) propertyMeta).getValue(instance);
        }
    }

    public static void setHGrow(
            final FXOMInstance instance, final Class<?> clazz, final String value) {
        assert instance != null && clazz != null;
        assert clazz.isAssignableFrom(instance.getDeclaredClass());
        final PropertyMetadata propertyMeta
                = getPropertyMetadata(clazz, "hgrow"); //NOI18N
        assert propertyMeta instanceof EnumerationPropertyMetadata;
        if (value != null) {
            ((EnumerationPropertyMetadata) propertyMeta).setValue(instance, value);
        }
    }

    public static void setLayoutX(
            final FXOMInstance instance, final Class<?> clazz, final double value) {
        assert instance != null && clazz != null;
        assert clazz.isAssignableFrom(instance.getDeclaredClass());
        final PropertyMetadata propertyMeta
                = getPropertyMetadata(clazz, "layoutX"); //NOI18N
        assert propertyMeta instanceof DoublePropertyMetadata;
        ((DoublePropertyMetadata) propertyMeta).setValue(instance, value);
    }

    public static void setLayoutY(
            final FXOMInstance instance, final Class<?> clazz, final double value) {
        assert instance != null && clazz != null;
        assert clazz.isAssignableFrom(instance.getDeclaredClass());
        final PropertyMetadata propertyMeta
                = getPropertyMetadata(clazz, "layoutY"); //NOI18N
        assert propertyMeta instanceof DoublePropertyMetadata;
        ((DoublePropertyMetadata) propertyMeta).setValue(instance, value);
    }

    public static double getMaxHeight(
            final FXOMInstance instance, final Class<?> clazz) {
        assert instance != null && clazz != null;
        assert clazz.isAssignableFrom(instance.getDeclaredClass());
        final PropertyMetadata propertyMeta
                = getPropertyMetadata(clazz, "maxHeight"); //NOI18N
        assert propertyMeta instanceof DoublePropertyMetadata;
        return ((DoublePropertyMetadata) propertyMeta).getValue(instance);
    }

    public static void setMaxHeight(
            final FXOMInstance instance, final Class<?> clazz, final double value) {
        assert instance != null && clazz != null;
        assert clazz.isAssignableFrom(instance.getDeclaredClass());
        final PropertyMetadata propertyMeta
                = getPropertyMetadata(clazz, "maxHeight"); //NOI18N
        assert propertyMeta instanceof DoublePropertyMetadata;
        ((DoublePropertyMetadata) propertyMeta).setValue(instance, value);
    }

    public static double getMaxWidth(
            final FXOMInstance instance, final Class<?> clazz) {
        assert instance != null && clazz != null;
        assert clazz.isAssignableFrom(instance.getDeclaredClass());
        final PropertyMetadata propertyMeta
                = getPropertyMetadata(clazz, "maxWidth"); //NOI18N
        assert propertyMeta instanceof DoublePropertyMetadata;
        return ((DoublePropertyMetadata) propertyMeta).getValue(instance);
    }

    public static void setMaxWidth(
            final FXOMInstance instance, final Class<?> clazz, final double value) {
        assert instance != null && clazz != null;
        assert clazz.isAssignableFrom(instance.getDeclaredClass());
        final PropertyMetadata propertyMeta
                = getPropertyMetadata(clazz, "maxWidth"); //NOI18N
        assert propertyMeta instanceof DoublePropertyMetadata;
        ((DoublePropertyMetadata) propertyMeta).setValue(instance, value);
    }

    public static double getMinHeight(
            final FXOMInstance instance, final Class<?> clazz) {
        assert instance != null && clazz != null;
        assert clazz.isAssignableFrom(instance.getDeclaredClass());
        final PropertyMetadata propertyMeta
                = getPropertyMetadata(clazz, "minHeight"); //NOI18N
        assert propertyMeta instanceof DoublePropertyMetadata;
        return ((DoublePropertyMetadata) propertyMeta).getValue(instance);
    }

    public static void setMinHeight(
            final FXOMInstance instance, final Class<?> clazz, final double value) {
        assert instance != null && clazz != null;
        assert clazz.isAssignableFrom(instance.getDeclaredClass());
        final PropertyMetadata propertyMeta
                = getPropertyMetadata(clazz, "minHeight"); //NOI18N
        assert propertyMeta instanceof DoublePropertyMetadata;
        ((DoublePropertyMetadata) propertyMeta).setValue(instance, value);
    }

    public static double getMinWidth(
            final FXOMInstance instance, final Class<?> clazz) {
        assert instance != null && clazz != null;
        assert clazz.isAssignableFrom(instance.getDeclaredClass());
        final PropertyMetadata propertyMeta
                = getPropertyMetadata(clazz, "minWidth"); //NOI18N
        assert propertyMeta instanceof DoublePropertyMetadata;
        return ((DoublePropertyMetadata) propertyMeta).getValue(instance);
    }

    public static void setMinWidth(
            final FXOMInstance instance, final Class<?> clazz, final double value) {
        assert instance != null && clazz != null;
        assert clazz.isAssignableFrom(instance.getDeclaredClass());
        final PropertyMetadata propertyMeta
                = getPropertyMetadata(clazz, "minWidth"); //NOI18N
        assert propertyMeta instanceof DoublePropertyMetadata;
        ((DoublePropertyMetadata) propertyMeta).setValue(instance, value);
    }

    public static void setOrientation(
            final FXOMInstance instance, final Class<?> clazz, final String value) {
        assert instance != null && clazz != null;
        assert clazz.isAssignableFrom(instance.getDeclaredClass());
        final PropertyMetadata propertyMeta
                = getPropertyMetadata(clazz, "orientation"); //NOI18N
        assert propertyMeta instanceof EnumerationPropertyMetadata;
        if (value != null) {
            ((EnumerationPropertyMetadata) propertyMeta).setValue(instance, value);
        }
    }

    public static double getPercentHeight(
            final FXOMInstance instance, final Class<?> clazz) {
        assert instance != null && clazz != null;
        assert clazz.isAssignableFrom(instance.getDeclaredClass());
        final PropertyMetadata propertyMeta
                = getPropertyMetadata(clazz, "percentHeight"); //NOI18N
        assert propertyMeta instanceof DoublePropertyMetadata;
        return ((DoublePropertyMetadata) propertyMeta).getValue(instance);
    }

    public static void setPercentHeight(
            final FXOMInstance instance, final Class<?> clazz, final double value) {
        assert instance != null && clazz != null;
        assert clazz.isAssignableFrom(instance.getDeclaredClass());
        final PropertyMetadata propertyMeta
                = getPropertyMetadata(clazz, "percentHeight"); //NOI18N
        assert propertyMeta instanceof DoublePropertyMetadata;
        ((DoublePropertyMetadata) propertyMeta).setValue(instance, value);
    }

    public static double getPercentWidth(
            final FXOMInstance instance, final Class<?> clazz) {
        assert instance != null && clazz != null;
        assert clazz.isAssignableFrom(instance.getDeclaredClass());
        final PropertyMetadata propertyMeta
                = getPropertyMetadata(clazz, "percentWidth"); //NOI18N
        assert propertyMeta instanceof DoublePropertyMetadata;
        return ((DoublePropertyMetadata) propertyMeta).getValue(instance);
    }

    public static void setPercentWidth(
            final FXOMInstance instance, final Class<?> clazz, final double value) {
        assert instance != null && clazz != null;
        assert clazz.isAssignableFrom(instance.getDeclaredClass());
        final PropertyMetadata propertyMeta
                = getPropertyMetadata(clazz, "percentWidth"); //NOI18N
        assert propertyMeta instanceof DoublePropertyMetadata;
        ((DoublePropertyMetadata) propertyMeta).setValue(instance, value);
    }

    public static double getPrefHeight(
            final FXOMInstance instance, final Class<?> clazz) {
        assert instance != null && clazz != null;
        assert clazz.isAssignableFrom(instance.getDeclaredClass());
        final PropertyMetadata propertyMeta
                = getPropertyMetadata(clazz, "prefHeight"); //NOI18N
        assert propertyMeta instanceof DoublePropertyMetadata;
        return ((DoublePropertyMetadata) propertyMeta).getValue(instance);
    }

    public static void setPrefHeight(
            final FXOMInstance instance, final Class<?> clazz, final double value) {
        assert instance != null && clazz != null;
        assert clazz.isAssignableFrom(instance.getDeclaredClass());
        final PropertyMetadata propertyMeta
                = getPropertyMetadata(clazz, "prefHeight"); //NOI18N
        assert propertyMeta instanceof DoublePropertyMetadata;
        ((DoublePropertyMetadata) propertyMeta).setValue(instance, value);
    }

    public static void setPrefViewportHeight(
            final FXOMInstance instance, final Class<?> clazz, final double value) {
        assert instance != null && clazz != null;
        assert clazz.isAssignableFrom(instance.getDeclaredClass());
        final PropertyMetadata propertyMeta
                = getPropertyMetadata(clazz, "prefViewportHeight"); //NOI18N
        assert propertyMeta instanceof DoublePropertyMetadata;
        ((DoublePropertyMetadata) propertyMeta).setValue(instance, value);
    }

    public static void setPrefViewportWidth(
            final FXOMInstance instance, final Class<?> clazz, final double value) {
        assert instance != null && clazz != null;
        assert clazz.isAssignableFrom(instance.getDeclaredClass());
        final PropertyMetadata propertyMeta
                = getPropertyMetadata(clazz, "prefViewportWidth"); //NOI18N
        assert propertyMeta instanceof DoublePropertyMetadata;
        ((DoublePropertyMetadata) propertyMeta).setValue(instance, value);
    }

    public static double getPrefWidth(
            final FXOMInstance instance, final Class<?> clazz) {
        assert instance != null && clazz != null;
        assert clazz.isAssignableFrom(instance.getDeclaredClass());
        final PropertyMetadata propertyMeta
                = getPropertyMetadata(clazz, "prefWidth"); //NOI18N
        assert propertyMeta instanceof DoublePropertyMetadata;
        return ((DoublePropertyMetadata) propertyMeta).getValue(instance);
    }

    public static void setPrefWidth(
            final FXOMInstance instance, final Class<?> clazz, final double value) {
        assert instance != null && clazz != null;
        assert clazz.isAssignableFrom(instance.getDeclaredClass());
        final PropertyMetadata propertyMeta
                = getPropertyMetadata(clazz, "prefWidth"); //NOI18N
        assert propertyMeta instanceof DoublePropertyMetadata;
        ((DoublePropertyMetadata) propertyMeta).setValue(instance, value);
    }

    public static String getVAlignment(
            final FXOMInstance instance, final Class<?> clazz) {
        assert instance != null && clazz != null;
        assert clazz.isAssignableFrom(instance.getDeclaredClass());
        final PropertyName propertyName = new PropertyName("valignment");  //NOI18N
        final FXOMProperty property = instance.getProperties().get(propertyName);
        if (property == null) {
            return null;
        } else {
            final PropertyMetadata propertyMeta
                    = getPropertyMetadata(clazz, "valignment"); //NOI18N
            assert propertyMeta instanceof EnumerationPropertyMetadata;
            return ((EnumerationPropertyMetadata) propertyMeta).getValue(instance);
        }
    }

    public static void setVAlignment(
            final FXOMInstance instance, final Class<?> clazz, final String value) {
        assert instance != null && clazz != null;
        assert clazz.isAssignableFrom(instance.getDeclaredClass());
        final PropertyMetadata propertyMeta
                = getPropertyMetadata(clazz, "valignment"); //NOI18N
        assert propertyMeta instanceof EnumerationPropertyMetadata;
        if (value != null) {
            ((EnumerationPropertyMetadata) propertyMeta).setValue(instance, value);
        }
    }

    public static String getVGrow(
            final FXOMInstance instance, final Class<?> clazz) {
        assert instance != null && clazz != null;
        assert clazz.isAssignableFrom(instance.getDeclaredClass());
        final PropertyName propertyName = new PropertyName("vgrow");  //NOI18N
        final FXOMProperty property = instance.getProperties().get(propertyName);
        if (property == null) {
            return null;
        } else {
            final PropertyMetadata propertyMeta
                    = getPropertyMetadata(clazz, "vgrow"); //NOI18N
            assert propertyMeta instanceof EnumerationPropertyMetadata;
            return ((EnumerationPropertyMetadata) propertyMeta).getValue(instance);
        }
    }

    public static void setVGrow(
            final FXOMInstance instance, final Class<?> clazz, final String value) {
        assert instance != null && clazz != null;
        assert clazz.isAssignableFrom(instance.getDeclaredClass());
        final PropertyMetadata propertyMeta
                = getPropertyMetadata(clazz, "vgrow"); //NOI18N
        assert propertyMeta instanceof EnumerationPropertyMetadata;
        if (value != null) {
            ((EnumerationPropertyMetadata) propertyMeta).setValue(instance, value);
        }
    }

    // Returns a String with no trailing zero; if decimal part is non zero then
    // it is kept.
    public static String getStringFromDouble(double value) {
        String res = Double.toString(value);
        if(res.endsWith(".0")) { //NOI18N
            res = Integer.toString((int)value);
        }
        return res;
    }

    /*
     * Private
     */
    private static PropertyMetadata getPropertyMetadata(
            final Class<?> componentClass, final String name) {

        final ComponentClassMetadata componentClassMetadata
                = Metadata.getMetadata().queryComponentMetadata(componentClass);
        final PropertyName propertyName = new PropertyName(name);
        return componentClassMetadata.lookupProperty(propertyName);
    }
}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/skeleton/AbstractSkeletonCreator.java<|end_filename|>
/*
 * Copyright (c) 2021, Gluon and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.kit.skeleton;

import com.oracle.javafx.scenebuilder.kit.i18n.I18N;

import java.lang.reflect.TypeVariable;
import java.net.URL;
import java.util.Map;
import java.util.ResourceBundle;

abstract class AbstractSkeletonCreator {

    static final String NL = System.lineSeparator();
    static final String INDENT = "    "; //NOI18N
    static final String FXML_ANNOTATION = "@FXML";

    String createFrom(SkeletonContext context) {
        final StringBuilder sb = new StringBuilder();

        appendHeaderComment(context, sb);
        appendPackage(context, sb);
        appendImports(context, sb);
        appendClass(context, sb);

        return sb.toString();
    }

    void appendHeaderComment(SkeletonContext context, StringBuilder sb) {
        if (!context.getSettings().isWithComments()) {
            return;
        }

        final String title = I18N.getString("skeleton.window.title", context.getDocumentName());
        sb.append("/**").append(NL); //NOI18N
        sb.append(" * ").append(title).append(NL); //NOI18N
        sb.append(" */").append(NL); //NOI18N
        sb.append(NL);
    }

    abstract void appendPackage(SkeletonContext context, StringBuilder sb);

    abstract void appendImports(SkeletonContext context, StringBuilder sb);

    void appendClass(SkeletonContext context, StringBuilder sb) {
        sb.append(NL);

        appendClassPart(context, sb);

        sb.append(" {").append(NL).append(NL); //NOI18N

        appendFields(context, sb);

        appendMethods(context, sb);

        sb.append("}").append(NL); //NOI18N
    }

    abstract void appendClassPart(SkeletonContext context, StringBuilder sb);

    void appendFields(SkeletonContext context, StringBuilder sb) {
        appendFieldsResourcesAndLocation(context, sb);

        appendFieldsWithFxId(context, sb);
    }

    void appendFieldsResourcesAndLocation(SkeletonContext context, StringBuilder sb) {
        if (!context.getSettings().isFull()) {
            return;
        }

        sb.append(INDENT).append(FXML_ANNOTATION);
        if (context.getSettings().isWithComments()) {
            sb.append(" // ResourceBundle that was given to the FXMLLoader"); //NOI18N
        }
        sb.append(NL);
        sb.append(INDENT);
        appendField(ResourceBundle.class, "resources", sb); //NOI18N
        sb.append(NL).append(NL);

        sb.append(INDENT).append(FXML_ANNOTATION);
        if (context.getSettings().isWithComments()) {
            sb.append(" // URL location of the FXML file that was given to the FXMLLoader"); //NOI18N
        }
        sb.append(NL);
        sb.append(INDENT);
        appendField(URL.class, "location", sb); //NOI18N
        sb.append(NL).append(NL);
    }

    void appendFieldsWithFxId(SkeletonContext context, StringBuilder sb) {
        for (Map.Entry<String, Class<?>> variable : context.getVariables().entrySet()) {
            sb.append(INDENT).append(FXML_ANNOTATION);
            if (context.getSettings().isWithComments()) {
                sb.append(" // fx:id=\"").append(variable.getKey()).append("\""); //NOI18N
            }
            sb.append(NL);

            sb.append(INDENT);
            appendField(variable.getValue(), variable.getKey(), sb);

            if (context.getSettings().isWithComments()) {
                sb.append(" // Value injected by FXMLLoader"); //NOI18N
            }
            sb.append(NL).append(NL);
        }
    }

    abstract void appendField(Class<?> fieldClass, String fieldName, StringBuilder sb);

    void appendFieldParameters(StringBuilder sb, Class<?> fieldClazz) {
        final TypeVariable<? extends Class<?>>[] parameters = fieldClazz.getTypeParameters();
        if (parameters.length > 0) {
            sb.append("<"); //NOI18N
            String sep = ""; //NOI18N
            for (TypeVariable<?> ignored : parameters) {
                sb.append(sep);
                appendFieldParameterType(sb);
                sep = ", "; //NOI18N
            }
            sb.append(">"); //NOI18N
        }
    }

    abstract void appendFieldParameterType(StringBuilder sb);

    void appendMethods(SkeletonContext context, StringBuilder sb) {
        appendEventHandlers(context, sb);

        appendInitialize(context, sb);
    }

    void appendEventHandlers(SkeletonContext context, StringBuilder sb) {
        for (Map.Entry<String, String> entry : context.getEventHandlers().entrySet()) {
            String methodName = entry.getKey();
            String eventClassName = entry.getValue();

            final String methodNamePured = methodName.replace("#", ""); //NOI18N

            sb.append(INDENT).append(FXML_ANNOTATION).append(NL).append(INDENT);
            appendEventHandler(methodNamePured, eventClassName, sb);
            sb.append(NL).append(NL);
        }
    }

    abstract void appendEventHandler(String methodName, String eventClassName, StringBuilder sb);

    void appendInitialize(SkeletonContext context, StringBuilder sb) {
        if (!context.getSettings().isFull()) {
            return;
        }

        sb.append(INDENT).append(FXML_ANNOTATION);
        if (context.getSettings().isWithComments()) {
            sb.append(" // This method is called by the FXMLLoader when initialization is complete"); //NOI18N
        }
        sb.append(NL);

        sb.append(INDENT);
        appendInitializeMethodPart(sb);
        sb.append(" {").append(NL); //NOI18N
        appendAssertions(context, sb);
        sb.append(NL);
        sb.append(INDENT);
        sb.append("}").append(NL).append(NL); //NOI18N
    }

    abstract void appendInitializeMethodPart(StringBuilder sb);

    abstract void appendAssertions(SkeletonContext context, StringBuilder sb);
}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/hierarchy/HierarchyAnimationScheduler.java<|end_filename|>
/*
 * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.kit.editor.panel.hierarchy;

import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.scene.control.ScrollBar;
import javafx.util.Duration;

/**
 * Used to schedule :
 *
 * - auto scrolling animation when reaching the TOP/BOTTOM of the hierarchy panel
 *
 * p
 * @treatAsPrivate
 */
public class HierarchyAnimationScheduler {

    private Timeline timeline;
    // Rate value used to set the timeline duration.
    // The bigger it is, the slower the animation will be.
    private final double rate = 4.0;

    public void playDecrementAnimation(final ScrollBar scrollBar) {
        assert scrollBar != null;
        final double minValue = scrollBar.getMin();
        assert isTimelineRunning() == false;
        // If the scroll bar is not yet at its min value,
        // we play the scroll bar decrement animation
        if (scrollBar.getValue() > minValue) {
            // The timeline duration value depends on :
            // - the scroll bar height
            // - the scroll bar thumb size (visibleAmount property)
            // - the scroll bar value
            final double scrollBarHeight = scrollBar.getHeight();
            final double scrollBarVisibleAmount = scrollBar.getVisibleAmount();
            final double scrollBarValue = scrollBar.getValue();

            // Height between the scroll bar top and the scroll bar thumb
            final double height = scrollBarHeight * scrollBarValue;
            final double duration = height * rate / scrollBarVisibleAmount; // duration in millis

            getTimeline().getKeyFrames().setAll(new KeyFrame(
                    new Duration(duration),
                    new KeyValue(scrollBar.valueProperty(), minValue)));
            getTimeline().play();
        }
    }

    public void playIncrementAnimation(final ScrollBar scrollBar) {
        assert scrollBar != null;
        final double maxValue = scrollBar.getMax();
        assert isTimelineRunning() == false;
        // If the scroll bar is not yet at its max value,
        // we play the scroll bar increment animation
        if (scrollBar.getValue() < maxValue) {
            // The timeline duration value depends on :
            // - the scroll bar height
            // - the scroll bar thumb size (visibleAmount property)
            // - the scroll bar value
            final double scrollBarHeight = scrollBar.getHeight();
            final double scrollBarVisibleAmount = scrollBar.getVisibleAmount();
            final double scrollBarValue = scrollBar.getValue();

            // Height between the scroll bar thumb and the scroll bar bottom
            final double height = scrollBarHeight * (scrollBar.getMax() - scrollBarValue);
            final double duration = height * rate / scrollBarVisibleAmount; // duration in millis

            getTimeline().getKeyFrames().setAll(new KeyFrame(
                    new Duration(duration),
                    new KeyValue(scrollBar.valueProperty(), maxValue)));
            getTimeline().play();
        }
    }

    public boolean isTimelineRunning() {
        return timeline == null ? false
                : timeline.getStatus() == Timeline.Status.RUNNING;
    }

    public void stopTimeline() {
        if (timeline != null) {
            timeline.stop();
            timeline = null;
        }
    }

    private Timeline getTimeline() {
        if (timeline == null) {
            timeline = new Timeline();
        }
        return timeline;
    }
}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/content/driver/pring/TabPring.java<|end_filename|>
/*
 * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.kit.editor.panel.content.driver.pring;

import com.oracle.javafx.scenebuilder.kit.editor.panel.content.ContentPanelController;
import com.oracle.javafx.scenebuilder.kit.editor.panel.content.driver.TabOutline;
import com.oracle.javafx.scenebuilder.kit.editor.panel.content.driver.TabPaneDesignInfoX;
import com.oracle.javafx.scenebuilder.kit.editor.panel.content.gesture.AbstractGesture;
import com.oracle.javafx.scenebuilder.kit.editor.panel.content.gesture.SelectWithPringGesture;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMInstance;
import javafx.geometry.Bounds;
import javafx.scene.Node;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.scene.paint.Paint;

/**
 *
 * 
 */
public class TabPring extends AbstractPring<Tab> {

    //
    //           2              3
    //           +--------------+
    //     0     |              |                 5
    //     +-----+              +-----------------+
    //     |     1              4                 |
    //     |                                      |
    //     |                                      |
    //     |                                      |
    //     +--------------------------------------+
    //     7                                      6
    //
        

    private final TabOutline tabOutline;

    private Node tabNode; // Skin node representing the tab
    
    public TabPring(ContentPanelController contentPanelController, FXOMInstance fxomInstance) {
        super(contentPanelController, fxomInstance, Tab.class);
        assert fxomInstance.getSceneGraphObject() instanceof Tab;
        
        tabOutline = new TabOutline(getSceneGraphObject());
        tabOutline.getRingPath().getStyleClass().add(PARENT_RING_CLASS);
        getRootNode().getChildren().add(tabOutline.getRingPath());
        
        attachPring(tabOutline.getRingPath());
    }
    
    public FXOMInstance getFxomInstance() {
        return (FXOMInstance) getFxomObject();
    }

    /*
     * AbstractPring
     */
    
    @Override
    protected void layoutDecoration() {
        tabOutline.layout(this);
    }
    
    @Override
    public void changeStroke(Paint stroke) {
        tabOutline.getRingPath().setStroke(stroke);
    }
    
    
    /*
     * AbstractDecoration
     */
    
    @Override
    public Bounds getSceneGraphObjectBounds() {
        return getSceneGraphObject().getTabPane().getLayoutBounds();
    }

    @Override
    public Node getSceneGraphObjectProxy() {
        return getSceneGraphObject().getTabPane();
    }

    @Override
    protected void startListeningToSceneGraphObject() {
        assert tabNode == null;
        
        final TabPane tabPane = getSceneGraphObject().getTabPane();
        startListeningToLayoutBounds(tabPane);
        startListeningToLocalToSceneTransform(tabPane);
        
        final TabPaneDesignInfoX di = new TabPaneDesignInfoX();
        tabNode = di.getTabNode(tabPane, getSceneGraphObject());
        startListeningToBoundsInParent(tabNode);
    }

    @Override
    protected void stopListeningToSceneGraphObject() {
        assert tabNode != null;
        
        final TabPane tabPane = getSceneGraphObject().getTabPane();
        stopListeningToLayoutBounds(tabPane);
        stopListeningToLocalToSceneTransform(tabPane);
        stopListeningToBoundsInParent(tabNode);
        
        tabNode = null;
    }

    @Override
    public AbstractGesture findGesture(Node node) {
        final AbstractGesture result;
        
        if (node == tabOutline.getRingPath()) {
            result = new SelectWithPringGesture(getContentPanelController(), 
                    getFxomInstance());
        } else {
            result = null;
        }
        
        return result;
    }
    
    /* 
     * Wraper to avoid the 'leaking this in constructor' warning emitted by NB.
     */
    private void attachPring(Node node) {
        attachPring(node, this);
    }
}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/job/atomic/RemovePropertyValueJob.java<|end_filename|>
/*
 * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.kit.editor.job.atomic;

import com.oracle.javafx.scenebuilder.kit.editor.job.atomic.RemovePropertyJob;
import com.oracle.javafx.scenebuilder.kit.editor.EditorController;
import com.oracle.javafx.scenebuilder.kit.editor.job.Job;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMObject;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMPropertyC;

/**
 *
 */
public class RemovePropertyValueJob extends Job {

    private final FXOMObject targetValue;
    
    private FXOMPropertyC parentProperty;
    private int indexInParentProperty;
    private RemovePropertyJob removePropertyJob;

    public RemovePropertyValueJob(FXOMObject value, EditorController editorController) {
        super(editorController);
        this.targetValue = value;
    }
    
    
    /*
     * Job
     */
    
    @Override
    public boolean isExecutable() {
        return targetValue.getParentProperty() != null;
    }

    @Override
    public void execute() {
        assert parentProperty == null;
        assert isExecutable();
        
        parentProperty = targetValue.getParentProperty();
        indexInParentProperty = targetValue.getIndexInParentProperty();
        if ((parentProperty.getValues().size() == 1) && (parentProperty.getParentInstance() != null)) {
            // targetValue is the last value of its parent property
            // => parent property must also be removed from its parent instance
            removePropertyJob = new RemovePropertyJob(parentProperty, getEditorController());
        }
        
        // Note : below we may have to run removePropertyJob.execute() so
        // we cannot re-use redo() here.
        getEditorController().getFxomDocument().beginUpdate();
        if (removePropertyJob != null) {
            removePropertyJob.execute();
        }
        targetValue.removeFromParentProperty();
        getEditorController().getFxomDocument().endUpdate();
    }

    @Override
    public void undo() {
        assert targetValue.getParentProperty() == null;
        
        getEditorController().getFxomDocument().beginUpdate();
        targetValue.addToParentProperty(indexInParentProperty, parentProperty);
        if (removePropertyJob != null) {
            removePropertyJob.undo();
        }
        getEditorController().getFxomDocument().endUpdate();

        assert targetValue.getParentProperty() == parentProperty;
        assert targetValue.getIndexInParentProperty() == indexInParentProperty;
    }

    @Override
    public void redo() {
        assert targetValue.getParentProperty() == parentProperty;
        assert targetValue.getIndexInParentProperty() == indexInParentProperty;
        
        getEditorController().getFxomDocument().beginUpdate();
        if (removePropertyJob != null) {
            removePropertyJob.redo();
        }
        targetValue.removeFromParentProperty();
        getEditorController().getFxomDocument().endUpdate();

        assert targetValue.getParentProperty() == null;
    }

    @Override
    public String getDescription() {
        // Should normally not reach the user
        return getClass().getSimpleName() 
                + "[" 
                + targetValue.getGlueElement().getTagName()
                + "]";
    }
    
}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/content/driver/TreeTableViewDesignInfoX.java<|end_filename|>
/*
 * Copyright (c) 2017 Gluon and/or its affiliates.
 * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.kit.editor.panel.content.driver;

import com.oracle.javafx.scenebuilder.kit.util.Deprecation;
import javafx.scene.control.skin.TableColumnHeader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import javafx.geometry.BoundingBox;
import javafx.geometry.Bounds;
import javafx.geometry.Point2D;
import javafx.scene.Node;
import javafx.scene.control.TableColumnBase;
import javafx.scene.control.TreeTableColumn;
import javafx.scene.control.TreeTableView;

/**
 * A temporary class that should extend TableViewDesignInfo and adds
 * some additional verbs for managing TableView at design time.
 * This could potentially move to TabDesignInfo some day.
 *
 */
public class TreeTableViewDesignInfoX {


    public Bounds getColumnBounds(TreeTableColumn<?,?> treeTableColumn) {
        final TreeTableView<?> tv = treeTableColumn.getTreeTableView();
        final Bounds tb = tv.getLayoutBounds();
        final Bounds hb = getColumnHeaderBounds(treeTableColumn);
        
        //
        //           x0             x1          
        //
        //     +--------------------------------------+
        // y0  |     +--------------+                 |
        //     |     |    header    |                 |
        //     |     +--------------+                 |
        //     +--------------------------------------+
        //     |                                      |
        //     |          table view content          |
        //     |                                      |
        // y1  +--------------------------------------+
        //
        
        final double x0 = hb.getMinX();
        final double x1 = hb.getMaxX();
        final double y0 = hb.getMinY();
        final double y1 = tb.getMaxY();
        
        return new BoundingBox(x0, y0, x1 - x0, y1 - y0);
    }
    
    
    public Bounds getColumnHeaderBounds(TreeTableColumn<?,?> treeTableColumn) {
        final TreeTableView<?> tv = treeTableColumn.getTreeTableView();
        final Node hn = getColumnNode(treeTableColumn);
        return Deprecation.localToLocal(hn, hn.getLayoutBounds(), tv);
    }
    
    
    public Node getColumnNode(TreeTableColumn<?,?> tableColumn) {
        assert tableColumn != null;
        assert tableColumn.getTreeTableView() != null;
        

        // Looks for the sub nodes which match the .column-header CSS selector
        final TreeTableView<?> tableView = tableColumn.getTreeTableView();
        final Set<Node> set = tableView.lookupAll(".column-header"); //NOI18N
        
        // Searches the result for the node associated to 'tableColumn'.
        // This item has (TableColumn.class, tableColumn) in its property list.
        Node result = null;
        final Iterator<Node> it = set.iterator();
        while ((result == null) && it.hasNext()) {
            Node n = it.next();
            assert n instanceof TableColumnHeader;
            final TableColumnBase<?,?> tc = ((TableColumnHeader)n).getTableColumn();
            if (tc == tableColumn) {
                result = n;
            }
        }

        return result;
    }
    
    
    public <T> TreeTableColumn<T,?> lookupColumn(TreeTableView<T>  tableView, double sceneX, double sceneY) {
        TreeTableColumn<T,?> result = null;
        
        //
        //                     x
        //          +--------------------------------------+
        //          |    +----------------------------+    |
        // #1    y  |    |            header          |    |
        //          |    +----------------------------+    |
        //          |    +--------------+-------------+    |
        // #2    y  |    |    header    |    header   |    |
        //          |    +--------------+-------------+    |
        //          +--------------------------------------+
        //          |                                      |
        // #3    y  |          table view content          |
        //          |                                      |
        //          +--------------------------------------+
        //
        
        // Walk through the column to see if one contains 'x' vertical
        List<TreeTableColumn<T,?>> tableColumns = tableView.getColumns();
        List<TreeTableColumn<T,?>> columnPath = new ArrayList<>();
        while (tableColumns.isEmpty() == false) {
            final TreeTableColumn<T,?> tc = lookupColumn(tableColumns, sceneX);
            if (tc != null) {
                columnPath.add(0, tc);
                tableColumns = tc.getColumns();
            } else {
                tableColumns = Collections.emptyList(); // To stop the loop
            }
        }
        
        if (columnPath.isEmpty()) {
            // No column contains sceneX
            result = null;
        } else {
            // Check if one column in columnPath contains (sceneX, sceneY)
            // => case #1 or #2
            for (TreeTableColumn<T,?> tc : columnPath) {
                final Node headerNode = getColumnNode(tc);
                final Bounds headerBounds = headerNode.getLayoutBounds();
                final Point2D p = headerNode.sceneToLocal(sceneX, sceneY, true /* rootScene */);
                if (headerBounds.contains(p)) {
                    result = tc;
                    break;
                }
            }
            
            if (result == null) {
                // No column in columnPath contains sceneX => case #3
                result = columnPath.get(0);
            }
        }
        
        return result;
    }
    
    
    private <T> TreeTableColumn<T,?> lookupColumn(
            List<TreeTableColumn<T,?>> tableColumns, double sceneX) {
        TreeTableColumn<T,?> result = null;
        
        // Walk through the columns to see if one contains 'x' vertical
        for (TreeTableColumn<T,?> tc : tableColumns) {
            final Node headerNode = getColumnNode(tc);
            if (headerNode != null) {
                final Bounds headerBounds = headerNode.getLayoutBounds();
                final Point2D p = headerNode.sceneToLocal(sceneX, 0, true /* rootScene */);
                if ((headerBounds.getMinX() <= p.getX()) 
                        && (p.getX() < headerBounds.getMaxX())) {
                    result = tc;
                    break;
                }
            }
        }
        
        return result;
    }
}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/content/driver/tring/Quad.java<|end_filename|>
/*
 * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.kit.editor.panel.content.driver.tring;

import java.util.List;
import javafx.geometry.Bounds;
import javafx.geometry.Insets;
import javafx.scene.shape.ClosePath;
import javafx.scene.shape.LineTo;
import javafx.scene.shape.MoveTo;
import javafx.scene.shape.Path;
import javafx.scene.shape.PathElement;

/**
 *
 * 
 */
public class Quad {
    
    private final boolean clockwise;
    private final MoveTo moveTo0 = new MoveTo();
    private final LineTo lineTo1 = new LineTo();
    private final LineTo lineTo2 = new LineTo();
    private final LineTo lineTo3 = new LineTo();
    
    public Quad(boolean clockwise) {
        this.clockwise = clockwise;
    }
    
    public Quad() {
        this(true /* clockwise */);
    }

    public void addToPath(Path path) {
        assert path != null;
        
        final List<PathElement> ringElements = path.getElements();
        if (clockwise) {
            ringElements.add(moveTo0);
            ringElements.add(lineTo1);
            ringElements.add(lineTo2);
            ringElements.add(lineTo3);
        } else {
            ringElements.add(moveTo0);
            ringElements.add(lineTo3);
            ringElements.add(lineTo2);
            ringElements.add(lineTo1);
        }
        ringElements.add(new ClosePath());
    }
    
    public void removeFromPath(Path path) {
        assert path != null;
        assert path.getElements().contains(moveTo0);
        
        final List<PathElement> ringElements = path.getElements();
        ringElements.remove(moveTo0);
        ringElements.remove(lineTo1);
        ringElements.remove(lineTo2);
        ringElements.remove(lineTo3);
    }

    public double getX0() {
        return moveTo0.getX();
    }
    
    public void setX0(double x) {
        moveTo0.setX(x);
    }

    public double getY0() {
        return moveTo0.getY();
    }

    public void setY0(double y) {
        moveTo0.setY(y);
    }

    public double getX1() {
        return lineTo1.getX();
    }

    public void setX1(double x) {
        lineTo1.setX(x);
    }

    public double getY1() {
        return lineTo1.getY();
    }

    public void setY1(double y) {
        lineTo1.setY(y);
    }

    public double getX2() {
        return lineTo2.getX();
    }

    public void setX2(double x) {
        lineTo2.setX(x);
    }

    public double getY2() {
        return lineTo2.getY();
    }

    public void setY2(double y) {
        lineTo2.setY(y);
    }

    public double getX3() {
        return lineTo3.getX();
    }

    public void setX3(double x) {
        lineTo3.setX(x);
    }

    public double getY3() {
        return lineTo3.getY();
    }

    public void setY3(double y) {
        lineTo3.setY(y);
    }

    public void setBounds(Bounds bounds) {
        moveTo0.setX(bounds.getMinX());
        moveTo0.setY(bounds.getMinY());
        lineTo1.setX(bounds.getMaxX());
        lineTo1.setY(bounds.getMinY());
        lineTo2.setX(bounds.getMaxX());
        lineTo2.setY(bounds.getMaxY());
        lineTo3.setX(bounds.getMinX());
        lineTo3.setY(bounds.getMaxY());
    }
    
    public void insets(Insets insets) {
        moveTo0.setX(moveTo0.getX() + insets.getLeft());
        moveTo0.setY(moveTo0.getY() + insets.getTop());
        lineTo1.setX(lineTo1.getX() - insets.getRight());
        lineTo1.setY(lineTo1.getY() + insets.getTop());
        lineTo2.setX(lineTo2.getX() - insets.getRight());
        lineTo2.setY(lineTo2.getY() - insets.getBottom());
        lineTo3.setX(lineTo3.getX() + insets.getLeft());
        lineTo3.setY(lineTo3.getY() - insets.getBottom());
    }
    
    public void insets(Insets insets, double minInset) {
        assert minInset >= 0.0;
        moveTo0.setX(moveTo0.getX() + Math.max(minInset, insets.getLeft()));
        moveTo0.setY(moveTo0.getY() + Math.max(minInset, insets.getTop()));
        lineTo1.setX(lineTo1.getX() - Math.max(minInset, insets.getRight()));
        lineTo1.setY(lineTo1.getY() + Math.max(minInset, insets.getTop()));
        lineTo2.setX(lineTo2.getX() - Math.max(minInset, insets.getRight()));
        lineTo2.setY(lineTo2.getY() - Math.max(minInset, insets.getBottom()));
        lineTo3.setX(lineTo3.getX() + Math.max(minInset, insets.getLeft()));
        lineTo3.setY(lineTo3.getY() - Math.max(minInset, insets.getBottom()));
    }
}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/job/wrap/WrapInGridPaneJob.java<|end_filename|>
/*
 * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.kit.editor.job.wrap;

import com.oracle.javafx.scenebuilder.kit.editor.EditorController;
import com.oracle.javafx.scenebuilder.kit.editor.job.Job;
import com.oracle.javafx.scenebuilder.kit.editor.job.JobUtils;
import com.oracle.javafx.scenebuilder.kit.editor.job.atomic.ModifyObjectJob;
import com.oracle.javafx.scenebuilder.kit.editor.job.wrap.FXOMObjectCourseComparator.BidimensionalComparator;
import com.oracle.javafx.scenebuilder.kit.editor.job.wrap.FXOMObjectCourseComparator.GridCourse;
import static com.oracle.javafx.scenebuilder.kit.editor.job.wrap.FXOMObjectCourseComparator.OVERLAP_FUZZ;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMDocument;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMInstance;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMObject;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javafx.geometry.Bounds;
import javafx.scene.Node;
import javafx.scene.layout.ColumnConstraints;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Priority;
import javafx.scene.layout.RowConstraints;

/**
 * Job used to wrap selection in a GridPane.
 */
public class WrapInGridPaneJob extends AbstractWrapInSubComponentJob {

    private static final double DEFAULT_MIN_WIDTH = 10;
    private static final double DEFAULT_MIN_HEIGHT = 10;
    // Key = FXOM object
    // Value = 2 dimensions integer array for the COLUMN and ROW index
    private final Map<FXOMObject, int[]> indices = new HashMap<>();

    public WrapInGridPaneJob(EditorController editorController) {
        super(editorController);
        newContainerClass = GridPane.class;
    }

    @Override
    protected List<Job> modifyChildrenJobs(final List<FXOMObject> children) {
        final List<Job> jobs = super.modifyChildrenJobs(children);

        for (FXOMObject child : children) {
            int[] childIndices = indices.get(child);

            // Modify child column index
            final ModifyObjectJob modifyColumnIndex = WrapJobUtils.modifyObjectJob(
                    (FXOMInstance) child, GridPane.class, "columnIndex", //NOI18N
                    childIndices[GridCourse.COL_BY_COL.index()],
                    getEditorController());
            jobs.add(modifyColumnIndex);

            // Modify child row index
            final ModifyObjectJob modifyRowIndex = WrapJobUtils.modifyObjectJob(
                    (FXOMInstance) child, GridPane.class, "rowIndex", //NOI18N
                    childIndices[GridCourse.ROW_BY_ROW.index()],
                    getEditorController());
            jobs.add(modifyRowIndex);
        }
        return jobs;
    }

    @Override
    protected void modifyNewContainer(final List<FXOMObject> children) {
        super.modifyNewContainer(children);

        final FXOMDocument fxomDocument = getEditorController().getFxomDocument();

        // Update the GridPane constraints depending on its children positionning
        // Find and set the column index for each element in the indices map.
        final int maxcol = computeIndexByCourse(children, GridCourse.COL_BY_COL, indices);
        // Find and set the row index for each element in the indices map.
        final int maxrow = computeIndexByCourse(children, GridCourse.ROW_BY_ROW, indices);
        final double[] columnWidth = new double[maxcol + 1];
        final double[] rowHeight = new double[maxrow + 1];
        computeSizes(children, indices, columnWidth, rowHeight);

        // COLUMNS
        for (int index = 0; index <= maxcol; index++) {
            final FXOMInstance constraint = makeConstraintsInstance(ColumnConstraints.class);
            JobUtils.setHGrow(constraint, ColumnConstraints.class, Priority.SOMETIMES.name());
            if (columnWidth[index] >= DEFAULT_MIN_WIDTH) {
                JobUtils.setMinWidth(constraint, ColumnConstraints.class, DEFAULT_MIN_WIDTH);
            } else {
                JobUtils.setMinWidth(constraint, ColumnConstraints.class, columnWidth[index]);
            }

            JobUtils.addColumnConstraints(fxomDocument, newContainer, constraint, index);
        }

        // ROWS
        for (int index = 0; index <= maxrow; index++) {
            final FXOMInstance constraint = makeConstraintsInstance(RowConstraints.class);
            JobUtils.setVGrow(constraint, RowConstraints.class, Priority.SOMETIMES.name());
            if (rowHeight[index] >= DEFAULT_MIN_HEIGHT) {
                JobUtils.setMinHeight(constraint, RowConstraints.class, DEFAULT_MIN_HEIGHT);
            } else {
                JobUtils.setMinHeight(constraint, RowConstraints.class, rowHeight[index]);
            }

            JobUtils.addRowConstraints(fxomDocument, newContainer, constraint, index);
        }
    }

    /**
     * This method computes either the ROW index or COLUMN index of each
     * element, by running through a would-be grid according to a given course.
     * For instance, when course==ROW_BY_ROW, this method first order the
     * elements row by row, and then sets their ROW index inside the indices
     * map. When course==COL_BY_COL, this method orders the elements column by
     * column, and then sets their COLUMN index inside the indices map. Note
     * that this method leaves the original elements and children list
     * unchanged. All it does is populating the indices map.
     *
     * @param fxomObjects The children of the would-be grid.
     * @param course The course for which this method runs.
     * @param indices The indices map.
     * @return the greater index.
     */
    private int computeIndexByCourse(
            final List<FXOMObject> fxomObjects,
            final GridCourse course,
            final Map<FXOMObject, int[]> indices) {

        final BidimensionalComparator comparator = new BidimensionalComparator(course);
        final List<FXOMObject> unsorted = new ArrayList<>(fxomObjects);
        Collections.sort(unsorted, comparator);
        FXOMObject lastObject = null;
        int rc = 0;
        int max = -1;
        for (int i = 0; i < unsorted.size(); i++) {
            FXOMObject currentObject = unsorted.get(i);
            int[] ind = indices.get(currentObject);
            if (ind == null) {
                ind = new int[2];
                indices.put(currentObject, ind);
            }
            if (lastObject != null) {
                if (comparator.compare(lastObject, currentObject) != 0) {
                    final Node lastNode = (Node) lastObject.getSceneGraphObject();
                    final Node currentNode = (Node) currentObject.getSceneGraphObject();
                    final Bounds lastBounds = lastNode.getBoundsInParent();
                    final Bounds currentBounds = currentNode.getBoundsInParent();
                    if (course.getMinY(currentBounds) >= course.getMaxY(lastBounds) - OVERLAP_FUZZ) {
                        rc++;
                    }
                }
            }
            ind[course.index()] = rc;
            max = Math.max(max, rc);
            lastObject = currentObject;
        }
        return max;
    }

    private void computeSizes(
            final List<FXOMObject> fxomObjects,
            final Map<FXOMObject, int[]> indices,
            double[] columnWidth, double[] rowHeight) {

        for (FXOMObject fxomObject : fxomObjects) {
            final Node node = (Node) fxomObject.getSceneGraphObject();
            final double width = node.getBoundsInLocal().getWidth();
            final double height = node.getBoundsInLocal().getHeight();
            final int[] ind = indices.get(fxomObject);
            final int col = ind[GridCourse.COL_BY_COL.index()];
            final int row = ind[GridCourse.ROW_BY_ROW.index()];
            columnWidth[col] = Math.max(columnWidth[col], width);
            rowHeight[row] = Math.max(rowHeight[row], height);
        }
    }

    private FXOMInstance makeConstraintsInstance(final Class<?> constraintsClass) {

        // Create new constraints instance
        final FXOMDocument newDocument = new FXOMDocument();
        final FXOMInstance result
                = new FXOMInstance(newDocument, constraintsClass);
        newDocument.setFxomRoot(result);
        result.moveToFxomDocument(getEditorController().getFxomDocument());

        return result;
    }
}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/job/ModifyCacheHintJob.java<|end_filename|>
/*
 * Copyright (c) 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.kit.editor.job;

import com.oracle.javafx.scenebuilder.kit.editor.EditorController;
import com.oracle.javafx.scenebuilder.kit.i18n.I18N;
import com.oracle.javafx.scenebuilder.kit.editor.job.atomic.ModifyObjectJob;
import com.oracle.javafx.scenebuilder.kit.editor.selection.ObjectSelectionGroup;
import com.oracle.javafx.scenebuilder.kit.editor.selection.Selection;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMInstance;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMObject;
import com.oracle.javafx.scenebuilder.kit.metadata.Metadata;
import com.oracle.javafx.scenebuilder.kit.metadata.property.ValuePropertyMetadata;
import com.oracle.javafx.scenebuilder.kit.metadata.util.PropertyName;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 *
 */
public class ModifyCacheHintJob extends ModifySelectionJob {

    private int subJobCount = 0;
    private final PropertyName cachePN = new PropertyName("cache"); //NOI18N
    private final PropertyName cacheHintPN = new PropertyName("cacheHint"); //NOI18N

    public ModifyCacheHintJob(ValuePropertyMetadata propertyMetadata, Object newValue, EditorController editorController) {
        super(propertyMetadata, newValue, editorController);
        assert cacheHintPN.equals(propertyMetadata.getName());
    }

    @Override
    protected List<Job> makeSubJobs() {

        final List<Job> result = new ArrayList<>();
        final Set<FXOMInstance> candidates = new HashSet<>();
        final Selection selection = getEditorController().getSelection();
        if (selection.getGroup() instanceof ObjectSelectionGroup) {
            final ObjectSelectionGroup osg = (ObjectSelectionGroup) selection.getGroup();
            for (FXOMObject fxomObject : osg.getItems()) {
                if (fxomObject instanceof FXOMInstance) {
                    candidates.add((FXOMInstance) fxomObject);
                }
            }
        } else {
            assert selection.getGroup() == null : "Add implementation for " + selection.getGroup();
        }

        // Add ModifyObject jobs
        for (FXOMInstance fxomInstance : candidates) {
            // ModifyObject job for the cacheHint property
            final ModifyObjectJob subJob1 = new ModifyObjectJob(
                    fxomInstance, propertyMetadata, newValue, getEditorController());
            if (subJob1.isExecutable()) {
                result.add(subJob1);
                subJobCount++;
            }
            // ModifyObject job for the cache property
            if ("DEFAULT".equals(newValue) == false) { //NOI18N
                final ValuePropertyMetadata cacheVPM
                        = Metadata.getMetadata().queryValueProperty(fxomInstance, cachePN);
                final ModifyObjectJob subJob2 = new ModifyObjectJob(
                        fxomInstance, cacheVPM, Boolean.TRUE, getEditorController());
                if (subJob2.isExecutable()) {
                    result.add(subJob2);
                }
            }
        }

        return result;
    }

    @Override
    protected String makeDescription() {
        final String result;

        switch (subJobCount) {
            case 0:
                result = "Unexecutable Set"; //NOI18N
                break;
            case 1: // Single selection
                result = getSubJobs().get(0).getDescription();
                break;
            default:
                result = I18N.getString("label.action.edit.set.n",
                        propertyMetadata.getName().toString(),
                        subJobCount);
                break;
        }

        return result;
    }
}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/util/control/effectpicker/editors/DoubleTextFieldControl.java<|end_filename|>
/*
 * Copyright (c) 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.kit.util.control.effectpicker.editors;

import com.oracle.javafx.scenebuilder.kit.editor.panel.inspector.editors.DoubleField;
import com.oracle.javafx.scenebuilder.kit.editor.panel.inspector.editors.EditorUtils;
import com.oracle.javafx.scenebuilder.kit.util.control.effectpicker.EffectPickerController;
import com.oracle.javafx.scenebuilder.kit.util.control.effectpicker.Utils;

import javafx.beans.property.SimpleDoubleProperty;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.control.Label;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.GridPane;

import java.io.IOException;
import java.io.InputStream;
import java.net.URL;

import javafx.beans.property.DoubleProperty;
import javafx.beans.value.ChangeListener;
import javafx.event.ActionEvent;

public class DoubleTextFieldControl extends GridPane {

    @FXML
    private Label editor_label;
    @FXML
    private DoubleField editor_textfield;

    private double mini;
    private double maxi;
    private double incDecValue;
    private final DoubleProperty value = new SimpleDoubleProperty();
    private final EffectPickerController effectPickerController;
    private final int roundingFactor = 100; // 2 decimals rounding

    public DoubleTextFieldControl(
            EffectPickerController effectPickerController,
            String labelString,
            double min,
            double max,
            double initVal,
            double incDec) {
        this.effectPickerController = effectPickerController;
        initialize(labelString, min, max, initVal, incDec);

        editor_textfield.focusedProperty().addListener((ChangeListener<Boolean>) (ov, oldValue, newValue) -> {
            // Commit the value on focus lost
            if (newValue == false) {
                double inputValue = Double.parseDouble(editor_textfield.getText());
                // First update the model
                setValue(inputValue);
                // Then notify the controller a change occured
                effectPickerController.incrementRevision();
            }
        });
        editor_textfield.setOnAction((ActionEvent event) -> {
            event.consume();
        });
    }

    public DoubleProperty valueProperty() {
        return value;
    }

    public double getValue() {
        return value.get();
    }

    @FXML
    void textfieldTyped(KeyEvent e) {
        if (e.getCode() == KeyCode.UP) {
            // First update the model
            incOrDecValue(incDecValue);
            // Then notify the controller a change occured
            effectPickerController.incrementRevision();
        }
        if (e.getCode() == KeyCode.DOWN) {
            // First update the model
            incOrDecValue(-incDecValue);
            // Then notify the controller a change occured
            effectPickerController.incrementRevision();
        }
        if (e.getCode() == KeyCode.ENTER) {
            double inputValue = Double.parseDouble(editor_textfield.getText());
            // First update the model
            setValue(inputValue);
            // Then notify the controller a change occured
            effectPickerController.incrementRevision();
            editor_textfield.selectAll();
        }
    }

    private void incOrDecValue(double delta) {
        setValue(getValue() + delta);
//        Platform.runLater(new Runnable() {
//            @Override
//            public void run() {
//                // position caret after new value for easy editing
//                editor_textfield.positionCaret(editor_textfield.getText().length());
//            }
//        });
    }

    private void setValue(double d) {
        double val = Utils.clamp(mini, d, maxi);
        double rounded = EditorUtils.round(val, roundingFactor);
        value.set(rounded);
        editor_textfield.setText(Double.toString(rounded));
    }

    private void initialize(String labelString,
            double min, double max, double initVal, double incDec) {

        final URL layoutURL = DoubleTextFieldControl.class.getResource("NumFieldControl.fxml"); //NOI18N
        try (InputStream is = layoutURL.openStream()) {
            FXMLLoader loader = new FXMLLoader();
            loader.setController(this);
            loader.setRoot(this);
            loader.setLocation(layoutURL);
            Parent p = (Parent) loader.load(is);
            assert p == this;
        } catch (IOException x) {
            throw new RuntimeException(x);
        }

        editor_label.setText(labelString);
        incDecValue = incDec;
        mini = min;
        maxi = max;
        setValue(initVal);
    }
}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/content/driver/WindowDriver.java<|end_filename|>
package com.oracle.javafx.scenebuilder.kit.editor.panel.content.driver;

import com.oracle.javafx.scenebuilder.kit.editor.drag.target.AbstractDropTarget;
import com.oracle.javafx.scenebuilder.kit.editor.drag.target.AccessoryDropTarget;
import com.oracle.javafx.scenebuilder.kit.editor.panel.content.ContentPanelController;
import com.oracle.javafx.scenebuilder.kit.editor.panel.content.driver.curve.AbstractCurveEditor;
import com.oracle.javafx.scenebuilder.kit.editor.panel.content.driver.handles.AbstractHandles;
import com.oracle.javafx.scenebuilder.kit.editor.panel.content.driver.handles.WindowHandles;
import com.oracle.javafx.scenebuilder.kit.editor.panel.content.driver.pring.AbstractPring;
import com.oracle.javafx.scenebuilder.kit.editor.panel.content.driver.pring.NodePring;
import com.oracle.javafx.scenebuilder.kit.editor.panel.content.driver.resizer.AbstractResizer;
import com.oracle.javafx.scenebuilder.kit.editor.panel.content.driver.tring.AbstractTring;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMInstance;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMObject;
import com.oracle.javafx.scenebuilder.kit.metadata.util.DesignHierarchyMask;

import javafx.geometry.Bounds;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.stage.Window;

public class WindowDriver extends AbstractDriver {

    public WindowDriver(ContentPanelController contentPanelController) {
        super(contentPanelController);
    }

    @Override
    public AbstractHandles<?> makeHandles(FXOMObject fxomObject) {
        assert fxomObject.getSceneGraphObject() instanceof Window;
        assert fxomObject instanceof FXOMInstance;
        return new WindowHandles(contentPanelController, (FXOMInstance) fxomObject);
    }

    @Override
    public AbstractTring<?> makeTring(AbstractDropTarget dropTarget) {
        return null;
    }

    @Override
    public AbstractPring<?> makePring(FXOMObject fxomObject) {
        assert fxomObject.getSceneGraphObject() instanceof Window;
        DesignHierarchyMask windowDesignHierarchyMask = new DesignHierarchyMask(fxomObject);
        FXOMObject scene = windowDesignHierarchyMask.getAccessory(DesignHierarchyMask.Accessory.SCENE);
        assert scene != null : "makePring should have only been called if the Window has a scene";
        assert scene.getSceneGraphObject() instanceof Scene;
        assert scene instanceof FXOMInstance;
        DesignHierarchyMask sceneDesignHierarchyMask = new DesignHierarchyMask(scene);
        FXOMObject root = sceneDesignHierarchyMask.getAccessory(DesignHierarchyMask.Accessory.ROOT);
        assert root != null;
        assert root.getSceneGraphObject() instanceof Node;
        assert root instanceof FXOMInstance;
        return new NodePring(contentPanelController, (FXOMInstance) root);
    }

    @Override
    public AbstractResizer<?> makeResizer(FXOMObject fxomObject) {
        // Resize gesture does not apply to Windows
        return null;
    }

    @Override
    public AbstractCurveEditor<?> makeCurveEditor(FXOMObject fxomObject) {
        return null;
    }

    @Override
    public FXOMObject refinePick(Node hitNode, double sceneX, double sceneY, FXOMObject fxomObject) {
        return fxomObject;
    }

    @Override
    public AbstractDropTarget makeDropTarget(FXOMObject fxomObject, double sceneX, double sceneY) {
        assert fxomObject instanceof FXOMInstance;
        return new AccessoryDropTarget((FXOMInstance) fxomObject, DesignHierarchyMask.Accessory.SCENE);
    }

    @Override
    public Node getInlineEditorBounds(FXOMObject fxomObject) {
        return null;
    }

    @Override
    public boolean intersectsBounds(FXOMObject fxomObject, Bounds bounds) {
        assert fxomObject.getSceneGraphObject() instanceof Window;
        DesignHierarchyMask windowDesignHierarchyMask = new DesignHierarchyMask(fxomObject);
        FXOMObject scene = windowDesignHierarchyMask.getAccessory(DesignHierarchyMask.Accessory.SCENE);
        if (scene == null) {
            return false;
        }
        assert scene.getSceneGraphObject() instanceof Scene;
        assert scene instanceof FXOMInstance;
        DesignHierarchyMask sceneDesignHierarchyMask = new DesignHierarchyMask(scene);
        FXOMObject root = sceneDesignHierarchyMask.getAccessory(DesignHierarchyMask.Accessory.ROOT);
        assert root != null;
        assert root.getSceneGraphObject() instanceof Node;
        Node rootNode = (Node) root.getSceneGraphObject();
        Bounds rootNodeBounds = rootNode.localToScene(rootNode.getLayoutBounds(), true /* rootScene */);
        return rootNodeBounds.intersects(bounds);
    }
}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/skeleton/SkeletonContext.java<|end_filename|>
/*
 * Copyright (c) 2021, Gluon and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.kit.skeleton;

import com.oracle.javafx.scenebuilder.kit.fxom.FXOMObject;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMPropertyT;
import com.oracle.javafx.scenebuilder.kit.util.eventnames.EventNames;
import com.oracle.javafx.scenebuilder.kit.util.eventnames.FindEventNamesUtil;
import com.oracle.javafx.scenebuilder.kit.util.eventnames.ImportBuilder;
import javafx.fxml.FXML;

import java.util.Collections;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.SortedMap;
import java.util.SortedSet;
import java.util.TreeMap;
import java.util.TreeSet;

class SkeletonContext {

    private final String fxController;
    private final String documentName;
    private final SkeletonSettings settings;
    private final SortedSet<String> imports;
    private final SortedMap<String, Class<?>> variables;
    private final SortedMap<String, String> eventHandlers;
    private final SortedSet<String> assertions;

    private SkeletonContext(
        String fxController,
        String documentName,
        SkeletonSettings settings,
        SortedSet<String> imports,
        SortedMap<String, Class<?>> variables,
        SortedMap<String, String> eventHandlers,
        SortedSet<String> assertions
    ) {
        this.fxController = fxController;
        this.documentName = documentName;
        this.settings = Objects.requireNonNull(settings);
        this.imports = Collections.unmodifiableSortedSet(imports);
        this.variables = Collections.unmodifiableSortedMap(variables);
        this.eventHandlers = Collections.unmodifiableSortedMap(eventHandlers);
        this.assertions = Collections.unmodifiableSortedSet(assertions);
    }

    static Builder builder() {
        return new Builder();
    }

    String getFxController() {
        return fxController;
    }

    String getDocumentName() {
        return documentName;
    }

    SkeletonSettings getSettings() {
        return settings;
    }

    Set<String> getImports() {
        return imports;
    }

    Map<String, Class<?>> getVariables() {
        return variables;
    }

    Map<String, String> getEventHandlers() {
        return eventHandlers;
    }

    Set<String> getAssertions() {
        return assertions;
    }

    static class Builder {

        private String fxController;
        private String documentName;
        private SkeletonSettings settings;

        private final SortedSet<String> imports = new TreeSet<>();
        private final SortedMap<String, Class<?>> variables = new TreeMap<>();
        private final SortedMap<String, String> eventHandlers = new TreeMap<>();
        private final SortedSet<String> assertions = new TreeSet<>();

        Builder withFxController(String fxController) {
            this.fxController = fxController;
            return this;
        }

        Builder withDocumentName(String documentName) {
            this.documentName = documentName;
            return this;
        }

        Builder withSettings(SkeletonSettings settings) {
            this.settings = settings;
            return this;
        }

        public void addFxId(FXOMObject value) {
            String fxId = value.getFxId();
            Class<?> type = value.getSceneGraphObject().getClass();

            addImportsFor(FXML.class, type);

            variables.put(fxId, type);
            assertions.add(fxId);
        }

        public void addEventHandler(FXOMPropertyT eventHandler) {
            String eventName = FindEventNamesUtil.findEventName(eventHandler.getName().getName());

            eventHandlers.put(eventHandler.getValue(), eventName);
            addImportsForEvents(eventName);
        }

        private void addImportsForEvents(String eventName) {
            if (EventNames.ACTION_EVENT.equals(eventName)) {
                ImportBuilder.add(ImportBuilder.IMPORT_STATEMENT.concat(ImportBuilder.EVENT_PACKAGE), eventName);
            } else {
                ImportBuilder.add(ImportBuilder.IMPORT_STATEMENT.concat(ImportBuilder.INPUT_PACKAGE), eventName);
            }
            buildAndCollectImports();
        }

        /**
         * Constructs import statements for other classes (like URL, ResourceBundle).
         *
         * @param classes other classes the statement should be built.
         */
        void addImportsFor(Class<?>... classes) {
            for (Class<?> c : classes) {
                ImportBuilder.add(ImportBuilder.IMPORT_STATEMENT, c.getName().replace("$", "."));
                buildAndCollectImports();
            }
            // need an import statement for @FXML, too
            ImportBuilder.add(ImportBuilder.IMPORT_STATEMENT, ImportBuilder.FXML_PACKAGE);
            buildAndCollectImports();
        }

        private void buildAndCollectImports() {
            imports.addAll(ImportBuilder.build());
            ImportBuilder.reset();
        }

        SkeletonContext build() {
            return new SkeletonContext(fxController, documentName, settings, imports, variables, eventHandlers, assertions);
        }
    }
}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/inspector/editors/ImageEditor.java<|end_filename|>
/*
 * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.kit.editor.panel.inspector.editors;

import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Arrays;
import java.util.Set;

import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.control.Label;
import javafx.scene.control.MenuItem;
import javafx.scene.control.TextField;
import javafx.scene.image.Image;
import javafx.stage.FileChooser;

import com.oracle.javafx.scenebuilder.kit.editor.EditorController;
import com.oracle.javafx.scenebuilder.kit.i18n.I18N;
import com.oracle.javafx.scenebuilder.kit.metadata.property.ValuePropertyMetadata;
import com.oracle.javafx.scenebuilder.kit.metadata.util.DesignImage;
import com.oracle.javafx.scenebuilder.kit.metadata.util.PrefixedValue;

/**
 * Image property editor (handle the url path).
 */
public class ImageEditor extends PropertyEditor {

    @FXML
    private Label prefixLb;
    @FXML
    private TextField imagePathTf;

    private Parent root;
    private DesignImage image = null;

    private final MenuItem documentRelativeMenuItem
            = new MenuItem(I18N.getString("inspector.resource.documentrelative"));
    private final MenuItem classPathRelativeMenuItem
            = new MenuItem(I18N.getString("inspector.resource.classpathrelative"));
    private final MenuItem absoluteMenuItem
            = new MenuItem(I18N.getString("inspector.resource.absolute"));

    private PrefixedValue.Type type = PrefixedValue.Type.PLAIN_STRING;
    private URL fxmlFileLocation;

    public ImageEditor(ValuePropertyMetadata propMeta, Set<Class<?>> selectedClasses, URL fxmlFileLocation) {
        super(propMeta, selectedClasses);
        initialize(fxmlFileLocation);
    }
    
    // Separate method to please FindBugs
    private void initialize(URL fxmlFileLocation) {
        this.fxmlFileLocation = fxmlFileLocation;
        root = EditorUtils.loadFxml("ImageEditor.fxml", this); //NOI18N

        EventHandler<ActionEvent> valueListener = event -> {
            Image imageObj;
            String prefixedValue = null;
            URL url;
            try {
                String suffix = imagePathTf.getText();
                if (suffix == null || suffix.isEmpty()) {
                    image = null;
                    switchType(PrefixedValue.Type.PLAIN_STRING);
                    userUpdateValueProperty(image);
                    return;
                }
                prefixedValue = new PrefixedValue(type, suffix).toString();
                url = EditorUtils.getUrl(suffix, type, ImageEditor.this.fxmlFileLocation);
                imageObj = new Image(url != null ? url.toExternalForm() : null);
            } catch (NullPointerException | IllegalArgumentException ex) {
                // Always happen for classpath relative, or if the url cannot be resolved.
                // In this case we cannot resolve the reference, so we use a dummy image.
                imageObj = new Image(DesignImage.getVoidImageUrl().toExternalForm());
            }
            image = new DesignImage(imageObj, prefixedValue);
            userUpdateValueProperty(image);
        };
        setTextEditorBehavior(this, imagePathTf, valueListener);

        documentRelativeMenuItem.setOnAction(e -> switchType(PrefixedValue.Type.DOCUMENT_RELATIVE_PATH));
        classPathRelativeMenuItem.setOnAction(e -> switchType(PrefixedValue.Type.CLASSLOADER_RELATIVE_PATH));
        absoluteMenuItem.setOnAction(e -> switchType(PrefixedValue.Type.PLAIN_STRING));
        getMenu().getItems().addAll(documentRelativeMenuItem, classPathRelativeMenuItem, absoluteMenuItem);
        removeLabel();
        updateMenuItems();
    }

    private void switchType(PrefixedValue.Type newType) {
        String suffix = imagePathTf.getText();
        if (suffix == null || suffix.isEmpty()) {
            type = newType;
            updateMenuItems();
            handlePrefix();
            return;
        }
        // Get the current url
        URL url = EditorUtils.getUrl(suffix, type, fxmlFileLocation);
        // Switch to the new type now
        String newSuffix = null;
        if ((url == null) || (newType == PrefixedValue.Type.CLASSLOADER_RELATIVE_PATH)) {
            // In this case we empty the text field (i.e. suffix) content
            newSuffix = ""; //NOI18N
        } else if (newType == PrefixedValue.Type.PLAIN_STRING) {
            newSuffix = url.toExternalForm();
        } else if (newType == PrefixedValue.Type.DOCUMENT_RELATIVE_PATH) {
            newSuffix = PrefixedValue.makePrefixedValue(url, fxmlFileLocation).getSuffix();
        }
        assert newSuffix != null;
        imagePathTf.setText(newSuffix);
        type = newType;
        // call the text field listener
        if (!newSuffix.isEmpty()) {
            getCommitListener().handle(null);
        }
        updateMenuItems();
        handlePrefix();
    }

    private void updateMenuItems() {
        documentRelativeMenuItem.setDisable(false);
        classPathRelativeMenuItem.setDisable(false);
        absoluteMenuItem.setDisable(false);
        if (fxmlFileLocation == null) {
            documentRelativeMenuItem.setDisable(true);
        }
        if (type == PrefixedValue.Type.DOCUMENT_RELATIVE_PATH) {
            documentRelativeMenuItem.setDisable(true);
        } else if (type == PrefixedValue.Type.CLASSLOADER_RELATIVE_PATH) {
            classPathRelativeMenuItem.setDisable(true);
        } else if (type == PrefixedValue.Type.PLAIN_STRING) {
            absoluteMenuItem.setDisable(true);
        }
    }

    protected void handlePrefix() {
        if (type == PrefixedValue.Type.DOCUMENT_RELATIVE_PATH) {
            setPrefix(FXMLLoader.RELATIVE_PATH_PREFIX);
        } else if (type == PrefixedValue.Type.CLASSLOADER_RELATIVE_PATH) {
            setPrefix(FXMLLoader.RELATIVE_PATH_PREFIX + "/");//NOI18N
        } else {
            // absolute
            removeLabel();
        }
    }

    private void setPrefix(String str) {
        if (!prefixLb.isVisible()) {
            prefixLb.setVisible(true);
            prefixLb.setManaged(true);
        }
        prefixLb.setText(str);
    }

    private void removeLabel() {
        prefixLb.setVisible(false);
        prefixLb.setManaged(false);
    }

    @Override
    public Node getValueEditor() {
        return super.handleGenericModes(root);
    }

    @Override
    public Object getValue() {
        return image;
    }

    @Override
    public void setValue(Object value) {
        setValueGeneric(value);
        if (isSetValueDone()) {
            return;
        }

        if (value == null) {
            image = null;
            imagePathTf.setText(""); //NOI18N
        } else {
            assert value instanceof DesignImage;
            image = (DesignImage) value;
            PrefixedValue prefixedValue = new PrefixedValue(image.getLocation());
            imagePathTf.setText(prefixedValue.getSuffix());
            type = prefixedValue.getType();
            handlePrefix();
            updateMenuItems();
        }
    }

    public void reset(ValuePropertyMetadata propMeta, Set<Class<?>> selectedClasses, URL fxmlFileLocation) {
        super.reset(propMeta, selectedClasses);
        this.fxmlFileLocation = fxmlFileLocation;
        imagePathTf.setPromptText(null);
    }

    @Override
    protected void valueIsIndeterminate() {
        handleIndeterminate(imagePathTf);
    }

    //
    // FXML methods
    //
    @FXML
    void chooseImage(ActionEvent event) {
        String[] extensions = {"*.jpg", "*.jpeg", "*.png", "*.gif"}; //NOI18N
        FileChooser fileChooser = new FileChooser();
        fileChooser.setTitle(I18N.getString("inspector.select.image"));
        fileChooser.getExtensionFilters().add(
                new FileChooser.ExtensionFilter(
                        I18N.getString("inspector.select.image"),
                        Arrays.asList(extensions)));
        fileChooser.setInitialDirectory(EditorController.getNextInitialDirectory());
        File file = fileChooser.showOpenDialog(imagePathTf.getScene().getWindow());
        if ((file == null)) {
            return;
        }
        // Keep track of the user choice for next time
        EditorController.updateNextInitialDirectory(file);
        URL url;
        try {
            url = file.toURI().toURL();
        } catch (MalformedURLException ex) {
            throw new RuntimeException("Invalid URL", ex); //NOI18N
        }

        // If the document exists, make the type as document relative by default.
        String urlStr;
        if (fxmlFileLocation != null) {
            urlStr = PrefixedValue.makePrefixedValue(url, fxmlFileLocation).toString();
            switchType(PrefixedValue.Type.DOCUMENT_RELATIVE_PATH);
        } else {
            urlStr = url.toExternalForm();
            switchType(PrefixedValue.Type.PLAIN_STRING);
        }
        PrefixedValue prefixedValue = new PrefixedValue(urlStr);
        type = prefixedValue.getType();
        String suffix = prefixedValue.getSuffix();
        imagePathTf.setText(suffix);
        image = new DesignImage(new Image(EditorUtils.getUrl(suffix, type, fxmlFileLocation).toExternalForm()), prefixedValue.toString());
        userUpdateValueProperty(getValue());
        updateMenuItems();
        handlePrefix();
    }

    @Override
    public void requestFocus() {
        EditorUtils.doNextFrame(() -> imagePathTf.requestFocus());
    }
}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/template/TemplatesBaseWindowController.java<|end_filename|>
/*
 * Copyright (c) 2017, Gluon and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation and Gluon nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */

package com.oracle.javafx.scenebuilder.kit.template;

import com.oracle.javafx.scenebuilder.kit.editor.panel.util.AbstractFxmlWindowController;
import javafx.fxml.FXML;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.control.Button;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.stage.Window;
import javafx.stage.WindowEvent;
import java.net.URL;
import java.util.ResourceBundle;
import java.util.function.Consumer;

public abstract class TemplatesBaseWindowController extends AbstractFxmlWindowController {

    @FXML
    protected Button basicDesktopApp;
    @FXML
    protected Button complexDesktopApp;
    @FXML
    protected Button emptyPhoneApp;
    @FXML
    protected Button basicPhoneApp;

    @FXML
    protected VBox templateContainer;

    private Consumer<Template> onTemplateChosen;

    public TemplatesBaseWindowController(URL fxmlURL, ResourceBundle resources, Stage owner) {
        super(fxmlURL, resources, owner);
    }


    @Override
    public void onCloseRequest(WindowEvent event) {
        getStage().hide();
    }

    /*
     * AbstractWindowController
     */
    @Override
    protected void controllerDidCreateStage() {
        assert getRoot() != null;
        assert getRoot().getScene() != null;
        assert getRoot().getScene().getWindow() != null;
    }

    @Override
    protected void controllerDidLoadFxml() {
        super.controllerDidLoadFxml();
        assert templateContainer != null;

        basicDesktopApp.setUserData(Template.BASIC_DESKTOP_APP);
        complexDesktopApp.setUserData(Template.COMPLEX_DESKTOP_APP);
        emptyPhoneApp.setUserData(Template.EMPTY_PHONE_APP);
        basicPhoneApp.setUserData(Template.BASIC_PHONE_APP);
    }

    protected void setupTemplateButtonHandlers() {
        setupTemplateButtonHandlers(templateContainer);
    }

    private void setupTemplateButtonHandlers(Parent templateContainer) {
        for (Node child : templateContainer.getChildrenUnmodifiable()) {
            if (!(child instanceof Button) && child instanceof Parent){
                setupTemplateButtonHandlers((Parent)child);
            }
            if (child instanceof Button) {
                Button button = (Button) child;
                button.setOnAction(event -> {
                    getStage().hide();
                    onTemplateChosen.accept((Template)button.getUserData());
                });
            }
        }
    }

    public void setOnTemplateChosen(Consumer<Template> onTemplateChosen) {
        this.onTemplateChosen = onTemplateChosen;
    }
}



<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/content/mode/EditModeController.java<|end_filename|>
/*
 * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.kit.editor.panel.content.mode;

import com.oracle.javafx.scenebuilder.kit.editor.EditorController;
import com.oracle.javafx.scenebuilder.kit.editor.drag.DragController;
import com.oracle.javafx.scenebuilder.kit.editor.drag.target.AbstractDropTarget;
import com.oracle.javafx.scenebuilder.kit.editor.drag.target.GridPaneDropTarget;
import com.oracle.javafx.scenebuilder.kit.editor.drag.target.RootDropTarget;
import com.oracle.javafx.scenebuilder.kit.editor.job.atomic.ModifyObjectJob;
import com.oracle.javafx.scenebuilder.kit.editor.job.RelocateSelectionJob;
import com.oracle.javafx.scenebuilder.kit.editor.messagelog.MessageLog;
import com.oracle.javafx.scenebuilder.kit.editor.panel.content.AbstractDecoration;
import com.oracle.javafx.scenebuilder.kit.editor.panel.content.ContentPanelController;
import com.oracle.javafx.scenebuilder.kit.editor.panel.content.driver.AbstractDriver;
import com.oracle.javafx.scenebuilder.kit.editor.panel.content.driver.gridpane.GridPaneHandles;
import com.oracle.javafx.scenebuilder.kit.editor.panel.content.driver.gridpane.GridPaneTring;
import com.oracle.javafx.scenebuilder.kit.editor.panel.content.driver.handles.AbstractHandles;
import com.oracle.javafx.scenebuilder.kit.editor.panel.content.driver.pring.AbstractPring;
import com.oracle.javafx.scenebuilder.kit.editor.panel.content.driver.tring.AbstractTring;
import com.oracle.javafx.scenebuilder.kit.editor.panel.content.gesture.AbstractGesture;
import com.oracle.javafx.scenebuilder.kit.editor.panel.content.gesture.DragGesture;
import com.oracle.javafx.scenebuilder.kit.editor.panel.content.gesture.mouse.SelectAndMoveGesture;
import com.oracle.javafx.scenebuilder.kit.editor.panel.content.gesture.mouse.SelectWithMarqueeGesture;
import com.oracle.javafx.scenebuilder.kit.editor.panel.content.gesture.ZoomGesture;
import com.oracle.javafx.scenebuilder.kit.editor.panel.content.gesture.key.MoveWithKeyGesture;
import com.oracle.javafx.scenebuilder.kit.editor.util.InlineEditController;
import com.oracle.javafx.scenebuilder.kit.editor.selection.GridSelectionGroup;
import com.oracle.javafx.scenebuilder.kit.editor.selection.ObjectSelectionGroup;
import com.oracle.javafx.scenebuilder.kit.editor.selection.Selection;
import com.oracle.javafx.scenebuilder.kit.editor.util.ContextMenuController;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMDocument;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMInstance;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMObject;
import com.oracle.javafx.scenebuilder.kit.metadata.Metadata;
import com.oracle.javafx.scenebuilder.kit.metadata.property.ValuePropertyMetadata;
import com.oracle.javafx.scenebuilder.kit.metadata.util.DesignHierarchyMask;
import com.oracle.javafx.scenebuilder.kit.metadata.util.PropertyName;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javafx.collections.ObservableList;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextInputControl;
import javafx.scene.input.DragEvent;
import javafx.scene.input.InputEvent;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import javafx.scene.input.ZoomEvent;
import javafx.scene.layout.GridPane;
import javafx.util.Callback;

/**
 *
 * 
 */

/**
 *
 * 
 */
public class EditModeController extends AbstractModeController
implements AbstractGesture.Observer {
    
    private final List<AbstractHandles<?>> handles = new ArrayList<>();
    private final Set<FXOMObject> excludes = new HashSet<>();
    private final SelectWithMarqueeGesture selectWithMarqueeGesture;
    private final SelectAndMoveGesture selectAndMoveGesture;
    private final ZoomGesture zoomGesture;
    
    private AbstractPring<?> pring;
    private AbstractTring<?> tring;
    private AbstractGesture activeGesture;
    private AbstractGesture glassGesture;
    private FXOMInstance inlineEditedObject;
    
    
    public EditModeController(ContentPanelController contentPanelController) {
        super(contentPanelController);
        selectWithMarqueeGesture = new SelectWithMarqueeGesture(contentPanelController);
        selectAndMoveGesture = new SelectAndMoveGesture(contentPanelController);
        zoomGesture = new ZoomGesture(contentPanelController);
    }
    
    
    /**
     * Returns null or the handles associated to the specified fxom object.
     * 
     * @param fxomObject an fxom object (never null)
     * @return null or the handles associated to the specified fxom object.
     */

    public AbstractHandles<?> lookupHandles(FXOMObject fxomObject) {
        assert fxomObject != null;
        
        AbstractHandles<?> result = null;
        for (AbstractHandles<?> h : handles) {
            if (h.getFxomObject() == fxomObject) {
                result = h;
                break;
            }
        }
        
        return result;
    }
    
    /*
     * AbstractGesture.Observer
     */
    
    @Override
    public void gestureDidTerminate(AbstractGesture gesture) {
        assert activeGesture == gesture;
        activeGesture = null;
        startListeningToInputEvents();
        contentPanelController.endInteraction();
        
        // Object below the mouse may have changed : current glass gesture
        // must be searched again.
        this.glassGesture = null;
    }
    
    /*
     * AbstractModeController
     */
    
    @Override
    public void willResignActive(AbstractModeController nextModeController) {
        stopListeningToInputEvents();        
        
        removeAllHandles();
        removePring();
        removeTring();
        
        assert contentPanelController.getHandleLayer().getChildren().isEmpty();
        assert contentPanelController.getPringLayer().getChildren().isEmpty();
        assert contentPanelController.getRudderLayer().getChildren().isEmpty();
    }

    @Override
    public void didBecomeActive(AbstractModeController previousModeController) {
        assert contentPanelController.getGlassLayer() != null;
        assert contentPanelController.getHandleLayer() != null;
        assert contentPanelController.getPringLayer() != null;
        assert contentPanelController.getRudderLayer() != null;
        
        editorSelectionDidChange();
        startListeningToInputEvents();
    }
    
    @Override
    public void editorSelectionDidChange() {
        updateParentRing();
        updateHandles();
        makeSelectionVisible();
    }

    @Override
    public void fxomDocumentDidChange(FXOMDocument oldDocument) {
        // Same logic as when the scene graph is changed
        fxomDocumentDidRefreshSceneGraph();
    }

    @Override
    public void fxomDocumentDidRefreshSceneGraph() {
        updateParentRing();
        updateHandles();
        
        // Object below the mouse may have changed : current glass gesture
        // must searched again.
        this.glassGesture = null;
    }

    @Override
    public void dropTargetDidChange() {
        updateTring();
    }

    /*
     * Private
     */
    
    private void makeSelectionVisible() {
        
        // Scrolls the content panel so that selected objects are visible.
        contentPanelController.scrollToSelection();
        
        // Walks trough the ancestor nodes of the first selected object and
        // makes sure that TabPane and Accordion are setup for displaying 
        // this selected object.
        if (handles.isEmpty() == false) {
            contentPanelController.reveal(handles.get(0).getFxomObject());
        }
    }
    
    /*
     * Private (pring)
     */
    private void updateParentRing() {
        final AbstractPring<?> newPring;
        
        if (contentPanelController.isContentDisplayable()) {
            final Selection selection 
                    = contentPanelController.getEditorController().getSelection();
            if ((pring == null) || (pring.getFxomObject() != selection.getAncestor())) {
                if (selection.getAncestor() != null) {
                    newPring = makePring(selection.getAncestor());
                } else {
                    newPring = null;
                }
            } else {
                switch(pring.getState()) {
                    default:
                    case CLEAN:
                        newPring = pring;
                        break;
                    case NEEDS_RECONCILE:
                        newPring = pring;
                        pring.reconcile();;
                        break;
                    case NEEDS_REPLACE:
                        newPring = makePring(pring.getFxomObject());
                        break;
                }
            }
        } else {
            // Document content cannot be displayed in content panel
            newPring = null;
        }
        
        if (newPring != pring) {
            final Group pringLayer = contentPanelController.getPringLayer();
            if (pring != null) {
                pringLayer.getChildren().remove(pring.getRootNode());
            }
            pring = newPring;
            if (pring != null) {
                pringLayer.getChildren().add(pring.getRootNode());
            }
        } else {
            assert (pring == null) || pring.getState() == AbstractPring.State.CLEAN;
        }
    }
    
    private AbstractPring<?> makePring(FXOMObject fxomObject) {
        final AbstractDriver driver = contentPanelController.lookupDriver(fxomObject);
        final AbstractPring<?> result;
        
        if (driver != null) {
            result = driver.makePring(fxomObject);
            if (result != null) {
                result.changeStroke(contentPanelController.getPringColor());
            }
        } else {
            result = null;
        }
        
        return result;
    }
    
    
    private void removePring() {
        if (pring != null) {
            final Group pringLayer = contentPanelController.getPringLayer();
            pringLayer.getChildren().remove(pring.getRootNode());
            pring = null;
        }
    }

    /*
     * Private (tring)
     */
    private void updateTring() {
        final DragController dragController
                = contentPanelController.getEditorController().getDragController();
        final AbstractTring<?> newTring;
        
        if (dragController.isDropAccepted()
                && contentPanelController.isContentDisplayable()) {
            final AbstractDropTarget dropTarget = dragController.getDropTarget();
            if ((tring instanceof GridPaneTring) && (dropTarget instanceof GridPaneDropTarget)) {
                // Let's reuse the GridPaneTring (because it's costly)
                newTring = tring;
                updateTring((GridPaneTring) tring, (GridPaneDropTarget) dropTarget);
            } else {
                newTring = makeTring(dragController.getDropTarget());
            }
        } else {
            newTring = null;
        }
        
        if (newTring != tring) {
            final Group rudderLayer = contentPanelController.getRudderLayer();
            if (tring != null) {
                rudderLayer.getChildren().remove(tring.getRootNode());
            }
            tring = newTring;
            if (tring != null) {
                rudderLayer.getChildren().add(tring.getRootNode());
            }
        } else {
            assert (tring == null) || tring.getState() == AbstractPring.State.CLEAN;
        }
    }
    
    private void updateTring(GridPaneTring tring, GridPaneDropTarget dropTarget) {
        assert tring != null;
        assert dropTarget != null;
        
        tring.setupWithDropTarget(dropTarget);
    }
    
    private AbstractTring<?> makeTring(AbstractDropTarget dropTarget) {
        final AbstractTring<?> result;
        
        if (dropTarget.getTargetObject() == null) {
            assert dropTarget instanceof RootDropTarget;
            result = null;
        } else {
            final AbstractDriver driver 
                    = contentPanelController.lookupDriver(dropTarget.getTargetObject());
            if (driver != null) {
                result = driver.makeTring(dropTarget);
                if (result != null) {
                    result.changeStroke(contentPanelController.getPringColor());
                }
            } else {
                result = null;
            }
        }
        
        return result;
    }
    
    private void removeTring() {
        if (tring != null) {
            final Group rudderLayer = contentPanelController.getRudderLayer();
            rudderLayer.getChildren().remove(tring.getRootNode());
            tring = null;
        }
    }
    
    /*
     * Private (handles)
     */

    private void updateHandles() {
        final Selection selection = contentPanelController.getEditorController().getSelection();
        if (selection.getGroup() instanceof ObjectSelectionGroup) {
            updateHandles((ObjectSelectionGroup) selection.getGroup());
        } else if (selection.getGroup() instanceof GridSelectionGroup) {
            updateHandles((GridSelectionGroup) selection.getGroup());
        } else {
            assert selection.getGroup() == null 
                    : "Implement updateHandles() for " + selection.getGroup();
            // Selection is empty : removes all handles
            removeAllHandles();
        }
        
        final boolean enabled = handles.size() == 1;
        for (AbstractHandles<?> h : handles) {
            h.setEnabled(enabled);
        }
    }
    
    private void updateHandles(ObjectSelectionGroup osg) {
        final List<AbstractHandles<?>> obsoleteHandles = new ArrayList<>();
        final List<FXOMObject> incomingObjects = new ArrayList<>();
        
        // Collects fxom objects from selection
        if (contentPanelController.isContentDisplayable()) {
            incomingObjects.addAll(osg.getItems());
        }
        
        // Collects obsolete handles
        for (AbstractHandles<?> h : handles) {
            if (incomingObjects.contains(h.getFxomObject())) {
                // FXOM object associated to these handles is still selected
                switch(h.getState()) {
                    case CLEAN:
                        incomingObjects.remove(h.getFxomObject());
                        break;
                    case NEEDS_RECONCILE:
                        // scene graph associated to h has changed but h is still compatible
                        h.reconcile();
                        incomingObjects.remove(h.getFxomObject());
                        break;
                    case NEEDS_REPLACE:
                        // h is no longer compatible with the new scene graph object 
                        obsoleteHandles.add(h);
                        break;
                }
                // If h is grid pane handles reset the selected columns/rows
                if (h instanceof GridPaneHandles) {
                    final GridPaneHandles gph = (GridPaneHandles) h;
                    gph.updateColumnRowSelection(null);
                }
            } else {
                // FXOM object associated to these handles is no longer selected
                // => handles become obsolete
                obsoleteHandles.add(h);
            }
        }
        
        // Let's create new handles for the incoming objects
        excludes.clear();
        final Group handleLayer = contentPanelController.getHandleLayer();
        for (FXOMObject incomingObject : incomingObjects) {
            final AbstractDriver driver = contentPanelController.lookupDriver(incomingObject);
            if (driver == null) {
                // incomingObject cannot be managed by content panel (eg MenuItem)
                excludes.add(incomingObject);
            } else {
                final AbstractHandles<?> newHandles = driver.makeHandles(incomingObject);
                handleLayer.getChildren().add(newHandles.getRootNode());
                handles.add(newHandles);
            }
        }
        
        // Let's disconnect the obsolete handles
        for (AbstractHandles<?> h : obsoleteHandles) {
            handleLayer.getChildren().remove(h.getRootNode());
            handles.remove(h);
        }
    }
    
    
    private void updateHandles(GridSelectionGroup gsg) {
        final List<AbstractHandles<?>> obsoleteHandles = new ArrayList<>();
        
        // Collects obsolete handles
        if (contentPanelController.isContentDisplayable()) {
            for (AbstractHandles<?> h : handles) {
                if (h.getFxomObject() == gsg.getParentObject()) {
                    assert h instanceof GridPaneHandles;

                    if (h.getState() == AbstractDecoration.State.NEEDS_RECONCILE) {
                        // scene graph associated to h has changed but h is still compatible
                        h.reconcile();
                    } else {
                        assert h.getState() == AbstractDecoration.State.CLEAN;
                    }

                    final GridPaneHandles gph = (GridPaneHandles) h;
                    gph.updateColumnRowSelection(gsg);
                } else {
                    // FXOM object associated to these handles is no longer selected
                    // => handles become obsolete
                    obsoleteHandles.add(h);
                }
            }
        } else {
            // Document content is not displayed (because its root is not a node)
            // => all handles are obsoletes
            obsoleteHandles.addAll(handles);
        }
        
        // Let's create new handles for the incoming objects
        excludes.clear();
        final Group handleLayer = contentPanelController.getHandleLayer();
        if (handles.size() == obsoleteHandles.size()) {
            // No handles for grid pane row/column selection : creates one.
            assert gsg.getParentObject().getSceneGraphObject() instanceof GridPane;
            final AbstractDriver driver = contentPanelController.lookupDriver(gsg.getParentObject());
            assert driver != null;
            final AbstractHandles<?> newHandles = driver.makeHandles(gsg.getParentObject());
            handleLayer.getChildren().add(newHandles.getRootNode());
            handles.add(newHandles);
            assert newHandles instanceof GridPaneHandles;
            final GridPaneHandles gridPaneHandles = (GridPaneHandles) newHandles;
            gridPaneHandles.updateColumnRowSelection(gsg);
        }
        
        // Let's disconnect the obsolete handles
        for (AbstractHandles<?> h : obsoleteHandles) {
            handleLayer.getChildren().remove(h.getRootNode());
            handles.remove(h);
        }
    }
    
    private void removeAllHandles() {
        final Group handleLayer = contentPanelController.getHandleLayer();
        for (AbstractHandles<?> h : new ArrayList<>(handles)) {
            handleLayer.getChildren().remove(h.getRootNode());
            handles.remove(h);
        }
    }
    
    /*
     * Private (event listeners)
     */

    private final EventHandler<MouseEvent> mouseEnteredGlassLayerListener
            = e -> mouseEnteredGlassLayer(e);
    
    private final EventHandler<MouseEvent> mouseExitedGlassLayerListener
            = e -> mouseExitedGlassLayer(e);
    
    private final EventHandler<MouseEvent> mouseMovedOnGlassLayerListener
            = e -> mouseMovedOnGlassLayer(e);
    
    private final EventHandler<MouseEvent> mousePressedOnGlassLayerListener
            = e -> mousePressedOnGlassLayer(e);
    
    private final EventHandler<KeyEvent> keyPressedOnGlassLayerListener
            = e -> keyPressedOnGlassLayer(e);
    
    private final EventHandler<ZoomEvent> zoomStartedOnGlassLayer
            = e -> zoomStartedOnGlassLayer(e);
    
    private final EventHandler<DragEvent> dragEnteredGlassLayerListener
            = e -> dragEnteredGlassLayer(e);

    private final EventHandler<MouseEvent> mousePressedOnHandleLayerListener
            = e -> mousePressedOnHandleLayer(e);

    private final EventHandler<MouseEvent> mousePressedOnPringLayerListener
            = e -> mousePressedOnPringLayer(e);

    private void startListeningToInputEvents() {
        final Node glassLayer = contentPanelController.getGlassLayer();
        assert glassLayer.getOnMouseEntered() == null;
        assert glassLayer.getOnMouseExited() == null;
        assert glassLayer.getOnMouseMoved() == null;
        assert glassLayer.getOnMousePressed() == null;
        assert glassLayer.getOnKeyPressed() == null;
        assert glassLayer.getOnZoomStarted() == null;
        assert glassLayer.getOnDragEntered() == null;
        
        glassLayer.setOnMouseEntered(mouseEnteredGlassLayerListener);
        glassLayer.setOnMouseExited(mouseExitedGlassLayerListener);
        glassLayer.setOnMouseMoved(mouseMovedOnGlassLayerListener);
        glassLayer.setOnMousePressed(mousePressedOnGlassLayerListener);
        glassLayer.setOnKeyPressed(keyPressedOnGlassLayerListener);
        glassLayer.setOnZoomStarted(zoomStartedOnGlassLayer);
        glassLayer.setOnDragEntered(dragEnteredGlassLayerListener);
        
        final Node handleLayer = contentPanelController.getHandleLayer();
        assert handleLayer.getOnMousePressed() == null;
        handleLayer.setOnMousePressed(mousePressedOnHandleLayerListener);
        
        final Node pringLayer = contentPanelController.getPringLayer();
        assert pringLayer.getOnMousePressed() == null;
        pringLayer.setOnMousePressed(mousePressedOnPringLayerListener);
    }
    
    private void stopListeningToInputEvents() {
        
        final Node glassLayer = contentPanelController.getGlassLayer();
        glassLayer.setOnMouseEntered(null);
        glassLayer.setOnMouseExited(null);
        glassLayer.setOnMouseMoved(null);
        glassLayer.setOnMousePressed(null);
        glassLayer.setOnKeyPressed(null);
        glassLayer.setOnZoomStarted(null);
        glassLayer.setOnDragEntered(null);
        
        final Node handleLayer = contentPanelController.getHandleLayer();
        handleLayer.setOnMousePressed(null);
        
        final Node pringLayer = contentPanelController.getPringLayer();
        pringLayer.setOnMousePressed(null);
    }
    
    
    /*
     * Private (event handlers)
     */

    private void mouseEnteredGlassLayer(MouseEvent e) {
        mouseMovedOnGlassLayer(e);
    }
    
    private void mouseExitedGlassLayer(MouseEvent e) {
        assert activeGesture == null : "activeGesture=" + activeGesture;
        glassGesture = null;
    }
    
    private void mouseMovedOnGlassLayer(MouseEvent e) {
        assert activeGesture == null : "activeGesture=" + activeGesture;
        
        /*
         *   1) hitObject == null
         *                  => mouse is over the workspace/background
         *                  => mouse press+drag should "select with marquee"
         *
         *   2) hitObject != null
         *
         *      2.1) hitObject == root object
         * 
         *          2.1) hitObject is the selectionAncestor
         *                  => mouse is over the "parent ring object"
         *                  => mouse press+drag should "select with marquee"
         *
         *          2.2) hitObject is not the selectionAncestor
         *                  => mouse is over an object
         *                  => this object is inside or outside of the parent ring
         *                  => mouse press+drag should "select and move"
         * 
         */
        
        final FXOMObject hitObject 
                = contentPanelController.pick(e.getSceneX(), e.getSceneY());
        final FXOMObject selectionAncestor
                = contentPanelController.getEditorController().getSelection().getAncestor();
        if (hitObject == null) {
            // Case #1
            selectWithMarqueeGesture.setup(null, selectionAncestor);
            glassGesture = selectWithMarqueeGesture;
        } else if (hitObject == selectionAncestor) {
            // Case #2.1
            selectWithMarqueeGesture.setup(selectionAncestor, selectionAncestor);
            glassGesture = selectWithMarqueeGesture;
        } else { 
            // Case #2.2
            selectAndMoveGesture.setHitObject(hitObject);
            selectAndMoveGesture.setHitSceneX(e.getSceneX());
            selectAndMoveGesture.setHitSceneY(e.getSceneY());
            glassGesture = selectAndMoveGesture;
        }
    }
    
    private void mousePressedOnGlassLayer(MouseEvent e) {

        // Make sure that glass layer has keyboard focus
        contentPanelController.getGlassLayer().requestFocus();

        /*
         * At that point, is expected that a "mouse entered" or "mouse moved" 
         * event was received before and that this.glassGesture is setup.
         * 
         * However this is no always the case. It may be null in two cases:
         * 1) on Linux, mouse entered/moved events are not always delivered
         *    before mouse pressed event (see DTL-5956).
         * 2) while the mouse is immobile, fxomDocumentDidRefreshSceneGraph()
         *    method may have been invoked and reset this.glassGesture.
         * 
         * That is why we test this.glassGesture and manually invoke
         * mouseMovedOnGlassLayer() here.
         */
        if (glassGesture == null) {
            mouseMovedOnGlassLayer(e);
        }
        
        assert glassGesture != null;
        switch(e.getClickCount()) {
            case 1:
                if (e.getButton() == MouseButton.SECONDARY) {
                    // Update the selection (see spec detailed in DTL-5640)
                    final FXOMObject hitObject;
                    if (glassGesture == selectAndMoveGesture) {
                        hitObject = selectAndMoveGesture.getHitObject();
                    } else {
                        assert glassGesture == selectWithMarqueeGesture;
                        hitObject = selectWithMarqueeGesture.getHitObject();
                    }
                    final Selection selection
                            = contentPanelController.getEditorController().getSelection();
                    if (hitObject != null && selection.isSelected(hitObject) == false) {
                        selection.select(hitObject);
                    }
                    final ContextMenuController contextMenuController
                            = contentPanelController.getEditorController().getContextMenuController();
                    // The context menu items depend on the selection so
                    // we need to rebuild it each time it is invoked.
                    contextMenuController.updateContextMenuItems();
                } else {
                    activateGesture(glassGesture, e);
                }
                break;
            case 2:
                mouseDoubleClickedOnGlassLayer(e);
                break;
            default:
                // We ignore triple clicks and upper...
                break;
        }
        e.consume();
    }
    
    private void mouseDoubleClickedOnGlassLayer(MouseEvent e) {
        assert activeGesture == null;
        assert (glassGesture == selectAndMoveGesture) 
                || (glassGesture == selectWithMarqueeGesture);
        
        if (glassGesture == selectAndMoveGesture) {
            assert selectAndMoveGesture.getHitObject() instanceof FXOMInstance;
            final FXOMInstance hitObject
                    = (FXOMInstance) selectAndMoveGesture.getHitObject();
            final DesignHierarchyMask m
                    = new DesignHierarchyMask(hitObject);
            // Do not allow inline editing of the I18N value
            if (m.isResourceKey() == false) {
                handleInlineEditing((FXOMInstance) selectAndMoveGesture.getHitObject());
            } else {
                final MessageLog ml = contentPanelController.getEditorController().getMessageLog();
                ml.logWarningMessage("log.warning.inline.edit.internationalized.strings");
            }
        }
    }
    
    private void handleInlineEditing(FXOMInstance hitObject) {

        assert hitObject != null;
        assert inlineEditedObject == null;
        
        final AbstractDriver driver
                = contentPanelController.lookupDriver(hitObject);
        final Node inlineEditingBounds 
                = driver.getInlineEditorBounds(hitObject);
        
        if (inlineEditingBounds != null) {
            inlineEditedObject = hitObject;
            
            final InlineEditController inlineEditController = 
                    contentPanelController.getEditorController().getInlineEditController();
            final DesignHierarchyMask m
                    = new DesignHierarchyMask(inlineEditedObject);
            final String text = m.getDescription();
            final InlineEditController.Type type;
            if (inlineEditingBounds instanceof TextArea
                    || DesignHierarchyMask.containsLineFeed(text)) {
                type = InlineEditController.Type.TEXT_AREA;
            } else {
                type = InlineEditController.Type.TEXT_FIELD;
            }
            final TextInputControl inlineEditor
                    = inlineEditController.createTextInputControl(
                            type, inlineEditingBounds, text);
            // CSS
            final ObservableList<String> styleSheets
                    = getContentPanelController().getPanelRoot().getStylesheets();
            inlineEditor.getStylesheets().addAll(styleSheets);
            inlineEditor.getStyleClass().add("theme-presets"); //NOI18N
            inlineEditor.getStyleClass().add(InlineEditController.INLINE_EDITOR);
            final Callback<String, Boolean> requestCommit
                    = value -> inlineEditingDidRequestCommit(value);
            final Callback<Void, Boolean> requestRevert
                    = value -> {
                inlineEditingDidRequestRevert();
                return true;
            };
            inlineEditController.startEditingSession(inlineEditor,
                    inlineEditingBounds, requestCommit, requestRevert);
        } else {
            System.out.println("Beep");
        }
        
        assert contentPanelController.getEditorController().isTextEditingSessionOnGoing() 
                || (inlineEditedObject == null);
    }
    
    
    private boolean inlineEditingDidRequestCommit(String newValue) {
        assert inlineEditedObject != null;
        
        final DesignHierarchyMask m 
                = new DesignHierarchyMask(inlineEditedObject);
        final PropertyName propertyName
                = m.getPropertyNameForDescription();
        assert propertyName != null;
        final ValuePropertyMetadata vpm
                = Metadata.getMetadata().queryValueProperty(inlineEditedObject, propertyName);
        final EditorController editorController
                = contentPanelController.getEditorController();
        final ModifyObjectJob job
                = new ModifyObjectJob(inlineEditedObject, vpm, newValue, editorController);
        
        if (job.isExecutable()) {
            editorController.getJobManager().push(job);
        }
        
        inlineEditedObject = null;
        
        return true;
    }
    
    
    private void inlineEditingDidRequestRevert() {
        assert inlineEditedObject != null;
        inlineEditedObject = null;
    }
    
    private void keyPressedOnGlassLayer(KeyEvent e) {
        assert activeGesture == null : "activeGesture=" + activeGesture;
        switch(e.getCode()) {
            case UP:
            case DOWN:
            case LEFT:
            case RIGHT:
                if (RelocateSelectionJob.isSelectionMovable(contentPanelController.getEditorController())) {
                    activateGesture(new MoveWithKeyGesture(contentPanelController), e);
                } else {
                    System.out.println("Selection is not movable");
                }
                e.consume();
                break;
            case ENTER:
                final Selection selection = contentPanelController.getEditorController().getSelection();
                if (selection.getGroup() instanceof ObjectSelectionGroup) {
                    final ObjectSelectionGroup osg = (ObjectSelectionGroup) selection.getGroup();
                    if (osg.getItems().size() == 1) {
                        final DesignHierarchyMask mask = new DesignHierarchyMask(osg.getSortedItems().get(0));
                        final FXOMObject nodeFxomObject = mask.getClosestFxNode();
                        if (nodeFxomObject instanceof FXOMInstance) {
                            handleInlineEditing((FXOMInstance)nodeFxomObject);
                        }
                    }
                }
                break;
            default:
                // We let other key events flow up in the scene graph
                break;
        }
    }
    
    private void zoomStartedOnGlassLayer(ZoomEvent e) {
        activateGesture(zoomGesture, e);
        e.consume();
    }
    
    private void dragEnteredGlassLayer(DragEvent e) {
        activateGesture(new DragGesture(contentPanelController), e);
    }

    
    private void mousePressedOnHandleLayer(MouseEvent e) {
        assert e.getTarget() instanceof Node;
        
        if (e.getButton() == MouseButton.SECONDARY) {
            final ContextMenuController contextMenuController
                    = contentPanelController.getEditorController().getContextMenuController();
            // The context menu items depend on the selection so
            // we need to rebuild it each time it is invoked.
            contextMenuController.updateContextMenuItems();
        } else {
            final Node target = (Node) e.getTarget();
            Node hitNode = target;
            AbstractHandles<?> hitHandles = AbstractHandles.lookupHandles(hitNode);
            while ((hitHandles == null) && (hitNode.getParent() != null)) {
                hitNode = hitNode.getParent();
                hitHandles = AbstractHandles.lookupHandles(hitNode);
            }

            if (hitHandles != null) {
                activateGesture(hitHandles.findEnabledGesture(hitNode), e);
            } else {
                // Emergency code
                assert false : "event target has no HANDLES property :" + target;
            }
        }
        e.consume();
    }
    
    private void mousePressedOnPringLayer(MouseEvent e) {
        assert e.getTarget() instanceof Node;
        
        final Node target = (Node) e.getTarget();
        Node hitNode = target;
        AbstractPring<?> hitPring = AbstractPring.lookupPring(target);
        while ((hitPring == null) && (hitNode.getParent() != null)) {
            hitNode = hitNode.getParent();
            hitPring = AbstractPring.lookupPring(hitNode);
        }
        
        if (hitPring != null) {
            activateGesture(hitPring.findGesture(hitNode), e);
        } else {
            // Emergency code
            assert false : "event target has no PRING property :" + target;
        }
        e.consume();
    }
    
    private void activateGesture(AbstractGesture gesture, InputEvent e) {
        assert activeGesture == null : "activeGesture=" + activeGesture;
        if (gesture == null) {
            return;
        }
        /*
         * Before activating the gesture, we check:
         *   - that there is a document attached to the editor controller
         *   - if a text session is on-going and can be completed cleanly. 
         * If not, we do not activate the gesture.
         */
        final EditorController editorController
                = contentPanelController.getEditorController();
        if (contentPanelController.isContentDisplayable() && editorController.canGetFxmlText()) {
            
            contentPanelController.beginInteraction();
            
            stopListeningToInputEvents();
            activeGesture = gesture;
            gesture.start(e, this);

            // Note that some gestures may terminates immediately.
            // So activeGesture may have switch back to null.
            assert (activeGesture == gesture) || (activeGesture == null);
        }
    }
    
}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/content/driver/GridPaneDriver.java<|end_filename|>
/*
 * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.kit.editor.panel.content.driver;

import com.oracle.javafx.scenebuilder.kit.editor.drag.target.AbstractDropTarget;
import com.oracle.javafx.scenebuilder.kit.editor.drag.target.GridPaneDropTarget;
import com.oracle.javafx.scenebuilder.kit.editor.drag.target.GridPaneDropTarget.ColumnArea;
import com.oracle.javafx.scenebuilder.kit.editor.drag.target.GridPaneDropTarget.RowArea;
import com.oracle.javafx.scenebuilder.kit.editor.panel.content.ContentPanelController;
import com.oracle.javafx.scenebuilder.kit.editor.panel.content.driver.handles.AbstractHandles;
import com.oracle.javafx.scenebuilder.kit.editor.panel.content.driver.gridpane.GridPaneHandles;
import com.oracle.javafx.scenebuilder.kit.editor.panel.content.driver.gridpane.GridPanePring;
import com.oracle.javafx.scenebuilder.kit.editor.panel.content.driver.gridpane.GridPaneTring;
import com.oracle.javafx.scenebuilder.kit.editor.panel.content.driver.pring.AbstractPring;
import com.oracle.javafx.scenebuilder.kit.editor.panel.content.driver.resizer.AbstractResizer;
import com.oracle.javafx.scenebuilder.kit.editor.panel.content.driver.resizer.RegionResizer;
import com.oracle.javafx.scenebuilder.kit.editor.panel.content.driver.tring.AbstractTring;
import com.oracle.javafx.scenebuilder.kit.editor.panel.content.util.BoundsUtils;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMInstance;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMObject;
import com.oracle.javafx.scenebuilder.kit.util.Deprecation;
import javafx.geometry.Bounds;
import javafx.geometry.Point2D;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Region;

/**
 *
 */
public class GridPaneDriver extends AbstractNodeDriver {

    private static final double MATCH_DIST = 4;

    public GridPaneDriver(ContentPanelController contentPanelController) {
        super(contentPanelController);
    }

    /*
     * AbstractNodeDriver
     */
    
    @Override
    public AbstractHandles<?> makeHandles(FXOMObject fxomObject) {
        assert fxomObject instanceof FXOMInstance;
        assert fxomObject.getSceneGraphObject() instanceof GridPane;
        return new GridPaneHandles(contentPanelController, (FXOMInstance)fxomObject);
    }

    @Override
    public AbstractPring<?> makePring(FXOMObject fxomObject) {
        assert fxomObject instanceof FXOMInstance;
        assert fxomObject.getSceneGraphObject() instanceof GridPane;
        return new GridPanePring(contentPanelController, (FXOMInstance) fxomObject);
    }

    @Override
    public AbstractTring<?> makeTring(AbstractDropTarget dropTarget) {
        assert dropTarget != null;
        assert dropTarget.getTargetObject() instanceof FXOMInstance;
        assert dropTarget.getTargetObject().getSceneGraphObject() instanceof GridPane;
        return new GridPaneTring(contentPanelController, (FXOMInstance) dropTarget.getTargetObject());
    }

    
    @Override
    public AbstractResizer<?> makeResizer(FXOMObject fxomObject) {
        assert fxomObject.getSceneGraphObject() instanceof GridPane;
        return new RegionResizer((Region) fxomObject.getSceneGraphObject());
    }
    
    
    @Override
    public AbstractDropTarget makeDropTarget(FXOMObject fxomObject, double sceneX, double sceneY) {
        assert fxomObject.getSceneGraphObject() instanceof GridPane;
        assert fxomObject instanceof FXOMInstance;
        
        final AbstractDropTarget result;
        
        final FXOMInstance fxomInstance = (FXOMInstance) fxomObject;
        final GridPane gridPane = (GridPane) fxomInstance.getSceneGraphObject();
        final int columnCount = Deprecation.getGridPaneColumnCount(gridPane);
        final int rowCount = Deprecation.getGridPaneRowCount(gridPane);
        
        
        /*
         *
         *               
         *     |--------|-------------|-----------|-----------------------|--|
         *         0           1            2                 3            4
         *     +-------------------------------------------------------------+ -
         *     |                                                             | |
         *     |  +-----+   +---------+   +-------+   +-------------------+  | |
         *     |  |     |   |         |   |       |   |                   |  | | 0
         *     |  | 0,0 |   |   1,0   |   |  2,0  |   |        3,0        |  | |
         *     |  |     |   |         |   |       |   |                   |  | |
         *     |  +-----+   +---------+   +-------+   +-------------------+  | -
         *     |                                                             | |
         *     |  +-----+   +---------+   +-------+   +-------------------+  | |
         *     |  |     |   |         |   |       |   |                   |  | | 1
         *     |  | 0,1 |   |   1,1   |   |  2,1  |   |        3,1        |  | |
         *     |  |     |   |         |   |       |   |                   |  | |
         *     |  +-----+   +---------+   +-------+   +-------------------+  | -
         *     |         (A)                                                 | |
         *     |  +-----+   +---------+   +-------+   +-------------------+  | |
         *     |  |     |   |         |   |       |   |                   |  | | 2
         *     |  | 0,2 |   |   1,2   |   |  2,2  |   |        3,2        |  | |
         *     |  |     |   |   (B)   |   |       |   |                   |  | |
         *     |  +-----+   +---------+   +-------+   +-------------------+  | -
         *     |                                                             | |
         *     |  +-----+   +---------+   +-------+   +-------------------+  | |
         *     |  |     |   |         |   |       |   |                   |  | | 3
         *     |  | 0,3 |   |   1,3   |   |  2,3  |   |        3,3        |  | |
         *     |  |     |   |         |   |       |   |                   |  | |
         *     |  +-----+   +---------+   +-------+   +-------------------+  | -
         *     |                                                             | |
         *     |  +-----+   +---------+   +-------+   +-------------------+  | |
         *     |  |     |   |         |   |       |   |                   |  | | 4
         *     |  | 0,4 |   |   1,4   |   |  2,4  |   |        3,4        |  | |
         *     |  |     |   |         |   |       |   |                   |  | |
         *     |  +-----+   +---------+   +-------+   +-------------------+  | -
         *     |                                                             | | 5
         *     +-------------------------------------------------------------+ -
         */
        
        if ((rowCount == 0) || (columnCount == 0)) {
            result = new GridPaneDropTarget(fxomInstance, 0, 0, ColumnArea.CENTER, RowArea.CENTER);
        } else {
        
            final Point2D hitPoint = gridPane.sceneToLocal(sceneX, sceneY, true /* rootScene */);
            final double hitX = hitPoint.getX();
            final double hitY = hitPoint.getY();
            final int targetColumnIndex, targetRowIndex;
            final double targetCellX, targetCellY;
            
            // Searches the column where hitX resides
            int c = 0;
            Bounds cellBounds = Deprecation.getGridPaneCellBounds(gridPane, c++, 0);
            double columnMaxX = cellBounds.getMaxX();
            while ((columnMaxX < hitX) && (c < columnCount)) {
                cellBounds = Deprecation.getGridPaneCellBounds(gridPane, c++, 0);                
                columnMaxX = cellBounds.getMaxX();
            }
            if (hitX <= columnMaxX) { // hitX is in column 'c-1'
                assert hitX <= columnMaxX;
                targetColumnIndex = c-1;
                targetCellX = hitX;
            } else { // hitX is past the last column
                targetColumnIndex = columnCount;
                targetCellX = 0.0;
            }
            
            // Searches the row where hitY resides
            int r = 0;
            cellBounds = Deprecation.getGridPaneCellBounds(gridPane, 0, r++);
            double rowMaxY = cellBounds.getMaxY();
            while ((rowMaxY < hitY) && (r < rowCount)) {
                cellBounds = Deprecation.getGridPaneCellBounds(gridPane, 0, r++);
                rowMaxY = cellBounds.getMaxY();
            }
            if (hitY <= rowMaxY) { // hitY is in row 'r-1'
                assert hitY <= rowMaxY;
                targetRowIndex = r-1;
                targetCellY = hitY;
            } else {
                targetRowIndex = rowCount;
                targetCellY = 0.0;
            }
            
            
            /*
             *            hgap          targetCellBounds.width
             *        +----------+----------------------------------+
             *        |                                             |
             *        |                                             |
             *        +          +----------------------------------+
             *        |          |                                  |
             *        |          |    +........................+    |
             *        |          |    .                        .    |
             *        |          |    .                        .    |
             *        |    (A)   |(B) .           (C)          . (D)|
             *        |          |    .                        .    |
             *        |          |    .                        .    |
             *        |          |    .                        .    |
             *        |          |    +........................+    |
             *        |          |                                  |
             *        +----------+----------------------------------+
             * 
             *        (A) ColumnArea.LEFT
             *        (B) ColumnArea.LEFT
             *        (C) ColumnArea.CENTER
             *        (D) ColumnArea.RIGHT
             */
            
            final ColumnArea targetColumnArea;
            if (targetColumnIndex < columnCount) {
                final Bounds targetCellBounds 
                        = Deprecation.getGridPaneCellBounds(gridPane, targetColumnIndex, 0);
                final BoundsUtils.EdgeInfo edgeInfo
                        = BoundsUtils.distanceToEdges(targetCellBounds, targetCellX, targetCellY, gridPane);
                if (targetCellX < targetCellBounds.getMinX()) {
                    targetColumnArea= ColumnArea.LEFT; // (A)
                } else if (edgeInfo == null) {
                    targetColumnArea= ColumnArea.CENTER; // cell bounds are empty
                } else {
                    final boolean eastMatch = edgeInfo.getEastDistance() < MATCH_DIST;
                    final boolean westMatch = edgeInfo.getWestDistance() < MATCH_DIST;
                    if (westMatch) {
                        targetColumnArea= ColumnArea.LEFT; // (B)
                    } else if (eastMatch) {
                        targetColumnArea= ColumnArea.RIGHT; // (D)
                    } else {
                        targetColumnArea= ColumnArea.CENTER; // (C)
                    }
                }
            } else {
                targetColumnArea = ColumnArea.LEFT;
            }
            
            /*
             *        +----------+----------------------------------+
             *        |                                             |
             * vgap   |                           (A)               |
             *        |                                             |
             *        +          +----------------------------------+
             *        |          |                (B)               |
             *        |          |    +........................+    |
             *        |          |    .                        .    |
             *        |          |    .                        .    |
             *        |          |    .           (C)          .    |
             *        |          |    .                        .    |
             *        |          |    .                        .    |
             *        |          |    .                        .    |
             *        |          |    +........................+    |
             *        |          |                (D)               |
             *        +----------+----------------------------------+
             * 
             *        (A) RowArea.TOP
             *        (B) RowArea.TOP
             *        (C) RowArea.CENTER
             *        (D) RowArea.BOTTOM
             */
            
            final RowArea targetRowArea;
            if (targetRowIndex < rowCount) {
                final Bounds targetCellBounds 
                        = Deprecation.getGridPaneCellBounds(gridPane, 0, targetRowIndex);
                final BoundsUtils.EdgeInfo edgeInfo
                        = BoundsUtils.distanceToEdges(targetCellBounds, targetCellX, targetCellY, gridPane);
                if (targetCellY < targetCellBounds.getMinY()) {
                    targetRowArea = RowArea.TOP; // (A)
                } else if (edgeInfo == null) {
                    targetRowArea = RowArea.CENTER; // cell bounds are empty
                } else {
                    final boolean northMatch = edgeInfo.getNorthDistance() < MATCH_DIST;
                    final boolean southMatch = edgeInfo.getSouthDistance() < MATCH_DIST;
                    if (northMatch) {
                        targetRowArea= RowArea.TOP; // (B)
                    } else if (southMatch) {
                        targetRowArea= RowArea.BOTTOM; // (D)
                    } else {
                        targetRowArea= RowArea.CENTER; // (C)
                    }
                }
            } else {
                targetRowArea = RowArea.TOP;
            }
            
            result = new GridPaneDropTarget(fxomInstance, 
                    targetColumnIndex, targetRowIndex, 
                    targetColumnArea, targetRowArea);
        }
        
        
        return result;
    }
    
}


<|start_filename|>kit/src/main/resources/com/oracle/javafx/scenebuilder/kit/css/ThemeDark.css<|end_filename|>
/*
 * Copyright (c) 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */

/*******************************************************************************
 *                                                                             *
 * Preset colours for the theme                                                *
 *                                                                             *
 ******************************************************************************/

.theme-presets {
    -sb-base: rgb(147, 147, 147);
    -sb-base-dark: derive(-sb-base, -60%);
    
    -sb-header-text: derive(-sb-base, -70%);
    -sb-header-text-dimmed: derive(-sb-base, -45%);
    -sb-header-detail: derive(-sb-base, -45%);
    -sb-header-height: 32;
    
    -sb-content-text: derive(-sb-base, 75%);
    -sb-content-text-dimmed: derive(-sb-base, 25%);
    -sb-content-detail: derive(-sb-base, 55%);
    -sb-content-background: derive(-sb-base, -32%);
    
    -sb-grad-hi: derive(-sb-base, 65%);
    -sb-sharp-edge-hi: derive(-sb-base, 20%);
    -sb-flat: -sb-base;
    -sb-sharp-edge-lo: derive(-sb-base, -40%);
    
    -sb-text-control-inner: rgb(90, 90, 90);
    -sb-text-control-outer: rgb(135, 135, 135);
    
    -sb-scrollbar-thumb: -sb-flat;
    
    -sb-focus-color: rgb(105, 255, 205);
    -sb-focus-inner-background: derive(-sb-flat, 10%);
    -sb-selected-bar-color: rgb(75, 155, 210);
    -sb-selected-bar-unfocused-color: derive(-sb-content-background, 20%);
    
    -sb-line-art: derive(-sb-base, 40%);
    -sb-line-art-accent: rgb(105, 255, 205);
    
    -sb-css-winner-color: rgb(125, 125, 85);
    -sb-warning-panel: rgb(230, 225, 140);
    -sb-warning-panel-text: derive(-sb-base, -50%);
    -sb-warning-panel-text-dimmed: derive(-sb-base, -20%);
    -sb-message-panel: rgb(185, 220, 240);
}

/*******************************************************************************
 *                                                                             *
 * Common UI Styling                                                           *
 *                                                                             *
 ******************************************************************************/

/*******************************************************************************
 * Common UI Styling - Menus                                                   *
 ******************************************************************************/

.theme-presets .context-menu {
    -fx-background-color: 
        derive(-sb-sharp-edge-lo, -30%), derive(-sb-content-background, -20%);
    -fx-background-insets: 0, 1;
    -fx-background-radius: 0;
}
.theme-presets .context-menu .separator:horizontal .line {
     -fx-border-color: 
         derive(-sb-text-control-outer, -20%), derive(-sb-content-background, -20%);
     -fx-border-insets: 0, 1 0 0 0;
}
.theme-presets .context-menu .menu-item > .label {
    -fx-text-fill: -sb-content-text;
}

.theme-presets .custom-menu-item {
    -fx-padding: 4 8 4 8;
}
.theme-presets .custom-menu-item:focused {
    -fx-background: derive(-sb-content-background, -20%);
    -fx-background-color: -fx-background;
}

.theme-presets .menu > .right-container > .arrow {
    -fx-background-color: -sb-content-detail;
}

.theme-presets .radio-menu-item:checked > .left-container > .radio {
    -fx-background-color: -sb-content-detail;
}

/*******************************************************************************
 * Common UI Styling - Cog Button                                              *
 ******************************************************************************/

.theme-presets .cog-shape {
    -fx-shape: "M15.385,6.769h-0.738c-0.338,0-0.697-0.265-0.797-0.587L13.424,5.15c-0.158-0.298-0.094-0.74,0.145-0.978l0.523-0.523
        c0.242-0.239,0.242-0.632,0-0.872l-0.869-0.868c-0.24-0.241-0.633-0.241-0.873,0l-0.521,0.523
        c-0.238,0.239-0.682,0.304-0.979,0.144L9.818,2.152C9.496,2.052,9.23,1.692,9.23,1.354V0.615C9.23,0.277,8.953,0,8.615,0H7.386
        C7.047,0,6.77,0.277,6.77,0.615v0.739c0,0.338-0.265,0.697-0.586,0.798L5.152,2.576c-0.299,0.16-0.739,0.097-0.978-0.144
        L3.65,1.909c-0.24-0.241-0.631-0.241-0.872,0L1.91,2.777c-0.241,0.24-0.241,0.633,0,0.871l0.522,0.524
        C2.672,4.41,2.738,4.853,2.578,5.15L2.152,6.183C2.052,6.504,1.692,6.77,1.355,6.77l-0.74-0.001C0.277,6.769,0,7.046,0,7.385v1.23
        C0,8.951,0.277,9.23,0.615,9.23h0.74c0.337,0,0.695,0.266,0.797,0.588l0.426,1.029c0.158,0.299,0.094,0.742-0.146,0.979L1.91,12.35
        c-0.241,0.24-0.241,0.633,0,0.873l0.868,0.869c0.241,0.24,0.632,0.24,0.872,0l0.523-0.523c0.237-0.238,0.679-0.305,0.977-0.143
        l1.033,0.424c0.321,0.1,0.586,0.459,0.586,0.797v0.738C6.77,15.723,7.047,16,7.386,16h1.229c0.338,0,0.615-0.277,0.615-0.615
        v-0.738c0-0.338,0.266-0.697,0.588-0.797l1.031-0.424c0.301-0.162,0.74-0.096,0.98,0.143l0.52,0.523c0.24,0.24,0.633,0.24,0.873,0
        l0.869-0.869c0.242-0.24,0.242-0.633,0-0.873l-0.523-0.523c-0.238-0.236-0.303-0.68-0.145-0.979l0.426-1.029
        c0.1-0.322,0.459-0.588,0.797-0.588h0.738C15.725,9.23,16,8.953,16,8.615v-1.23C16,7.046,15.725,6.769,15.385,6.769z M11.582,7.998
        c0,1.979-1.602,3.58-3.578,3.58c-1.979,0-3.582-1.602-3.582-3.58c0-1.978,1.603-3.58,3.582-3.58
        C9.98,4.418,11.582,6.021,11.582,7.998z";
    -fx-background-color: -sb-header-detail;
    -fx-min-width: 8;
    -fx-max-width: 8;
    -fx-min-height: 8;
    -fx-max-height: 8;
}
.theme-presets .property-sheet .cog-shape {
    -fx-background-color: -sb-content-detail;
}

/*******************************************************************************
 * Common UI Styling - Accordion and TitledPane and search area shadows        *
 ******************************************************************************/

.theme-presets .accordion > .titled-pane > .title {
    -fx-background-color: 
        derive(-sb-content-background, -35%), -sb-sharp-edge-lo, -sb-sharp-edge-hi, -sb-flat;
    -fx-background-insets: 0 0 -1 0, 0, 1 0 0 0, 2 0 0 0; 
    -fx-padding: 0.385em 0.692em 0.231em 0.692em;
    -fx-font-size: 0.875em;
    -fx-alignment: center-right;
    -fx-effect: dropshadow( two-pass-box, rgba(0, 0, 0, 0.2), 5, 0.0, 0, 1 );
}
.theme-presets .accordion > .titled-pane > .title > .text {
    -fx-fill: -sb-header-text;
}
.theme-presets .accordion > .first-titled-pane > .title {
    -fx-background-insets: 0 0 -1 0, 0, 1 0 1 0, 2 0 0 0;
}
.theme-presets .accordion > .titled-pane > .title > .arrow-button {
    -fx-padding: -0.154em 0.583em 0.0em 0.0em; 
}
.theme-presets .accordion > .titled-pane > .title > .arrow-button > .arrow {
    -fx-background-color: -sb-header-detail;
    -fx-padding: 0.154em 0.231em 0.231em 0.231em; /* 2 3 3 3 */
}
.theme-presets .pane-shadow {
    -fx-background-color: derive(-sb-content-background, -35%);
    -fx-effect: dropshadow( two-pass-box, rgba(0, 0, 0, 0.6), 5, 0.0, 0, 0 );
}

/*******************************************************************************
 * Common UI Styling - Panel Backgrounds                                       *
 ******************************************************************************/

.theme-presets .property-sheet, 
.theme-presets .property-sheet > .viewport, 
.theme-presets .SBKIT-library-panel, 
.theme-presets .SBKIT-library-panel .accordion > .titled-pane > .content,
.theme-presets .SBKIT-hierarchy-panel .tree-view {
    -fx-border-color: null;
    -fx-border-width: 0;
    -fx-background-color: -sb-content-background;
    -fx-background-insets: 0;
    -fx-padding: 0;
}

.theme-presets .titled-pane > .content {
    -fx-border-width: 0;
    -fx-border-insets: 0;
}

/*******************************************************************************
 * Common UI Styling - Split Panes                                             *
 ******************************************************************************/

.theme-presets .split-pane {
    -fx-background-color: -sb-base-dark;
    -fx-background-insets: 0;
    -fx-padding: 0;
}
.theme-presets .split-pane > .split-pane-divider {
    -fx-background-color: -sb-base-dark;
    -fx-padding: 0 0.5 0 0;
}
.theme-presets .split-pane:horizontal .split-pane-divider .horizontal-grabber,
.theme-presets .split-pane:vertical .split-pane-divider .vertical-grabber {
    -fx-padding: 3;
    -fx-shape: "";
}

/*******************************************************************************
 * Common UI Styling - ScrollBars                                              *
 ******************************************************************************/

.theme-presets .scroll-bar .track-background {
    -fx-background-color: transparent;
}
.theme-presets .scroll-bar:vertical > .decrement-button,
.theme-presets .scroll-bar:vertical > .increment-button {
    -fx-padding: 0.077em 0.769em 0.077em 0.615em;
}
.theme-presets .scroll-bar:horizontal > .decrement-button,
.theme-presets .scroll-bar:horizontal > .increment-button {
    -fx-padding: 0.692em 0.077em 0.692em 0.077em;
}
.theme-presets .scroll-bar .increment-arrow, 
.theme-presets .scroll-bar .decrement-arrow {
    -fx-padding: 0;
}
.theme-presets .scroll-bar:vertical {
    -fx-background-color: derive(-sb-sharp-edge-lo, -20%), derive(-sb-content-background, -8%);
    -fx-background-insets: 0 0 0 0, 0 0 0 1;
}
.theme-presets .scroll-bar:horizontal {
    -fx-background-color: derive(-sb-sharp-edge-lo, -20%), derive(-sb-content-background, -8%);
    -fx-background-insets: 0, 1 0 0 0;
}
.theme-presets .scroll-bar .thumb { 
    -fx-background-color: -sb-text-control-outer, derive(-sb-content-background, 10%);
    -fx-background-radius: 0;
}
.theme-presets .scroll-bar:vertical .thumb {
    -fx-background-insets: 3 4 3 4, 4 5 4 5;
}
.theme-presets .scroll-bar:horizontal .thumb {
    -fx-background-insets: 4 3 4 3, 5 4 5 4;
}
.theme-presets .scroll-pane .corner,
.theme-presets .tree-view .virtual-flow .corner {
    -fx-background-color: -sb-sharp-edge-lo, -sb-content-background;
    -fx-background-insets: 0 -1 -1 0, 1 -1 -1 1;
}

.theme-presets .text-area .scroll-pane .scroll-bar:horizontal {
    -fx-background-insets: 0 1 1 1, 1 1 1 1;
}
.theme-presets .text-area .scroll-pane .scroll-bar:vertical {
    -fx-background-insets: 1 1 1 0, 1 1 1 1;
}
.theme-presets .text-area .scroll-pane .corner {
    -fx-background-insets: 0 1 1 0, 1 1 1 1;
}

/*******************************************************************************
 *                                                                             *
 * UI Panels                                                                   *
 *                                                                             *
 ******************************************************************************/

/*******************************************************************************
 *                                                                             *
 * Library panel                                                               *
 *                                                                             *
 ******************************************************************************/

.theme-presets.SBKIT-library-panel .list-view {
    -fx-background-color: transparent;
    -fx-padding: 0;
}
.theme-presets.SBKIT-library-panel .list-view .list-cell {
    -fx-background-color: transparent;
    -fx-cell-size: 1.8em;
    -fx-background-insets: 2 0 -2 0;
    -fx-padding: 2 0 0 4;
}
.theme-presets.SBKIT-library-panel .list-view .list-cell:filled:hover {
    -fx-background-color: null; /* no hover effect on list view */
}
.theme-presets.SBKIT-library-panel .list-view:focused .list-cell:filled:selected {
    -fx-background-color: -sb-selected-bar-color;
}
.theme-presets.SBKIT-library-panel .list-view .list-cell:filled:selected {
    /* When the ListView is not focused there is no need to highlight
    the selected item */
    -fx-background-color: null;
}
.theme-presets.SBKIT-library-panel .list-view .scroll-bar {    
    /* Cursor may dynamically change when working in library / hierarchy.
       Set back the cursor to its default. */
    -fx-cursor: default;
}

/* ListCell graphic is an HBox */
.theme-presets.SBKIT-library-panel .list-cell-graphic {
    -fx-alignment: center-left;
    -fx-padding: 0 2 0 2;
    -fx-spacing: 4;
}

.theme-presets.SBKIT-library-panel .library-no-results-label {
    -fx-text-fill: -sb-content-text-dimmed;
    -fx-font-size: 0.875em;
    -fx-padding: 6 0 0 10;
}
   
.theme-presets.SBKIT-library-panel .library-section-label {
    -fx-text-fill: derive(-sb-content-text, 10%);
    -fx-font-size: 0.875em;    
    -fx-background-color: 
        linear-gradient(to right, derive(-sb-sharp-edge-lo, -20%), -sb-sharp-edge-lo),
        linear-gradient(to right, derive(-sb-content-background, -10%) 0%, -sb-content-background 70%);
    -fx-background-insets: -4 -2 -4 -12, -3 -2 -3 -12;
}

.theme-presets.SBKIT-library-panel .library-classname-label {
    -fx-text-fill: -sb-content-text;
}

.theme-presets.SBKIT-library-panel .library-qualifier-label {
    -fx-text-fill: -sb-content-text-dimmed;
}

/*******************************************************************************
 * Library Panel - Jar import dialog                                           *
 ******************************************************************************/

.theme-presets .jar-import-preview-label {
    -fx-text-fill: -sb-content-text-dimmed;
    -fx-font-size : 0.875em;
}

.theme-presets .jar-import-preview {
    -fx-background-color: azure;
}

.theme-presets .jar-import-num-of-items-label {
    -fx-font-size : 0.875em;
}

.theme-presets .jar-import-class-name-label {
    -fx-text-fill: -sb-content-text-dimmed;
    -fx-font-size : 0.875em;
}

/*******************************************************************************
 *                                                                             *
 * Hierarchy Panel                                                             *
 *                                                                             *
 ******************************************************************************/

/*******************************************************************************
 * Hierarchy Panel - TreeView / TreeTableView                                  *
 ******************************************************************************/

.theme-presets.SBKIT-hierarchy-panel .tree-cell {
    -fx-background-color: transparent;
    -fx-padding: 2 0 2 0;
    -fx-background-insets: 0 0 0 0;
}
.theme-presets.SBKIT-hierarchy-panel .tree-cell .label {
    -fx-text-fill: -sb-content-text;
}
.theme-presets.SBKIT-hierarchy-panel .hierarchy-first-cell {
    -fx-padding: 4 0 0 1; 
    -fx-background-insets: 4 0 0 0;
}

/* Selected rows */
.theme-presets.SBKIT-hierarchy-panel .tree-view:focused > .virtual-flow > .clipped-container > .sheet > .tree-cell:filled:selected,
.theme-presets.SBKIT-hierarchy-panel .tree-table-view:focused > .virtual-flow > .clipped-container > .sheet > .tree-table-row-cell:filled:selected,
.theme-presets.SBKIT-hierarchy-panel .tree-table-view:focused > .virtual-flow > .clipped-container > .sheet > .tree-table-row-cell .tree-table-cell:selected {
    -fx-background-color: -sb-selected-bar-color;
}
/* Selected rows when control is not focused */
.theme-presets.SBKIT-hierarchy-panel .tree-cell:filled:selected,
.theme-presets.SBKIT-hierarchy-panel .tree-table-row-cell:filled:selected,
.theme-presets.SBKIT-hierarchy-panel .tree-table-row-cell:filled > .tree-table-cell:selected {
    -fx-background-color: -sb-selected-bar-unfocused-color;
}

/*******************************************************************************
 * Hierarchy Panel - Graphic Icon and Disclosure Arrows                        *
 ******************************************************************************/

/* TreeCell graphic is a HBox */
.theme-presets.SBKIT-hierarchy-panel .tree-cell-graphic {
    -fx-alignment: center-left;
    -fx-padding: 0 2 0 0;
    -fx-spacing: 2;
}

.theme-presets.SBKIT-hierarchy-panel .tree-view .tree-cell .tree-disclosure-node {
    -fx-padding: 7 6 6 9;
    -fx-background-color: derive(-sb-base-dark, -10%), derive(-sb-sharp-edge-hi, 10%);
    -fx-background-insets: 3 2 2 5, 4 3 3 6;
    -fx-background-radius: 10;
    -fx-cursor: hand;
}
.theme-presets.SBKIT-hierarchy-panel .tree-view .tree-cell:expanded .tree-disclosure-node {
    -fx-background-color: derive(-sb-sharp-edge-hi, -20%), -sb-content-background;
}
.theme-presets.SBKIT-hierarchy-panel .tree-view .tree-cell .tree-disclosure-node .arrow {
    -fx-shape: "M0,3h3V0h1v3h3v1H4v3H3l0-3H0V3z";
    -fx-padding: 0.2em;
    -fx-background-color: -sb-header-text;
}
.theme-presets.SBKIT-hierarchy-panel .tree-view .tree-cell:expanded .tree-disclosure-node .arrow {
    -fx-shape: "M0,3h7v1H0V3z M0,0L0,0L0,0L0,0L0,0z M7,7L7,7L7,7L7,7L7,7z";
    -fx-padding: 0.2em;
    -fx-background-color: -sb-content-text;
    -fx-rotate: 0;
}

/*******************************************************************************
 * Hierarchy Panel - Place holder Labels                                       *
 ******************************************************************************/

.theme-presets.SBKIT-hierarchy-panel .hierarchy-place-holder-label {
    -fx-label-padding: 0 3 0 0;
}

/*******************************************************************************
 * Hierarchy Panel - Read/Write labels and Inline editing                      *
 ******************************************************************************/

/* editable label on node in hierarchy (info / node id / fx:id) */
.theme-presets.SBKIT-hierarchy-panel .hierarchy-readwrite-label {
    -fx-border-color: transparent;
    -fx-border-insets: 0 1 0 0;
}
.theme-presets.SBKIT-hierarchy-panel .hierarchy-readwrite-label:hover {
    -fx-border-color: -sb-sharp-edge-hi;
    -fx-cursor: text;
}
.theme-presets.SBKIT-hierarchy-panel .hierarchy-readwrite-label .text {
    -fx-fill: -sb-content-text-dimmed;
}
.theme-presets.SBKIT-hierarchy-panel .hierarchy-readwrite-label:hover .text {
    -fx-fill: -sb-content-text;
}

.theme-presets.SBKIT-hierarchy-panel .inline-editor, .inline-editor:focused {
    -fx-background-color: white;
    -fx-background-insets: 0;
    -fx-border-color: rgb(25, 164, 255);
    -fx-text-fill: black;
    -fx-padding: 0 1 0 1;
}

/*******************************************************************************
 * Hierarchy Panel - Empty FXML 'drag items here' prompt                       *
 ******************************************************************************/

.theme-presets.SBKIT-hierarchy-panel .hierarchy-prompt-label {
    -fx-font-size: 1.231em; /* 16px (base pixel size of 13px) */
    -fx-text-fill: derive(-sb-content-text-dimmed, 20%);
    -fx-text-alignment: center;
    -fx-alignment: center;
    -fx-wrap-text: true;
    -fx-label-padding: 16;
    -fx-background-color: transparent;
    -fx-border-color: -sb-sharp-edge-hi;
    -fx-border-width: 1;
    -fx-border-style: segments(1px, 2px);
}

/*******************************************************************************
 *                                                                             *
 * Content Panel                                                               *
 *                                                                             *
 ******************************************************************************/

.theme-presets.SBKIT-content-panel .selection-handles {
    -fx-fill: white;
    -fx-stroke: blue;
    -fx-stroke-width: 1;
    -fx-stroke-type: inside;
}

.theme-presets.SBKIT-content-panel .selection-handles-dim {
    -fx-fill: white;
    -fx-stroke: gray;
    -fx-stroke-width: 1;
    -fx-stroke-type: inside;
}

.theme-presets.SBKIT-content-panel .selection-rect {
    -fx-stroke: null; 
    -fx-fill: rgba(0, 0, 0, 0.05);
}

.theme-presets.SBKIT-content-panel .selection-wire {
    -fx-stroke: rgb(25, 164, 255);
    -fx-stroke-width: 1;
    -fx-fill: null;
}

.theme-presets.SBKIT-content-panel .selection-pipe {
    -fx-stroke: rgb(25, 164, 255);
    -fx-stroke-width: 3;
    -fx-fill: null;
}

.theme-presets.SBKIT-content-panel .selection-gap {
    -fx-stroke: null;
    -fx-fill: lightgrey;
    -fx-opacity: 0.5;
}

.theme-presets.SBKIT-content-panel .parent-ring {
    /* -fx-stroke is setup from user preferences */
    -fx-stroke-width: 7;
    -fx-stroke-type: outside;
    -fx-opacity: 0.5;
    -fx-fill: null;
}

.theme-presets.SBKIT-content-panel .target-ring {
    /* -fx-stroke is setup from user preferences */
    -fx-stroke-width: 7;
    -fx-stroke-type: inside;
    -fx-opacity: 0.9;
    -fx-fill: null;
}

.theme-presets.SBKIT-content-panel .target-crack {
    -fx-stroke-width: 5;
    -fx-stroke-type: centered;
    -fx-stroke-line-cap: butt;
    -fx-stroke: black;
    -fx-opacity: 0.5;
    -fx-fill: null;
}

.theme-presets.SBKIT-content-panel .resize-rudder {
    -fx-stroke-width: 1;
    -fx-opacity: 0.3;
    -fx-stroke-dash-array: 8 8;
}

/*******************************************************************************
 * Content Panel - FXML 'drag items here' prompts                              *
 ******************************************************************************/

.theme-presets.SBKIT-content-panel .stage-prompt {
    -fx-font-size: 1.231em; /* 16px (base pixel size of 13px) */
    -fx-text-fill: derive(-sb-content-text-dimmed, 10%);
    -fx-text-alignment: center;
    -fx-alignment: center;
    -fx-wrap-text: true;
    -fx-label-padding: 16;
    -fx-background-color: transparent;
    -fx-border-color: -sb-sharp-edge-hi;
    -fx-border-width: 1;
    -fx-border-style: segments(1px, 2px);
}

.theme-presets.SBKIT-content-panel .stage-prompt-default {
    -fx-font-size: 1.231em; /* 16px (base pixel size of 13px) */
    -fx-text-fill: -sb-content-text;
    -fx-text-alignment: center;
    -fx-alignment: center;
    -fx-wrap-text: true;
    -fx-label-padding: 16;
}

/*******************************************************************************
 * Content Panel - GridPane Chrome #0 : common properties                      *
 ******************************************************************************/

.theme-presets.SBKIT-content-panel .tray {
    -fx-border-color: rgba(255, 255, 255, 0.4), rgba(0, 0, 0, 0.5), rgba(255, 255, 255, 0.5);
    -fx-border-insets: 0, 1, 2;
    -fx-border-radius: 5 5 0 0, 4 4 0 0, 3 3 0 0;
    -fx-background-color: #bbddff; /* Can be overwritten by user preferences */
    -fx-background-radius: 2 2 0 0;
    -fx-background-insets: 3;
    -fx-alignment: center;
}

.theme-presets.SBKIT-content-panel .tray.south {
    -fx-border-radius: 0 0 5 5, 0 0 4 4, 0 0 3 3;
    -fx-background-radius: 0 0 2 2;
}

.theme-presets.SBKIT-content-panel .tray.text {
    -fx-text-fill: black;
    -fx-text-overrun: leading-word-ellipsis;
    -fx-font-size: 0.769em; /* 10px (base pixel size of 13px) */
}

.theme-presets.SBKIT-content-panel .tray.west .text {
   -fx-rotate: +90;
}

.theme-presets.SBKIT-content-panel .tray.east .text {
   -fx-rotate: -90;
}

/*******************************************************************************
 * Content Panel - GridPane Chrome #1 : trays and cells of selected gridpane   *
 ******************************************************************************/

.theme-presets.SBKIT-content-panel .gap {
    -fx-stroke: transparent;
    -fx-fill: rgba(0,0,0,0.2);
}

.theme-presets.SBKIT-content-panel .gap.selected {
    -fx-fill: rgba(0,0,0,0.4);
}

.theme-presets.SBKIT-content-panel .gap.hilit {
    -fx-stroke: rgba(0,0,0,0.4);
    -fx-stroke-line-cap: butt;
    /* -fx-stroke-width is setup programmatically */
}

.theme-presets.SBKIT-content-panel .gap.empty {
    -fx-stroke: rgba(0,0,0,0.4);
    -fx-stroke-line-cap: butt;
    -fx-stroke-dash-array: 3 3;
}

.theme-presets.SBKIT-content-panel .tray.selected {
    -fx-border-color: rgba(255, 255, 255, 0.4), rgba(0, 0, 0, 1), rgba(255, 255, 255, 0.5);
    -fx-background-color: yellow;
}

.theme-presets.SBKIT-content-panel .tray.selected .text {
    -fx-text-fill: rgba(0,0,0,0.8);
}

/*******************************************************************************
 * Content Panel - BorderPane                                                  *
 ******************************************************************************/

.theme-presets.SBKIT-content-panel .target-ring.BorderPane {
    -fx-alignment: CENTER;
    -fx-border-width: 1;
    -fx-border-color: rgba(0,0,0,0.1);
    -fx-background-color: rgba(0,0,0,0.1);
    -fx-text-fill: grey;
}

.theme-presets.SBKIT-content-panel .target-ring.selected.BorderPane {
    -fx-alignment: CENTER;
    -fx-border-width: 1;
    -fx-border-color: rgba(0,0,0,0.1);
    -fx-background-color: rgba(0,0,0,0.4);
    -fx-text-fill: grey;
}

/*******************************************************************************
 * Content Panel - Marquee Chrome                                             *
 ******************************************************************************/

.theme-presets.SBKIT-content-panel .marquee {
/*    -fx-fill: rgba(178, 178, 229, 0.2);*/
    -fx-fill: rgba(150, 150, 150, 0.2);
    -fx-stroke: rgba(0, 0, 0, 0.3);
    -fx-stroke-type: inside;
}

/*******************************************************************************
 * Content Panel - Resize Shadow                                               *
 ******************************************************************************/

.theme-presets.SBKIT-content-panel .resize-shadow {
    -fx-background-color: rgba(255, 255, 0.0, 0.5);
    -fx-border-width: 1;
    -fx-border-style: dotted;
    -fx-border-color: black;
}

/*******************************************************************************
 * Content Panel - Resize Shadow                                               *
 ******************************************************************************/

.theme-presets.SBKIT-content-panel .css-pick-chrome {
    -fx-background-color: rgba(0.0, 255, 0.0, 0.5);
    -fx-border-width: 1;
    -fx-border-style: dotted;
    -fx-border-color: black;
}

/*******************************************************************************
 * Content Panel - Outline Mode                                                *
 ******************************************************************************/

.theme-presets.SBKIT-content-panel .outline-ring {
    -fx-stroke: rgba(0,0,0,0.2);
    -fx-stroke-type: inside;
    -fx-fill: rgba(0,0,0,0.01);
}


/*******************************************************************************
 *                                                                             *
 * Inspector Panel                                                             *
 *                                                                             *
 ******************************************************************************/

.theme-presets .SBKIT-inspector-panel .secondary-section-title {
    -fx-text-fill: -sb-header-text-dimmed;
}

/* Specific css for the Inspector SplitPane */
.theme-presets.SBKIT-inspector-panel > .split-pane-divider {
    -fx-background-color: transparent;
    -fx-background-insets: -1 0 -3 0;
    -fx-border-width: 0;
    -fx-padding: 0;
}

/*******************************************************************************
 *                                                                             *
 * Property Sheet Panels (Inspector, Controller)                               *
 *                                                                             *
 ******************************************************************************/

/*******************************************************************************
 * Property Sheet Panels - General Appearance                                  *
 ******************************************************************************/

.property-sheet {
    -fx-font-size: 0.875em;
}

.theme-presets.property-sheet .label {
    -fx-text-fill: -sb-content-text;
    -fx-padding: 4 0 3 0;
}

.theme-presets.property-sheet .button,
.theme-presets.property-sheet .toggle-button {
    -fx-text-fill: -sb-content-text;
}

.theme-presets.property-sheet .slider > .track {
    -fx-background-color: -sb-text-control-outer, -sb-text-control-inner;
    -fx-background-insets: -2 -5 -3 -5, -1 -4 -2 -4;
    -fx-background-radius: 0;
}
.theme-presets.property-sheet .slider:vertical > .track {
    -fx-background-insets: -5 -3 -5 -2, -4 -2 -4 -1;
}
.theme-presets.property-sheet .slider > .thumb {
    -fx-background-color: -sb-content-detail, derive(-sb-content-background, 10%);
    -fx-background-insets: 2, 3;
    -fx-background-radius: 0;
    -fx-effect: null;
}

.theme-presets.property-sheet .check-box {
    -fx-text-fill: -sb-content-text;
}
.theme-presets.property-sheet .check-box .box {
    -fx-background-color: -sb-text-control-outer, -sb-text-control-inner;
    -fx-background-insets: 0, 1;
    -fx-background-radius: 0;
    -fx-padding: 0.231em 0.231em 0.154em 0.154em;
}
.theme-presets.property-sheet .check-box:selected .box {
    -fx-background-radius: 0;
    -fx-padding: 0.308em;
}
.theme-presets.property-sheet .check-box:selected > .box > .mark,
.theme-presets.property-sheet .check-box:indeterminate > .box > .mark {
    -fx-background-color: -sb-content-text;
    -fx-background-insets: 0;
    -fx-padding: 0.308em 0.308em 0.385em 0.385em;
}

.theme-presets.property-sheet .hyperlink, 
.theme-presets.property-sheet .hyperlink:visited {
    -fx-text-fill: -sb-content-text;
    -fx-padding: 3 4 2 0;
    -fx-underline: false;
}
.theme-presets.property-sheet .hyperlink:hover, 
.theme-presets.property-sheet .hyperlink:visited:hover {
    -fx-underline: true;
}

.theme-presets.property-sheet .button, 
.theme-presets.property-sheet .choice-box, 
.theme-presets.property-sheet .menu-button, 
.theme-presets.property-sheet .toggle-button {
    -fx-background-color: -sb-text-control-outer, derive(-sb-content-background, 10%);
    -fx-background-insets: 0, 1;
    -fx-padding: 4 6 3 6;
    -fx-background-radius: 0;
}

.theme-presets.property-sheet .text-field {
    -fx-background-color: -sb-text-control-outer, -sb-text-control-inner;
    -fx-background-insets: 0, 1;
    -fx-text-fill: -sb-content-text;
    -fx-padding: 4 5 3 5;
    -fx-background-radius: 0;
}

.theme-presets.property-sheet .text-area {
    -fx-background-color: -sb-text-control-outer, -sb-text-control-inner;
    -fx-background-insets: 0, 1;
    -fx-text-fill: -sb-content-text;
    -fx-background-radius: 0;
}
.theme-presets.property-sheet .text-area .content {
    -fx-background-color: -sb-text-control-inner;
    -fx-background-radius: 0;
    -fx-padding: 3 5 3 5;
}

.theme-presets.property-sheet .choice-box > .open-button, 
.theme-presets.property-sheet .menu-button > .arrow-button {
    -fx-padding: 2 0 2 0;
}
.theme-presets.property-sheet .choice-box > .label, 
.theme-presets.property-sheet .menu-button > .label {
    -fx-padding: 0 4 0 0;
}
.theme-presets.property-sheet .choice-box > .open-button > .arrow,
.theme-presets.property-sheet .menu-button > .arrow-button > .arrow  {
    -fx-background-color: -sb-content-detail;
    -fx-background-insets: 0;
    -fx-padding: 0.154em 0.308em 0.154em 0.231em;  /* 2 4 2 3 */
}

/*.theme-presets .property-sheet .table-view .column-header .label {
    -fx-text-fill: -sb-content-text;
    -fx-font-size: 0.875em;
    -fx-font-weight: normal;
    -fx-alignment: CENTER_LEFT;
    -fx-padding: 0 4 0 4;
}
.theme-presets .property-sheet .table-view > .virtual-flow > .clipped-container > .sheet > .table-row-cell .table-cell {
    -fx-padding: 4 5 2 5;
}
.theme-presets .property-sheet .table-view:focused > .virtual-flow > .clipped-container > .sheet > .table-row-cell:filled:selected,
.theme-presets .property-sheet .table-view:focused > .virtual-flow > .clipped-container > .sheet > .table-row-cell .table-cell:selected {
    -fx-background: -sb-selected-bar-color;
    -fx-table-cell-border-color: -sb-selected-bar-color;
}*/

.theme-presets.property-sheet .small-label {
    -fx-font-size: 0.750em; /* 9 */
    -fx-text-fill: -sb-content-text-dimmed;
    -fx-padding: 0;
}

.inspector-message .text {
    /* Used for 'No Selection' and 'No Results' messages */
    -fx-fill: -sb-content-text-dimmed;
}

.theme-presets.property-sheet .cog-menubutton {
    -fx-background-color: -sb-content-background;
    -fx-background-radius: 0;
    -fx-padding: 4 0 3 1;
    -fx-graphic-text-gap: 0;
    -fx-min-width: 10;
    -fx-max-width: 10;
    -fx-min-height: 10;
}
.theme-presets.property-sheet .cog-menubutton:hover {
    -fx-background-color: derive(-sb-content-background, -10%), -sb-content-background;
    -fx-background-insets: 0, 1;
}
.theme-presets.property-sheet .cog-menubutton:focused {
    -fx-background-color: -fx-focus-color, -fx-background;
}
.theme-presets.property-sheet .cog-menubutton > .arrow-button {
    visibility: hidden;
    -fx-padding: 0;
}
.theme-presets.property-sheet .cog-menubutton > .arrow {
    -fx-shape: null;
    -fx-padding: 0;
}

/*******************************************************************************
 * Property Sheet Panels - General Appearance - Focused States                 *
 ******************************************************************************/

.theme-presets.property-sheet .button:focused, 
.theme-presets.property-sheet .choice-box:focused, 
.theme-presets.property-sheet .menu-button:focused, 
.theme-presets.property-sheet .slider:focused > .thumb, 
.theme-presets.property-sheet .toggle-button:focused {
    -fx-background-color: -sb-focus-color, derive(-sb-content-background, 10%);
}
.theme-presets.property-sheet .check-box:focused .box, 
.theme-presets.property-sheet .text-field:focused, 
.theme-presets.property-sheet .text-area:focused {
    -fx-background-color: -sb-focus-color, -sb-text-control-inner;
}

/*******************************************************************************
 * Property Sheet Panels - CSS Override property decoration                    *
 ******************************************************************************/

.theme-presets.property-sheet .css-override {
    -fx-background-color: -sb-css-winner-color;
    -fx-border-color: derive(-sb-sharp-edge-lo, 0%);
    -fx-border-style: dotted;
}
.theme-presets.property-sheet .css-override > .hyperlink {
    -fx-padding: 0;
}

/*******************************************************************************
 * Property Sheet Panels - Sub Section Titles                                  *
 ******************************************************************************/

.sub-section-background {
    -fx-background-color:  
        linear-gradient(to left, derive(-sb-sharp-edge-lo, -20%), -sb-sharp-edge-lo),
        linear-gradient(to left, derive(-sb-content-background, -10%) 0%, -sb-content-background 70%);
    -fx-background-insets: 7 -1 8 -10, 8 -1 9 -10;
    -fx-padding: 8 10 8 0;
}

/*******************************************************************************
 * Property Sheet Panels - Insets Editor                                       *
 ******************************************************************************/

.theme-presets.property-sheet .insets-icon-top {
    -fx-background-color: -sb-content-detail, derive(-sb-content-detail, -30%), -sb-content-background;
    -fx-background-insets: 0, 1 0 0 0, 1;
}
.theme-presets.property-sheet .insets-icon-right {
    -fx-background-color: -sb-content-detail, derive(-sb-content-detail, -30%), -sb-content-background;
    -fx-background-insets: 0, 0 1 0 0, 1;
}
.theme-presets.property-sheet .insets-icon-bottom {
    -fx-background-color: -sb-content-detail, derive(-sb-content-detail, -30%), -sb-content-background;
    -fx-background-insets: 0, 0 0 1 0, 1;
}
.theme-presets.property-sheet .insets-icon-left {
    -fx-background-color: -sb-content-detail, derive(-sb-content-detail, -30%), -sb-content-background;
    -fx-background-insets: 0, 0 0 0 1, 1;
}

.theme-presets.property-sheet .insets-copy-button-shape {
    -fx-shape: "M1.25,7.5c-0.13,0-0.259-0.051-0.357-0.15C0.7,7.152,0.703,6.836,0.9,
        6.643L3.598,4L0.9,1.357 C0.703,1.164,0.7,0.847,0.893,0.65c0.194-0.196,0.51-0.2,
        0.708-0.007L5.027,4L1.6,7.357C1.502,7.453,1.376,7.5,1.25,7.5z";
    -fx-background-color: -sb-content-detail;
    -fx-min-width: 4;
    -fx-max-width: 4;
    -fx-min-height: 8;
    -fx-max-height: 8;
}
.theme-presets.property-sheet .insets-copy-button {
    -fx-background-color: null;
    -fx-background-radius: 0;
    -fx-min-width: 10;
    -fx-max-width: 10;
}
.theme-presets.property-sheet .insets-copy-button:hover {
    -fx-background-color: derive(-sb-content-background, -10%), -sb-content-background;
    -fx-background-insets: 0 1 0 1, 1 2 1 2;
}
.theme-presets.property-sheet .insets-copy-button:focused {
    -fx-background-color: -fx-focus-color, -fx-background;
    -fx-background-insets: 0 1 0 1, 1 2 1 2;
}

/*******************************************************************************
 * Property Sheet Panels - Rotate Editor                                       *
 ******************************************************************************/

.theme-presets.property-sheet .rotation-dial {
    -fx-background-radius: 100;
}
.theme-presets.property-sheet .rotation-handle {
    -fx-background-color: -sb-content-detail, derive(-sb-content-background, 10%);
    -fx-background-insets: 14 5 14 23, 15 6 15 24;
    -fx-background-radius: 20, 20;
}

/*******************************************************************************
 * Property Sheet Panels - Anchor Pane Constraints Editor                      *
 ******************************************************************************/

.theme-presets.property-sheet .ap-constraints-outer-border {
    -fx-border-color: -sb-line-art;
    -fx-border-insets: -1;
}
.theme-presets.property-sheet .ap-constraints-inner-border {
    -fx-border-color: -sb-line-art;
}
.theme-presets.property-sheet .ap-constraints-shape {
    -fx-background-color: -sb-line-art;
}
.theme-presets.property-sheet .ap-constraints-toggle:selected .ap-constraints-shape {
    -fx-background-color: -sb-line-art-accent;
}
.theme-presets.property-sheet .ap-constraints-toggle {
    -fx-background-color: transparent;
    -fx-padding: 3;
}
.theme-presets.property-sheet .ap-constraints-toggle:hover {
    -fx-cursor: hand;
    -fx-background-color: #cccccc, -fx-base;
    -fx-background-insets: 1, 2;
}
.theme-presets.property-sheet .ap-constraints-toggle:focused {
    -fx-background-color: -fx-focus-color, -fx-inner-border, -fx-base, -fx-faint-focus-color, -fx-base;
    -fx-background-insets: -0.2, 1, 2, -1.4, 2.6;
}

/*******************************************************************************
 * Property Sheet Panels - Multi Row Editor Buttons                         *
 ******************************************************************************/

.theme-presets.property-sheet .multi-row-plus-button {
    -fx-text-fill: -sb-content-detail;
    -fx-padding: 4 3 3 4;
    -fx-min-width: 17;
}

.theme-presets.property-sheet .multi-row-menubutton {
    -fx-padding: 4 5 3 1;
    -fx-min-width: 17;
}

/*******************************************************************************
 * Property Sheet Panels - Symbol Prefix @ @/ / # etc                          *
 ******************************************************************************/

.theme-presets.property-sheet .symbol-prefix {
    -fx-text-fill: -sb-content-text-dimmed;
    -fx-background-color: 
        -sb-text-control-outer, -sb-content-background;
    -fx-background-insets: 0, 1 0 1 1;
    -fx-padding: 4 3 3 5;
}

/*******************************************************************************
 * Property Sheet Panels - Suggestive ListView                                 *
 ******************************************************************************/

.theme-presets.property-sheet .auto-suggest-popup, 
.theme-presets.property-sheet .auto-suggest-popup .custom-menu-item {
    -fx-padding: 0;
}
.theme-presets.property-sheet .auto-suggest-popup .custom-menu-item .list-view {
    -fx-background-color: -sb-sharp-edge-lo, white;
    -fx-background-insets: 0, 1;
}

/*******************************************************************************
 * Property Sheet Panels - TableView                                           *
 ******************************************************************************/

.theme-presets.property-sheet .table-view {
    /* Constants used throughout the tableview. */
    /*-fx-table-header-border-color: -fx-box-border;*/
    -fx-table-cell-border-color: derive(-sb-text-control-outer, -15%);
}
.theme-presets.property-sheet .table-view {
    -fx-background-color: -sb-text-control-outer, -sb-content-background;
    -fx-background-insets: 0, 1;
    -fx-padding: 1;
}
.theme-presets.property-sheet .table-view .placeholder .label {
    -fx-text-fill: -sb-content-text-dimmed;
}
.table-cell {
    -fx-padding: 3 4 1 4;
}
.theme-presets.property-sheet .table-row-cell {
    -fx-background: -sb-text-control-inner;
    -fx-background-color: -fx-table-cell-border-color, -fx-background;
}
.theme-presets.property-sheet .table-row-cell:odd {
    -fx-background: -sb-text-control-inner;
}
.theme-presets.property-sheet .table-row-cell .text {
    -fx-fill: -sb-content-text;
}
.theme-presets.property-sheet .table-view .column-header,
.theme-presets.property-sheet .table-view .filler,
.theme-presets.property-sheet .table-view > .column-header-background > .show-hide-columns-button,
.theme-presets.property-sheet .table-view:constrained-resize .filler,
.theme-presets.property-sheet .tree-table-view:constrained-resize .filler {
    -fx-background-color: -sb-text-control-outer, derive(-sb-content-background, 10%);
    -fx-size: 1em;
    -fx-padding: 2 6 2 6;
}
.theme-presets.property-sheet .column-header .label {
    -fx-text-fill: -sb-content-text;
    -fx-font-weight: normal;
    -fx-alignment: CENTER_LEFT;
}
.theme-presets.property-sheet .table-view .virtual-flow .scroll-bar {
    -fx-background-color: -sb-text-control-outer, derive(-sb-content-background, -18%);
}
.theme-presets.property-sheet .table-view:row-selection .virtual-flow .corner {
    -fx-background-color: -sb-text-control-outer, derive(-sb-content-background, -18%);
    -fx-background-insets: 0, 0 0 1 1;
}
.theme-presets.property-sheet .table-view .arrow {
    -fx-background-color: -sb-content-detail;
}
/* Selected rows when table-view IS focused */
.theme-presets.property-sheet .table-view:focused .table-row-cell:filled:selected,
.theme-presets.property-sheet .table-view:focused .table-row-cell .table-cell:selected {
    -fx-background: -sb-selected-bar-color;
    -fx-table-cell-border-color: -sb-selected-bar-color;
}
/* Selected rows when table-view is NOT focused */
.theme-presets.property-sheet .table-view .table-row-cell:filled:selected,
.theme-presets.property-sheet .table-view .table-row-cell .table-cell:selected {
    -fx-background: -sb-selected-bar-unfocused-color;
    -fx-table-cell-border-color: -sb-selected-bar-unfocused-color;
}

/*******************************************************************************
 *                                                                             *
 * CSS Panel                                                                   *
 *                                                                             *
 ******************************************************************************/

.theme-presets.css-panel-background {
    -fx-background-color: -sb-sharp-edge-lo, -sb-content-background;
    -fx-background-insets: 0, 1 0 0 0;
}

/*******************************************************************************
 * CSS Panel - TableView                                                      *
 ******************************************************************************/

.theme-presets.SBKIT-css-panel .table-view .placeholder {
    -fx-background-color: -sb-content-background;
}
.theme-presets.SBKIT-css-panel .table-view .placeholder .label {
    -fx-text-fill: -sb-content-text-dimmed;
}
.theme-presets.SBKIT-css-panel .table-view .hyperlink {
    -fx-font-size: 0.875em;
    -fx-padding: 0 0 0 4;
    -fx-border-width: 0px;
    -fx-text-fill: -sb-content-text;
}
.theme-presets.SBKIT-css-panel .table-view .label {
    -fx-font-size: 0.875em;
    -fx-padding: 0 0 0 4;
    -fx-text-fill: -sb-content-text;
}
.theme-presets.SBKIT-css-panel .table-view {
    -fx-background-color: -sb-content-background;
    -fx-background-insets: 0;
    -fx-border-width: 0;
    -fx-padding: 0;
}
.theme-presets.SBKIT-css-panel .table-row-cell, .table-row-cell:selected {
    -fx-background-color: -sb-sharp-edge-lo, -sb-content-background;
    -fx-background-insets: 0, 0 0 1 0;
}
.theme-presets.SBKIT-css-panel .table-cell {
    -fx-background-color: -sb-sharp-edge-lo, -sb-content-background;
    -fx-background-insets: 0, 0 1 1 0;
    -fx-border-width: 0;
    -fx-padding: 0;
}
.theme-presets.SBKIT-css-panel .table-cell:last-visible {
    -fx-background-insets: 0, 0 0 1 0;
}
.theme-presets.SBKIT-css-panel .table-view .column-header {
    -fx-background-color: derive(-sb-sharp-edge-lo, -20%), -sb-sharp-edge-hi, -sb-flat;
    -fx-background-insets: -1 0 0 0, 0 1 1 0, 1 1 1 0;
}
.theme-presets.SBKIT-css-panel .table-view .column-header .label {
    -fx-text-fill: -sb-header-text;
    -fx-font-weight: normal;
    -fx-alignment: CENTER_LEFT;
    -fx-padding: 0 0 0 5;
}
.theme-presets.SBKIT-css-panel .table-view .filler {
    -fx-background-color: derive(-sb-sharp-edge-lo, -20%), -sb-sharp-edge-hi, -sb-flat;
    -fx-background-insets: -1 0 0 0, 0 1 1 0, 1 1 1 0;
}

.theme-presets.SBKIT-css-panel .notAppliedStyleLine .line {
    -fx-border-width: 0;
    -fx-background-color:black;
}

.theme-presets.SBKIT-css-panel .winner-background {
    -fx-background-color: -sb-css-winner-color;
    -fx-background-insets: 0 1 1 0;
}

.theme-presets.SBKIT-css-panel .note-label .text {
    -fx-font-size: 0.750em;
    -fx-fill: -sb-content-text-dimmed;
}

.theme-presets.SBKIT-css-panel .css-panel-message .text {
    /* Used for 'Multiple Selection' message */
    -fx-fill: -sb-content-text-dimmed;
    -fx-font-size: 0.875em;
}

/*******************************************************************************
 * CSS Panel - Picking Mode Toggle Buttons                                     *
 ******************************************************************************/

.theme-presets.SBKIT-css-panel .cursor-shape {
    -fx-shape: "M9.084,7.833c-1.709,0.25-3.242,0.734-3.242,0.734l2.783,4.892l-1.708,1L3.904,9.454C2.713,
    9.967,1.208,11.5,1.208,11.5L1,1 L9.084,7.833z";
    -fx-background-color: derive(-sb-header-detail, -15%);
    -fx-min-width: 8;
    -fx-max-width: 8;
    -fx-min-height: 13;
    -fx-max-height: 13;
}

.theme-presets.SBKIT-css-panel .css-cursor-shape {
    -fx-shape: "M9.083,7.833C4.833,8.25,1,11,1,11V1L9.083,7.833z M8,14H6v-3h2v-1H5v5h3V14z M12,
    12h-2v-1h2v-1H9v3h2v1H9v1h3V12z M16,12 h-2v-1h2v-1h-3v3h2v1h-2v1h3V12z";
    -fx-background-color: derive(-sb-header-detail, -15%);
    -fx-min-width: 15;
    -fx-max-width: 15;
    -fx-min-height: 15;
    -fx-max-height: 15;
}

.theme-presets.SBKIT-css-panel .toggle-button {
    -fx-background-color: -sb-sharp-edge-lo, -sb-sharp-edge-hi, -sb-flat;
    -fx-background-insets: 0, 0 0 1 0, 1 0 1 0;
    -fx-background-radius: 0;
    -fx-min-width: 25;
    -fx-min-height: 25;
}
.theme-presets.SBKIT-css-panel .toggle-button:selected {
    -fx-background-color: 
        derive(-sb-sharp-edge-lo, -25%), 
        derive(-sb-sharp-edge-lo, -10%), 
        linear-gradient(to bottom, derive(-sb-flat, -20% ) 0%, derive(-sb-flat, -12% ) 20%);
    -fx-background-insets: -1 0 0 0, 0, 0 1 1 1;
}
.theme-presets.SBKIT-css-panel .toggle-button:focused {
    -fx-background-color: 
        -fx-focus-color, 
        linear-gradient(to bottom, derive(-sb-flat, -20% ) 0%, derive(-sb-flat, -12% ) 20%);
    -fx-background-insets: -1 0 0 0, 0 1 1 1;
}
.theme-presets.SBKIT-css-panel .css-toggle-left {
    -fx-background-insets: 0, 0 0 1 1, 1 0 1 1;
}

/*******************************************************************************
 * CSS Panel - Stylelable Path                                                 *
 ******************************************************************************/

.theme-presets.SBKIT-css-panel .styleable-path {
    -fx-background-color: -sb-sharp-edge-lo, -sb-sharp-edge-hi, -sb-flat;
    -fx-background-insets: -1 0 0 0, 0 0 1 0, 1 0 1 0;
}
.theme-presets.SBKIT-css-panel .styleable-path > .label {
    -fx-font-size: 0.875em;
    -fx-text-fill: -sb-header-text;
    -fx-padding: 0 12 0 8;
}
.theme-presets.SBKIT-css-panel .styleable-path .hyperlink {
    -fx-font-size: 0.875em;
    -fx-text-fill: -sb-header-text;
    -fx-padding: 0 0 0 0;
    -fx-border-width: 0;
}
.theme-presets.SBKIT-css-panel .styleable-path .button {
    -fx-background-color: -sb-sharp-edge-lo, derive(-sb-sharp-edge-hi, 10%);
    -fx-background-insets: 0, 1;
    -fx-background-radius: 100;
    -fx-min-width: 16;
    -fx-max-width: 16;
    -fx-min-height: 16;
    -fx-max-height: 16;
}
.theme-presets.SBKIT-css-panel .styleable-path .button:hover {
    -fx-background-color: derive(-sb-sharp-edge-lo, -10%), derive(-sb-sharp-edge-hi, 40%);
}
.theme-presets.SBKIT-css-panel .styleable-path-button-shape {
    -fx-shape: "M 0 -3.5 v 7 l 4 -3.5 z";
    -fx-background-color: -sb-header-detail;
    -fx-min-width: 5;
    -fx-max-width: 5;
    -fx-min-height: 6;
    -fx-max-height: 6;
}
.theme-presets.SBKIT-css-panel .styleable-path-optional-label .text {
    -fx-fill: -sb-header-text-dimmed;
}

/*******************************************************************************
 * CSS Panel - Lookup Button                                                   *
 ******************************************************************************/

.theme-presets.SBKIT-css-panel .lookup-button {
    -fx-background-color: transparent;
    -fx-background-insets: 0;
}
.theme-presets.SBKIT-css-panel .lookup-button .label {
    -fx-padding: 0;
}
.theme-presets.SBKIT-css-panel .lookup-button .arrow-button {
    -fx-padding: 0 3 2 2;
}
.theme-presets.SBKIT-css-panel .lookup-button .arrow-button > .arrow {
    -fx-padding: 0 0.231em 0.154em 0.154em;
    -fx-background-color: derive(-sb-header-detail, -20%);
    -fx-effect: null;
}
.theme-presets.SBKIT-css-panel .lookup-button:hover .arrow-button {
    -fx-background-color: black, white;
    -fx-background-insets: 0 0 3 0, 1 1 4 0;
    -fx-background-radius: 0, 0;
}
.theme-presets.SBKIT-css-panel .lookup-button .custom-menu-item .tree-view {
    -fx-background-color: derive(-sb-sharp-edge-lo, -20%);
    -fx-background-insets: 0;
}
.theme-presets.SBKIT-css-panel .lookup-button .custom-menu-item .tree-view .tree-cell {
    -fx-background-color: -sb-content-background;
}
.theme-presets.SBKIT-css-panel .lookup-button .custom-menu-item .tree-view .tree-cell .tree-disclosure-node .arrow {
    -fx-background-color: -sb-content-detail;
}

/*******************************************************************************
 * CSS Panel - Cog Button                                                      *
 ******************************************************************************/

.theme-presets.SBKIT-css-panel .css-panel-cog-menubutton {
    -fx-background-color: transparent;
    -fx-background-radius: 0;
    -fx-graphic-text-gap: 0;
    -fx-min-width: 16;
    -fx-max-width: 16;
    -fx-min-height: 12;
    -fx-max-height: 12;
}
.theme-presets.SBKIT-css-panel .css-panel-cog-menubutton:hover {
    -fx-background-color: derive(-sb-sharp-edge-lo, 20%), -sb-css-winner-color;
    -fx-background-insets: 0, 1;
}
.theme-presets.SBKIT-css-panel .css-panel-cog-menubutton:focused {
    -fx-background-color: -fx-focus-color, -fx-background;
}
.theme-presets.SBKIT-css-panel .css-panel-cog-menubutton > .arrow-button {
    visibility: hidden;
    -fx-padding: 0;
}
.theme-presets.SBKIT-css-panel .css-panel-cog-menubutton > .arrow {
    -fx-shape: null;
    -fx-padding: 0;
}


<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/content/gesture/mouse/EditCurveGesture.java<|end_filename|>
/*
 * Copyright (c) 2018, Gluon and/or its affiliates.
 * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.kit.editor.panel.content.gesture.mouse;

import com.oracle.javafx.scenebuilder.kit.editor.EditorController;
import com.oracle.javafx.scenebuilder.kit.editor.job.atomic.ModifyObjectJob;
import com.oracle.javafx.scenebuilder.kit.editor.panel.content.ContentPanelController;
import com.oracle.javafx.scenebuilder.kit.editor.panel.content.HudWindowController;
import com.oracle.javafx.scenebuilder.kit.editor.panel.content.driver.curve.AbstractCurveEditor;
import com.oracle.javafx.scenebuilder.kit.editor.panel.content.driver.handles.AbstractHandles;
import com.oracle.javafx.scenebuilder.kit.editor.panel.content.guides.EditCurveGuideController;
import com.oracle.javafx.scenebuilder.kit.editor.panel.content.util.CardinalPoint;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMDocument;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMInstance;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMObject;
import com.oracle.javafx.scenebuilder.kit.metadata.Metadata;
import com.oracle.javafx.scenebuilder.kit.metadata.property.ValuePropertyMetadata;
import com.oracle.javafx.scenebuilder.kit.metadata.util.DesignHierarchyMask;
import com.oracle.javafx.scenebuilder.kit.metadata.util.PropertyName;
import java.util.ArrayList;
import java.util.EnumMap;
import javafx.geometry.Point2D;
import javafx.scene.Node;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;

import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import javafx.scene.Cursor;
import javafx.scene.input.MouseEvent;
import javafx.scene.shape.Circle;

/**
 *
 * 
 */
public class EditCurveGesture extends AbstractMouseGesture {
    
    private final FXOMInstance fxomInstance;
    
    private final AbstractCurveEditor<?> editor;
    private EditCurveGuideController controller;

    private boolean straightAnglesMode = false;
    
    private static final PropertyName POINTS_NAME = new PropertyName("points"); //NOI18N
    private static final int MAX_POINTS_HUD = 24;
    
    public enum Tunable {
        START,
        END,
        CONTROL1,
        CONTROL2,
        VERTEX,
        SIDE;
    }
    
    private final EnumMap<Tunable, Integer> tunableMap = new EnumMap<>(Tunable.class);

    public EditCurveGesture(ContentPanelController contentPanelController, FXOMInstance fxomInstance, Tunable tunable) {
        super(contentPanelController);
        assert contentPanelController.lookupDriver(fxomInstance) != null;
        assert fxomInstance.getSceneGraphObject() instanceof Node;
        this.fxomInstance = fxomInstance;
        tunableMap.put(tunable, -1);
        editor = contentPanelController.lookupDriver(fxomInstance).makeCurveEditor(fxomInstance);
    }

    public EnumMap<Tunable, Integer> getTunableMap() {
        return tunableMap;
    }
    
    /*
     * AbstractMouseGesture
     */
    
    private boolean inserted, removed;
    private Point2D insertionPoint;
    
    @Override
    protected void mousePressed() {
        final MouseEvent mousePressedEvent = getMousePressedEvent();
        inserted = false;
        removed = false;
        if (tunableMap.containsKey(Tunable.SIDE) && mousePressedEvent.isShortcutDown()) {
            final double hitX = mousePressedEvent.getSceneX();
            final double hitY = mousePressedEvent.getSceneY();
            insertionPoint = editor.getSceneGraphObject().sceneToLocal(hitX, hitY, true);
            inserted = true;
        } else if (tunableMap.containsKey(Tunable.VERTEX) && mousePressedEvent.isShortcutDown()) {
            removed = true;
        } 
        updateHandle(true);
    }

    @Override
    protected void mouseDragStarted() {
        assert editor != null;
        assert editor.getSceneGraphObject() == fxomInstance.getSceneGraphObject();

        controller = editor.createController(tunableMap);

        final double hitX = getLastMouseEvent().getSceneX();
        final double hitY = getLastMouseEvent().getSceneY();
        final Set<FXOMObject> pickExcludes = new HashSet<>();
        pickExcludes.add(fxomInstance);

        FXOMObject hitParent = contentPanelController.pick(hitX, hitY, pickExcludes);
        if (hitParent == null) {
            final FXOMDocument fxomDocument
                    = contentPanelController.getEditorController().getFxomDocument();
            hitParent = fxomDocument.getFxomRoot();
        }

        assert hitParent != null;

        DesignHierarchyMask hitParentMask = new DesignHierarchyMask(hitParent);
        assert hitParentMask.isFreeChildPositioning();

        for (int i = 0, c = hitParentMask.getSubComponentCount(); i < c; i++) {
            final FXOMObject child = hitParentMask.getSubComponentAtIndex(i);
            final boolean isNode = child.getSceneGraphObject() instanceof Node;
            if (isNode && child != fxomInstance) {
                final Node childNode = (Node) child.getSceneGraphObject();
                controller.addSampleBounds(childNode);
            }
        }

        assert hitParent.getSceneGraphObject() instanceof Node;
        final Node hitParentNode = (Node) hitParent.getSceneGraphObject();
        controller.addSampleBounds(hitParentNode);
        
        setupAndOpenHudWindow();
        
        mouseDragged();
    }

    @Override
    protected void mouseDragged() {
        contentPanelController.getHudWindowController().updatePopupLocation();
        updateCurvePosition();
    }

    @Override
    protected void mouseDragEnded() {
        final Map<PropertyName, Object> changeMap = editor.getChangeMap();
        List<Double> points = null;
        if (editor.getPoints() != null) {
            points = new ArrayList<>(editor.getPoints());
        }
        userDidCancel();

        final Metadata metadata = Metadata.getMetadata();
        final Map<ValuePropertyMetadata, Object> metaValueMap = new HashMap<>();
        for (Map.Entry<PropertyName,Object> e : changeMap.entrySet()) {
            final ValuePropertyMetadata vpm = metadata.queryValueProperty(fxomInstance, e.getKey());
            assert vpm != null;
            metaValueMap.put(vpm, e.getValue());
        }
        if (!changeMap.isEmpty()) {
            final EditorController editorController
                    = contentPanelController.getEditorController();
            for (Map.Entry<ValuePropertyMetadata, Object> e : metaValueMap.entrySet()) {
                final ModifyObjectJob job = new ModifyObjectJob(
                        fxomInstance,
                        e.getKey(),
                        e.getValue(),
                        editorController,
                        "Edit");
                if (job.isExecutable()) {
                    editorController.getJobManager().push(job);
                }
            }
        }
        
        if (points != null) {
            final EditorController editorController
                    = contentPanelController.getEditorController();
            final ValuePropertyMetadata pointsMeta 
                = metadata.queryValueProperty(fxomInstance, POINTS_NAME);
            final ModifyObjectJob job = new ModifyObjectJob(fxomInstance,
                        pointsMeta,
                        points,
                        editorController);
            if (job.isExecutable()) {
                editorController.getJobManager().push(job);
            }
        }
    }

    @Override
    protected void mouseReleased() {
        updateHandle(false);
        if (removed || inserted) {
            if (removed) {
                editor.removePoint(tunableMap);
            } else if (inserted) {
                editor.addPoint(tunableMap, insertionPoint.getX(), insertionPoint.getY());
            }

            List<Double> points = null;
            if (editor.getPoints() != null) {
                points = editor.getPoints().stream().collect(Collectors.toList());
            }
            userDidCancel();
            
            final Metadata metadata = Metadata.getMetadata();
            if (points != null) {
                final EditorController editorController
                        = contentPanelController.getEditorController();
                final ValuePropertyMetadata pointsMeta 
                    = metadata.queryValueProperty(fxomInstance, POINTS_NAME);
                final ModifyObjectJob job = new ModifyObjectJob(fxomInstance,
                            pointsMeta,
                            points,
                            editorController);
                if (job.isExecutable()) {
                    editorController.getJobManager().push(job);
                }
            }
        }
    }
    
    @Override
    protected void keyEvent(KeyEvent e) {
        if (e.getCode() == KeyCode.SHIFT) {
            if (e.getEventType() == KeyEvent.KEY_PRESSED) {
                straightAnglesMode = true;
            } else if (e.getEventType() == KeyEvent.KEY_RELEASED) {
                straightAnglesMode = false;
            }
            mouseDragged();
        }
   }

    @Override
    protected void userDidCancel() {
        editor.revertToOriginalState();
        editor.getSceneGraphObject().getParent().layout();
        contentPanelController.getHudWindowController().closeWindow();
    }
    
    private void updateCurvePosition() {
        if (editor == null || controller == null) {
            return;
        }
        final Node sceneGraphObject = editor.getSceneGraphObject();
        sceneGraphObject.getParent().layout();

        final double currentSceneX = getLastMouseEvent().getSceneX();
        final double currentSceneY = getLastMouseEvent().getSceneY();
        Point2D current = new Point2D(currentSceneX, currentSceneY);
        
        if (straightAnglesMode) {
            current = controller.makeStraightAngles(current);
        } else {
            current = controller.correct(current);
        }

        current = sceneGraphObject.sceneToLocal(current.getX(), current.getY(), true);
        editor.moveTunable(tunableMap, current.getX(), current.getY());
        sceneGraphObject.getParent().layout();
        
        updateHudWindow();
    }
    
    private void updateHandle(boolean value) {
        Node hitNode = (Node) getMousePressedEvent().getTarget();
        AbstractHandles<?> hitHandles = AbstractHandles.lookupHandles(hitNode);
        while (hitHandles == null && hitNode.getParent() != null) {
            hitNode = hitNode.getParent();
            hitHandles = AbstractHandles.lookupHandles(hitNode);
        }
        if (hitNode instanceof Circle) {
            if (removed) {
                hitNode.setCursor(value ? Cursor.CROSSHAIR : Cursor.OPEN_HAND);
            } else {
                hitNode.setCursor(value ? Cursor.CLOSED_HAND : Cursor.OPEN_HAND);
            }
        } 
    }
    
    private void setupAndOpenHudWindow() {
        final HudWindowController hudWindowController = contentPanelController.getHudWindowController();
        
        final int propertiesCount = editor.getPropertyNames().size();
        final int pointsCount = editor.getPoints() != null ? Math.min(MAX_POINTS_HUD, editor.getPoints().size()) : 0;
        hudWindowController.setRowCount(propertiesCount + pointsCount);
        
        final List<PropertyName> sizePropertyNames = editor.getPropertyNames();
        for (int i = 0; i < propertiesCount; i++) {
            final PropertyName pn = sizePropertyNames.get(i);
            hudWindowController.setNameAtRowIndex(pn.getName() + ":", i);
        }
        
        for (int i = 0; i < pointsCount / 2; i++) {
            hudWindowController.setNameAtRowIndex("" + (i + 1) + ".X:", 2 * i + propertiesCount);
            hudWindowController.setNameAtRowIndex("" + (i + 1) + ".Y:", 2 * i + 1 + propertiesCount);
        }
        
        updateHudWindow();
        
        hudWindowController.setRelativePosition(CardinalPoint.E);
        hudWindowController.openWindow(editor.getSceneGraphObject());
    }
    
    private void updateHudWindow() {
        final HudWindowController hudWindowController = contentPanelController.getHudWindowController();
        final List<PropertyName> sizePropertyNames = editor.getPropertyNames();
        final int propertiesCount = sizePropertyNames.size();
        
        for (int i = 0; i < propertiesCount; i++) {
            final PropertyName pn = sizePropertyNames.get(i);
            final String value = String.valueOf(editor.getValue(pn));
            hudWindowController.setValueAtRowIndex(value, i);
        }
        
        // Limit added points to grid
        // TODO: Add another column to the grid
        final int pointsCount = editor.getPoints() != null ? Math.min(MAX_POINTS_HUD, editor.getPoints().size()) : 0;
        if (pointsCount > 0) {
            for (int i = 0; i < pointsCount; i++) {
                hudWindowController.setValueAtRowIndex(String.format("%.3f", editor.getPoints().get(i)), i + propertiesCount);
            }
        }
        
    }
    
}

<|start_filename|>kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/inspector/editors/CharsetEditor.java<|end_filename|>
/*
 * Copyright (c) 2017, Gluon and/or its affiliates.
 * All rights reserved. Use is subject to license terms.
 *
 * This file is available and licensed under the following license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - 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.
 *  - Neither the name of Oracle Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 */
package com.oracle.javafx.scenebuilder.kit.editor.panel.inspector.editors;

import com.oracle.javafx.scenebuilder.kit.metadata.property.ValuePropertyMetadata;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;

import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.*;

/**
 * Editor for setting a charset property of an included element.
 */
public class CharsetEditor extends AutoSuggestEditor {

    private final Map<String, Charset> availableCharsets;

    public CharsetEditor(ValuePropertyMetadata propMeta, Set<Class<?>> selectedClasses, Map<String, Charset> availableCharsets) {
        super(propMeta, selectedClasses, new ArrayList<>(availableCharsets.keySet()), Type.ALPHA);
        this.availableCharsets = availableCharsets;
        initialize();
    }

    private void initialize() {
        EventHandler<ActionEvent> onActionListener = event -> {
            if (isHandlingError()) {
                // Event received because of focus lost due to error dialog
                return;
            }
            Object value = getValue();
            if ((value != null) && isValidValue((String) value)) {
                userUpdateValueProperty(value);
                getTextField().selectAll();
            } else {
                handleInvalidValue(getTextField().getText());
            }
        };
        setTextEditorBehavior(this, textField, onActionListener);
    }

    @Override
    public Object getValue() {
        String val = getTextField().getText();
        if (val.isEmpty()) {
            val = "";
            getTextField().setText(val);
            return String.valueOf(val);
        }
        Object constantValue = this.availableCharsets.get(val.toUpperCase(Locale.ROOT));
        if (constantValue != null) {
            val = EditorUtils.valAsStr(constantValue);
        }
        return String.valueOf(val);
    }

    @Override
    public void setValue(Object value) {
        Object changedValue = value;
        setValueGeneric(changedValue);
        if (isSetValueDone()) {
            return;
        }
        // Get the corresponding constant if any
        for (Map.Entry<String, Charset> entry : this.availableCharsets.entrySet()) {
            if (changedValue.equals(entry.getValue())) {
                changedValue = entry.getKey();
            }
        }
        getTextField().setText(EditorUtils.valAsStr(changedValue));
    }

    @Override
    public void requestFocus() {
        EditorUtils.doNextFrame(() -> getTextField().requestFocus());
    }

    public void reset(ValuePropertyMetadata propMeta, Set<Class<?>> selectedClasses,
                      Map<String, Charset> constants) {
        super.reset(propMeta, selectedClasses, new ArrayList<>(constants.keySet()));
    }

    /**
     * For the performance, it is much better to use standard charsets, than Charset.availableCharsets().
     * Need to be static!
     *
     * @return Map with the standard charsets.
     */
    public static Map<String, Charset> getStandardCharsets() {
        Map<String, Charset> charsets = new HashMap<>();
        charsets.put("UTF-8", StandardCharsets.UTF_8);
        charsets.put("UTF-16", StandardCharsets.UTF_16);
        charsets.put("UTF-16BE", StandardCharsets.UTF_16BE);
        charsets.put("UTF-16LE", StandardCharsets.UTF_16LE);
        charsets.put("US-ASCII", StandardCharsets.US_ASCII);
        charsets.put("ISO-8859-1", StandardCharsets.ISO_8859_1);
        return charsets;
    }

    private boolean isValidValue(String value) {
        boolean valid = false;
        if (this.availableCharsets.containsKey(value)) {
            valid = true;
        }
        return valid;
    }
}