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