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.handles; 33 34 import java.util.List; 35 36 import javafx.beans.value.ChangeListener; 37 import javafx.geometry.Bounds; 38 import javafx.geometry.Point2D; 39 import javafx.scene.Cursor; 40 import javafx.scene.Group; 41 import javafx.scene.Node; 42 import javafx.scene.control.TableColumn; 43 import javafx.scene.control.TableView; 44 import javafx.scene.paint.Color; 45 import javafx.scene.shape.Line; 46 47 import com.oracle.javafx.scenebuilder.kit.editor.panel.content.AbstractResilientHandles; 48 import com.oracle.javafx.scenebuilder.kit.editor.panel.content.ContentPanelController; 49 import com.oracle.javafx.scenebuilder.kit.editor.panel.content.driver.TableViewDesignInfoX; 50 import com.oracle.javafx.scenebuilder.kit.editor.panel.content.gesture.AbstractGesture; 51 import com.oracle.javafx.scenebuilder.kit.editor.panel.content.gesture.mouse.ResizeTableColumnGesture; 52 import com.oracle.javafx.scenebuilder.kit.fxom.FXOMInstance; 53 import com.oracle.javafx.scenebuilder.kit.fxom.FXOMObject; 54 import com.oracle.javafx.scenebuilder.kit.metadata.util.DesignHierarchyMask; 55 56 /** 57 * 58 * 59 */ 60 61 public class TableColumnHandles extends AbstractResilientHandles<Object> { 62 63 /* 64 * Handles for TableColumn need a special treatment. 65 * 66 * A TableColumn instance can be transiently disconnected from its parent TableView: 67 * - TableColumn.getTableView() returns null 68 * - TableView.getColumns().contains() returns false 69 * 70 * When the TableColumn is disconnected, handles cannot be drawn. 71 * This Handles class inherits from AbstractResilientHandles to take 72 * care of this singularity. 73 */ 74 75 private final Group grips = new Group(); 76 private final TableViewDesignInfoX tableViewDesignInfo 77 = new TableViewDesignInfoX(); 78 private TableView<?> tableView; 79 private Node columnHeaderNode; 80 81 public TableColumnHandles(ContentPanelController contentPanelController, 82 FXOMInstance fxomInstance) { 83 super(contentPanelController, fxomInstance, Object.class); 84 assert fxomInstance.getSceneGraphObject() instanceof TableColumn; 85 86 getRootNode().getChildren().add(grips); // Above handles 87 88 getTableColumn().tableViewProperty().addListener( 89 (ChangeListener<Object>) (ov, v1, v2) -> tableViewOrVisibilityDidChange()); 90 getTableColumn().visibleProperty().addListener( 91 (ChangeListener<Object>) (ov, v1, v2) -> tableViewOrVisibilityDidChange()); 92 93 tableViewOrVisibilityDidChange(); 94 } 95 96 public FXOMInstance getFxomInstance() { 97 return (FXOMInstance) getFxomObject(); 98 } 99 100 /* 101 * AbstractGenericHandles 102 */ 103 @Override 104 public Bounds getSceneGraphObjectBounds() { 105 assert isReady(); 106 assert tableView != null; 107 assert getTableColumn().isVisible(); 108 return tableViewDesignInfo.getColumnBounds(getTableColumn()); 109 } 110 111 @Override 112 public Node getSceneGraphObjectProxy() { 113 assert isReady(); 114 assert tableView != null; 115 return tableView; 116 } 117 118 @Override 119 protected void startListeningToSceneGraphObject() { 120 assert isReady(); 121 assert tableView != null; 122 startListeningToLayoutBounds(tableView); 123 startListeningToLocalToSceneTransform(tableView); 124 125 assert columnHeaderNode == null; 126 columnHeaderNode = tableViewDesignInfo.getColumnNode(getTableColumn()); 127 startListeningToBoundsInParent(columnHeaderNode); 128 } 129 130 @Override 131 protected void stopListeningToSceneGraphObject() { 132 assert isReady(); 133 assert tableView != null; 134 stopListeningToLayoutBounds(tableView); 135 stopListeningToLocalToSceneTransform(tableView); 136 137 assert columnHeaderNode != null; 138 stopListeningToBoundsInParent(columnHeaderNode); 139 columnHeaderNode = null; 140 } 141 142 @Override 143 protected void layoutDecoration() { 144 assert tableView != null; 145 146 super.layoutDecoration(); 147 148 // Adjusts the number of grip lines to the number of dividers 149 adjustGripCount(); 150 151 // Updates grip positions 152 for (int i = 0, count = getTableColumns().size(); i < count; i++) { 153 layoutGrip(i); 154 } 155 } 156 157 @Override 158 public AbstractGesture findGesture(Node node) { 159 final AbstractGesture result; 160 161 final int gripIndex = grips.getChildren().indexOf(node); 162 if (gripIndex != -1) { 163 final FXOMObject parentObject = getFxomInstance().getParentObject(); 164 final DesignHierarchyMask m = new DesignHierarchyMask(parentObject); 165 final FXOMObject columnObject = m.getSubComponentAtIndex(gripIndex); 166 assert columnObject instanceof FXOMInstance; 167 result = new ResizeTableColumnGesture(getContentPanelController(), 168 (FXOMInstance)columnObject); 169 } else { 170 result = super.findGesture(node); 171 } 172 173 return result; 174 } 175 176 177 /* 178 * Private 179 */ 180 181 private TableColumn<?,?> getTableColumn() { 182 assert getSceneGraphObject() instanceof TableColumn; 183 return (TableColumn<?,?>) getSceneGraphObject(); 184 } 185 186 private void tableViewOrVisibilityDidChange() { 187 tableView = getTableColumn().getTableView(); 188 setReady((tableView != null) && getTableColumn().isVisible()); 189 } 190 191 private List<?> getTableColumns() { 192 final List<?> result; 193 194 final TableColumn<?,?> tableColumn = getTableColumn(); 195 if (tableColumn.getParentColumn() == null) { 196 result = tableView.getColumns(); 197 } else { 198 result = tableColumn.getParentColumn().getColumns(); 199 } 200 201 return result; 202 } 203 204 205 206 /* 207 * Private (grips) 208 */ 209 210 private void adjustGripCount() { 211 assert tableView != null; 212 213 final int columnCount = getTableColumns().size(); 214 final List<Node> gripChildren = grips.getChildren(); 215 216 while (gripChildren.size() < columnCount) { 217 gripChildren.add(makeGripLine()); 218 } 219 while (gripChildren.size() > columnCount) { 220 gripChildren.remove(gripChildren.size()-1); 221 } 222 } 223 224 private Line makeGripLine() { 225 final Line result = new Line(); 226 result.setStrokeWidth(SELECTION_HANDLES_SIZE); 227 result.setStroke(Color.TRANSPARENT); 228 result.setCursor(Cursor.H_RESIZE); 229 attachHandles(result); 230 return result; 231 } 232 233 private void layoutGrip(int gripIndex) { 234 assert grips.getChildren().get(gripIndex) instanceof Line; 235 assert getTableColumns().get(gripIndex) instanceof TableColumn<?,?>; 236 237 final List<?> columns = getTableColumns(); 238 final TableColumn<?,?> tc = (TableColumn<?,?>)columns.get(gripIndex); 239 if (tc.isVisible()) { 240 final TableViewDesignInfoX di = new TableViewDesignInfoX(); 241 final Bounds b = di.getColumnHeaderBounds(tc); 242 final double startX = b.getMaxX(); 243 final double startY = b.getMinY(); 244 final double endY = b.getMaxY(); 245 246 final boolean snapToPixel = true; 247 final Point2D startPoint = sceneGraphObjectToDecoration(startX, startY, snapToPixel); 248 final Point2D endPoint = sceneGraphObjectToDecoration(startX, endY, snapToPixel); 249 250 final Line gripLine = (Line) grips.getChildren().get(gripIndex); 251 gripLine.setVisible(true); 252 gripLine.setManaged(true); 253 gripLine.setStartX(startPoint.getX()); 254 gripLine.setStartY(startPoint.getY()); 255 gripLine.setEndX(endPoint.getX()); 256 gripLine.setEndY(endPoint.getY()); 257 } else { 258 final Line gripLine = (Line) grips.getChildren().get(gripIndex); 259 gripLine.setVisible(false); 260 gripLine.setManaged(false); 261 } 262 } 263 264 265 /* 266 * Wrapper to avoid the 'leaking this in constructor' warning emitted by NB. 267 */ 268 private void attachHandles(Node node) { 269 attachHandles(node, this); 270 } 271 }