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 
  33 package com.oracle.javafx.scenebuilder.kit.editor.panel.content.driver.resizer;
  34 
  35 import com.oracle.javafx.scenebuilder.kit.metadata.util.PropertyName;
  36 import com.oracle.javafx.scenebuilder.kit.util.Deprecation;
  37 import com.oracle.javafx.scenebuilder.kit.util.MathUtils;
  38 import java.util.HashMap;
  39 import java.util.Map;
  40 import javafx.geometry.Bounds;
  41 import javafx.scene.layout.ColumnConstraints;
  42 import javafx.scene.layout.GridPane;
  43 import javafx.scene.layout.Region;
  44 
  45 /**
  46  *
  47  */
  48 public class GridPaneColumnResizer {
  49 
  50     private static final PropertyName minWidthName     = new PropertyName("minWidth"); //NOI18N
  51     private static final PropertyName prefWidthName    = new PropertyName("prefWidth"); //NOI18N
  52     private static final PropertyName maxWidthName     = new PropertyName("maxWidth"); //NOI18N
  53     private static final PropertyName percentWidthName = new PropertyName("percentWidth"); //NOI18N
  54 
  55     private final GridPane gridPane;
  56     private final int columnIndex;
  57     private final ColumnSizing originalSizing; // Column at columnIndex
  58     private final ColumnSizing originalSizingNext; // Column at columnIndex+1
  59     private final boolean usePercentSizing;
  60     private final double x1, x2, x3, x4, xm;
  61 
  62     public GridPaneColumnResizer(GridPane gridPane, int columnIndex) {
  63         assert gridPane != null;
  64         assert columnIndex >= 0;
  65         assert columnIndex+1 < gridPane.getColumnConstraints().size();
  66 
  67         this.gridPane = gridPane;
  68         this.columnIndex = columnIndex;
  69         this.originalSizing
  70                 = new ColumnSizing(gridPane.getColumnConstraints().get(columnIndex));
  71         this.originalSizingNext
  72                 = new ColumnSizing(gridPane.getColumnConstraints().get(columnIndex+1));
  73         this.usePercentSizing
  74                 = countPercentWidths() == Deprecation.getGridPaneColumnCount(gridPane);
  75 
  76         //
  77         //         x1                x2         x3             xm        x4
  78         //       --+-----------------+----------+--------------+---------+--
  79         //         |      col n      |  hgap n  |         col n+1        |
  80         //         |                 |          |                        |
  81         //
  82         //       Range for moving x2 is [x1, xm]
  83         //       With (x4 - xm) == (x3 - x2)
  84         //
  85 
  86         final Bounds cellBounds
  87                 = Deprecation.getGridPaneCellBounds(gridPane, columnIndex, 0);
  88         final Bounds nextBounds
  89                 = Deprecation.getGridPaneCellBounds(gridPane, columnIndex+1, 0);
  90         x1 = cellBounds.getMinX();
  91         x2 = cellBounds.getMaxX();
  92         x3 = nextBounds.getMinX();
  93         x4 = nextBounds.getMaxX();
  94         xm = x4 - (x3 - x2);
  95         assert x1 <= xm;
  96     }
  97 
  98     public GridPane getSceneGraphObject() {
  99         return gridPane;
 100     }
 101 
 102     public int getColumnIndex() {
 103         return columnIndex;
 104     }
 105 
 106     public void updateWidth(double dx) {
 107 
 108         // Clamp x2 + dx in [x1, xm]
 109         final double newX2 = Math.max(x1, Math.min(xm, x2 + dx));
 110         final double newX3 = newX2 + (x3 - x2);
 111         final double newCellWidth = newX2 - x1;
 112         final double newNextWidth = x4 - newX3;
 113 
 114 //        assert (newCellWidth+newNextWidth) == (downColWidths[colIndex]+downColWidths[colIndex+1]) :
 115 //                "newCellWidth+newNextWidth=" +  (newCellWidth+newNextWidth) + ", " +
 116 //                "downColWidths[colIndex]+downColWidths[colIndex+1]=" +
 117 //                (downColWidths[colIndex]+downColWidths[colIndex+1]);
 118 
 119         // Updates width of columns at columnIndex and columnIndex+1
 120         final ColumnConstraints cc = gridPane.getColumnConstraints().get(columnIndex);
 121         final ColumnConstraints ccNext = gridPane.getColumnConstraints().get(columnIndex+1);
 122 
 123         if (usePercentSizing) {
 124             final double ratio = newCellWidth / (xm - x1);
 125 
 126             final double base
 127                     = originalSizing.getPercentWidth()
 128                     + originalSizingNext.getPercentWidth();
 129 
 130             final double newPercentWidth = Math.floor(ratio * base);
 131             final double newPercentWidthNext = base - newPercentWidth;
 132 
 133             cc.setPercentWidth(newPercentWidth);
 134             ccNext.setPercentWidth(newPercentWidthNext);
 135 
 136         } else {
 137 
 138             // Column at columnIndex
 139             cc.setPrefWidth(newCellWidth);
 140             if (cc.getMinWidth() == Region.USE_COMPUTED_SIZE) {
 141                 cc.setMinWidth(newCellWidth);
 142             } else {
 143                 cc.setMinWidth(Math.min(newCellWidth, cc.getMinWidth()));
 144             }
 145             if (cc.getMaxWidth() == Region.USE_COMPUTED_SIZE) {
 146                 cc.setMaxWidth(newCellWidth);
 147             } else {
 148                 cc.setMaxWidth(Math.max(newCellWidth, cc.getMaxWidth()));
 149             }
 150 
 151             // Column at columnIndex+1
 152             ccNext.setPrefWidth(newNextWidth);
 153             if (ccNext.getMinWidth() == Region.USE_COMPUTED_SIZE) {
 154                 ccNext.setMinWidth(newNextWidth);
 155             } else {
 156                 ccNext.setMinWidth(Math.min(newNextWidth, ccNext.getMinWidth()));
 157             }
 158             if (ccNext.getMaxWidth() == Region.USE_COMPUTED_SIZE) {
 159                 ccNext.setMaxWidth(newNextWidth);
 160             } else {
 161                 ccNext.setMaxWidth(Math.max(newNextWidth, ccNext.getMaxWidth()));
 162             }
 163         }
 164 
 165         // Adjusts min
 166     }
 167 
 168     public void revertToOriginalSize() {
 169         // Restore sizing of columns at columnIndex and columnIndex+1
 170         final ColumnConstraints cc = gridPane.getColumnConstraints().get(columnIndex);
 171         final ColumnConstraints ccNext = gridPane.getColumnConstraints().get(columnIndex+1);
 172 
 173         originalSizing.applyTo(cc);
 174         originalSizingNext.applyTo(ccNext);
 175     }
 176 
 177 
 178     public Map<PropertyName, Object> getChangeMap() {
 179         final Map<PropertyName, Object> result = new HashMap<>();
 180 
 181         final ColumnConstraints cc = gridPane.getColumnConstraints().get(columnIndex);
 182         if (MathUtils.equals(cc.getMinWidth(), originalSizing.getMinWidth()) == false) {
 183             result.put(minWidthName, cc.getMinWidth());
 184         }
 185         if (MathUtils.equals(cc.getPrefWidth(), originalSizing.getPrefWidth()) == false) {
 186             result.put(prefWidthName, cc.getPrefWidth());
 187         }
 188         if (MathUtils.equals(cc.getMaxWidth(), originalSizing.getMaxWidth()) == false) {
 189             result.put(maxWidthName, cc.getMaxWidth());
 190         }
 191         if (MathUtils.equals(cc.getPercentWidth(), originalSizing.getPercentWidth()) == false) {
 192             result.put(percentWidthName, cc.getPercentWidth());
 193         }
 194         return result;
 195     }
 196 
 197 
 198     public Map<PropertyName, Object> getChangeMapNext() {
 199         final Map<PropertyName, Object> result = new HashMap<>();
 200 
 201         final ColumnConstraints ccNext = gridPane.getColumnConstraints().get(columnIndex+1);
 202         if (MathUtils.equals(ccNext.getMinWidth(), originalSizingNext.getMinWidth()) == false) {
 203             result.put(minWidthName, ccNext.getMinWidth());
 204         }
 205         if (MathUtils.equals(ccNext.getPrefWidth(), originalSizingNext.getPrefWidth()) == false) {
 206             result.put(prefWidthName, ccNext.getPrefWidth());
 207         }
 208         if (MathUtils.equals(ccNext.getMaxWidth(), originalSizingNext.getMaxWidth()) == false) {
 209             result.put(maxWidthName, ccNext.getMaxWidth());
 210         }
 211         if (MathUtils.equals(ccNext.getPercentWidth(), originalSizingNext.getPercentWidth()) == false) {
 212             result.put(percentWidthName, ccNext.getPercentWidth());
 213         }
 214         return result;
 215     }
 216 
 217 
 218     /*
 219      * Private
 220      */
 221 
 222     private int countPercentWidths() {
 223         int result = 0;
 224         for (ColumnConstraints cc : gridPane.getColumnConstraints()) {
 225             if (cc.getPercentWidth() != -1) {
 226                 result++;
 227             }
 228         }
 229         return result;
 230     }
 231 
 232 
 233     private static class ColumnSizing {
 234         private final double minWidth;
 235         private final double maxWidth;
 236         private final double prefWidth;
 237         private final double percentWidth;
 238 
 239         public ColumnSizing(ColumnConstraints cc) {
 240             this.minWidth = cc.getMinWidth();
 241             this.maxWidth = cc.getMaxWidth();
 242             this.prefWidth = cc.getPrefWidth();
 243             this.percentWidth = cc.getPercentWidth();
 244         }
 245 
 246         public double getMinWidth() {
 247             return minWidth;
 248         }
 249 
 250         public double getMaxWidth() {
 251             return maxWidth;
 252         }
 253 
 254         public double getPrefWidth() {
 255             return prefWidth;
 256         }
 257 
 258         public double getPercentWidth() {
 259             return percentWidth;
 260         }
 261 
 262         public void applyTo(ColumnConstraints cc) {
 263             cc.setMinWidth(minWidth);
 264             cc.setMaxWidth(maxWidth);
 265             cc.setPrefWidth(prefWidth);
 266             cc.setPercentWidth(percentWidth);
 267         }
 268     }
 269 }