1 /*
   2  * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
   3  * All rights reserved. Use is subject to license terms.
   4  *
   5  * This file is available and licensed under the following license:
   6  *
   7  * Redistribution and use in source and binary forms, with or without
   8  * modification, are permitted provided that the following conditions
   9  * are met:
  10  *
  11  *  - Redistributions of source code must retain the above copyright
  12  *    notice, this list of conditions and the following disclaimer.
  13  *  - Redistributions in binary form must reproduce the above copyright
  14  *    notice, this list of conditions and the following disclaimer in
  15  *    the documentation and/or other materials provided with the distribution.
  16  *  - Neither the name of Oracle Corporation nor the names of its
  17  *    contributors may be used to endorse or promote products derived
  18  *    from this software without specific prior written permission.
  19  *
  20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  23  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  24  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  31  */
  32 package com.oracle.javafx.scenebuilder.kit.editor.panel.content.driver.relocater;
  33 
  34 import com.oracle.javafx.scenebuilder.kit.metadata.util.PropertyName;
  35 import com.oracle.javafx.scenebuilder.kit.util.MathUtils;
  36 import java.util.ArrayList;
  37 import java.util.HashMap;
  38 import java.util.List;
  39 import java.util.Map;
  40 import java.util.Objects;
  41 import javafx.geometry.Bounds;
  42 import javafx.scene.Node;
  43 import javafx.scene.layout.AnchorPane;
  44 
  45 /**
  46  *
  47  *
  48  */
  49 public class AnchorPaneRelocater extends AbstractRelocater<AnchorPane> {
  50 
  51     private final double originalLayoutX;
  52     private final double originalLayoutY;
  53     private final Double originalLeftAnchor;
  54     private final Double originalRightAnchor;
  55     private final Double originalTopAnchor;
  56     private final Double originalBottomAnchor;
  57     private final PropertyName layoutXName = new PropertyName("layoutX");
  58     private final PropertyName layoutYName = new PropertyName("layoutY");
  59     private final PropertyName leftAnchorName = new PropertyName("leftAnchor", AnchorPane.class);
  60     private final PropertyName rightAnchorName = new PropertyName("rightAnchor", AnchorPane.class);
  61     private final PropertyName topAnchorName = new PropertyName("topAnchor", AnchorPane.class);
  62     private final PropertyName bottomAnchorName = new PropertyName("bottomAnchor", AnchorPane.class);
  63     private final List<PropertyName> propertyNames = new ArrayList<>();
  64 
  65     public AnchorPaneRelocater(Node sceneGraphObject) {
  66         super(sceneGraphObject, AnchorPane.class);
  67         this.originalLayoutX = sceneGraphObject.getLayoutX();
  68         this.originalLayoutY = sceneGraphObject.getLayoutY();
  69         this.originalLeftAnchor = AnchorPane.getLeftAnchor(sceneGraphObject);
  70         this.originalRightAnchor = AnchorPane.getRightAnchor(sceneGraphObject);
  71         this.originalTopAnchor = AnchorPane.getTopAnchor(sceneGraphObject);
  72         this.originalBottomAnchor = AnchorPane.getBottomAnchor(sceneGraphObject);
  73 
  74         if ((originalLeftAnchor == null) && (originalRightAnchor == null)) {
  75             propertyNames.add(layoutXName);
  76         } else {
  77             if (originalLeftAnchor != null) {
  78                 propertyNames.add(leftAnchorName);
  79                 propertyNames.add(layoutXName);
  80             }
  81             if (originalRightAnchor != null) {
  82                 propertyNames.add(rightAnchorName);
  83             }
  84         }
  85         if ((originalTopAnchor == null) && (originalBottomAnchor == null)) {
  86             propertyNames.add(layoutYName);
  87         } else {
  88             if (originalTopAnchor != null) {
  89                 propertyNames.add(topAnchorName);
  90                 propertyNames.add(layoutYName);
  91             }
  92             if (originalBottomAnchor != null) {
  93                 propertyNames.add(bottomAnchorName);
  94             }
  95         }
  96     }
  97 
  98 
  99     /*
 100      * AbstractRelocater
 101      */
 102     @Override
 103     public void moveToLayoutX(double newLayoutX, Bounds newLayoutBounds) {
 104         if ((originalLeftAnchor == null) && (originalRightAnchor == null)) {
 105             sceneGraphObject.setLayoutX(Math.round(newLayoutX));
 106         } else {
 107             final Bounds parentLayoutBounds = sceneGraphObject.getParent().getLayoutBounds();
 108             if (originalLeftAnchor != null) {
 109                 final double leftAnchor = computeLeftAnchor(parentLayoutBounds, newLayoutBounds, newLayoutX);
 110                 AnchorPane.setLeftAnchor(sceneGraphObject, (double)Math.round(leftAnchor));
 111             }
 112             if (originalRightAnchor != null) {
 113                 final double rightAnchor = computeRightAnchor(parentLayoutBounds, newLayoutBounds, newLayoutX);
 114                 AnchorPane.setRightAnchor(sceneGraphObject, (double)Math.round(rightAnchor));
 115             }
 116         }
 117     }
 118 
 119     @Override
 120     public void moveToLayoutY(double newLayoutY, Bounds newLayoutBounds) {
 121         if ((originalTopAnchor == null) && (originalBottomAnchor == null)) {
 122             sceneGraphObject.setLayoutY(Math.round(newLayoutY));
 123         } else {
 124             final Bounds parentLayoutBounds = sceneGraphObject.getParent().getLayoutBounds();
 125             if (originalTopAnchor != null) {
 126                 final double topAnchor = computeTopAnchor(parentLayoutBounds, newLayoutBounds, newLayoutY);
 127                 AnchorPane.setTopAnchor(sceneGraphObject, (double)Math.round(topAnchor));
 128             }
 129             if (originalBottomAnchor != null) {
 130                 final double bottomAnchor = computeBottomAnchor(parentLayoutBounds, newLayoutBounds, newLayoutY);
 131                 AnchorPane.setBottomAnchor(sceneGraphObject, (double)Math.round(bottomAnchor));
 132             }
 133         }
 134     }
 135 
 136     @Override
 137     public void revertToOriginalLocation() {
 138         sceneGraphObject.setLayoutX(originalLayoutX);
 139         sceneGraphObject.setLayoutY(originalLayoutY);
 140         AnchorPane.setLeftAnchor(sceneGraphObject, originalLeftAnchor);
 141         AnchorPane.setRightAnchor(sceneGraphObject, originalRightAnchor);
 142         AnchorPane.setTopAnchor(sceneGraphObject, originalTopAnchor);
 143         AnchorPane.setBottomAnchor(sceneGraphObject, originalBottomAnchor);
 144     }
 145 
 146     @Override
 147     public List<PropertyName> getPropertyNames() {
 148         return propertyNames;
 149     }
 150 
 151     @Override
 152     public Object getValue(PropertyName propertyName) {
 153         assert propertyName != null;
 154         assert propertyNames.contains(propertyName) : "propertyName=" + propertyName;
 155 
 156         final Object result;
 157         if (propertyName.equals(layoutXName)) {
 158             result = sceneGraphObject.getLayoutX();
 159         } else if (propertyName.equals(layoutYName)) {
 160             result = sceneGraphObject.getLayoutY();
 161         } else if (propertyName.equals(leftAnchorName)) {
 162             result = AnchorPane.getLeftAnchor(sceneGraphObject);
 163         } else if (propertyName.equals(rightAnchorName)) {
 164             result = AnchorPane.getRightAnchor(sceneGraphObject);
 165         } else if (propertyName.equals(topAnchorName)) {
 166             result = AnchorPane.getTopAnchor(sceneGraphObject);
 167         } else if (propertyName.equals(bottomAnchorName)) {
 168             result = AnchorPane.getBottomAnchor(sceneGraphObject);
 169         } else {
 170             // Emergency code
 171             result = null;
 172         }
 173 
 174         return result;
 175     }
 176 
 177     @Override
 178     public Map<PropertyName, Object> getChangeMap() {
 179         final Map<PropertyName, Object> result = new HashMap<>();
 180         if ((originalLeftAnchor == null) && (originalRightAnchor == null)) {
 181             if (MathUtils.equals(sceneGraphObject.getLayoutX(), originalLayoutX) == false) {
 182                 result.put(layoutXName, sceneGraphObject.getLayoutX());
 183             }
 184         } else {
 185             if (Objects.equals(AnchorPane.getLeftAnchor(sceneGraphObject), originalLeftAnchor) == false) {
 186                 result.put(leftAnchorName, AnchorPane.getLeftAnchor(sceneGraphObject));
 187                 result.put(layoutXName, sceneGraphObject.getLayoutX());
 188             }
 189             if (Objects.equals(AnchorPane.getRightAnchor(sceneGraphObject), originalRightAnchor) == false) {
 190                 result.put(rightAnchorName, AnchorPane.getRightAnchor(sceneGraphObject));
 191             }
 192         }
 193         if ((originalTopAnchor == null) && (originalBottomAnchor == null)) {
 194             if (MathUtils.equals(sceneGraphObject.getLayoutY(), originalLayoutY) == false) {
 195                 result.put(layoutYName, sceneGraphObject.getLayoutY());
 196             }
 197         } else {
 198             if (Objects.equals(AnchorPane.getTopAnchor(sceneGraphObject), originalTopAnchor) == false) {
 199                 result.put(topAnchorName, AnchorPane.getTopAnchor(sceneGraphObject));
 200                 result.put(layoutYName, sceneGraphObject.getLayoutY());
 201             }
 202             if (Objects.equals(AnchorPane.getBottomAnchor(sceneGraphObject), originalBottomAnchor) == false) {
 203                 result.put(bottomAnchorName, AnchorPane.getBottomAnchor(sceneGraphObject));
 204             }
 205         }
 206         return result;
 207     }
 208 
 209 
 210 
 211     /*
 212      * Public (static)
 213      */
 214 
 215     public static double computeLeftAnchor(Bounds parentBounds, Bounds childBounds, double targetLayoutX) {
 216         /*
 217          *       o parent origin
 218          *
 219          *              +---------------------------------------------------+
 220          *              |                                                   |
 221          *              |                  o child origin                   |
 222          *              |                                                   |
 223          *              |                       +---------------------+     |
 224          *              |                       |                     |     |
 225          *              |                       |                     |     |
 226          *              |                       |                     |     |
 227          *              |                       +---------------------+     |
 228          *              |                                                   |
 229          *              |                                                   |
 230          *              +------------------+----+---------------------------+
 231          *              .                  .    .
 232          *              .                  .    .
 233          *              .                  .    .
 234          *              x0                 .    x1
 235          *                                 .
 236          *                           targetLayoutX
 237          *
 238          *  result = x1 - x0
 239          */
 240 
 241         final double x0 = parentBounds.getMinX();
 242         final double x1 = parentBounds.getMinX() + targetLayoutX + childBounds.getMinX();
 243         return x1 - x0;
 244     }
 245 
 246     public static double computeRightAnchor(Bounds parentBounds, Bounds childBounds, double targetLayoutX) {
 247         /*
 248          *       o parent origin
 249          *
 250          *              +---------------------------------------------------+
 251          *              |                                                   |
 252          *              |                  o child origin                   |
 253          *              |                                                   |
 254          *              |                       +---------------------+     |
 255          *              |                       |                     |     |
 256          *              |                       |                     |     |
 257          *              |                       |                     |     |
 258          *              |                       +---------------------+     |
 259          *              |                                                   |
 260          *              |                                                   |
 261          *              +------------------+--------------------------------+
 262          *                                 .                          .     .
 263          *                                 .                          .     .
 264          *                                 .                          .     .
 265          *                                 .                          x0    x1
 266          *                                 .
 267          *                           targetLayoutX
 268          *
 269          *  result = x1 - x0
 270          */
 271 
 272         final double x0 = parentBounds.getMinX() + targetLayoutX + childBounds.getMaxX();
 273         final double x1 = parentBounds.getMaxX();
 274         return x1 - x0;
 275     }
 276 
 277     public static double computeTopAnchor(Bounds parentBounds, Bounds childBounds, double targetLayoutY) {
 278         final double y0 = parentBounds.getMinY();
 279         final double y1 = parentBounds.getMinY() + targetLayoutY + childBounds.getMinY();
 280         return y1 - y0;
 281     }
 282 
 283     public static double computeBottomAnchor(Bounds parentBounds, Bounds childBounds, double targetLayoutY) {
 284         final double y0 = parentBounds.getMinY() + targetLayoutY + childBounds.getMaxY();
 285         final double y1 = parentBounds.getMaxY();
 286         return y1 - y0;
 287     }
 288 }