1 /* 2 * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.sun.javafx.scene.control.behavior; 27 28 import static javafx.scene.input.KeyCode.*; 29 30 import javafx.beans.value.ChangeListener; 31 import javafx.beans.value.WeakChangeListener; 32 import javafx.collections.ObservableList; 33 import javafx.geometry.NodeOrientation; 34 import javafx.scene.control.TableColumnBase; 35 import javafx.scene.control.TableFocusModel; 36 import javafx.scene.control.TablePositionBase; 37 import javafx.scene.control.TableSelectionModel; 38 import javafx.scene.control.TreeItem; 39 import javafx.scene.control.TreeTableColumn; 40 import javafx.scene.control.TreeTablePosition; 41 import javafx.scene.control.TreeTableView; 42 import javafx.scene.input.KeyEvent; 43 import javafx.util.Callback; 44 45 import java.util.ArrayList; 46 import java.util.List; 47 48 public class TreeTableViewBehavior<T> extends TableViewBehaviorBase<TreeTableView<T>, TreeItem<T>, TreeTableColumn<T, ?>> { 49 50 /************************************************************************** 51 * * 52 * Setup key bindings * 53 * * 54 *************************************************************************/ 55 56 protected static final List<KeyBinding> TREE_TABLE_VIEW_BINDINGS = new ArrayList<KeyBinding>(); 57 58 static { 59 // Add these bindings at the front of the list, so they take precedence 60 TREE_TABLE_VIEW_BINDINGS.add(new KeyBinding(LEFT, "CollapseRow")); 61 TREE_TABLE_VIEW_BINDINGS.add(new KeyBinding(KP_LEFT, "CollapseRow")); 62 TREE_TABLE_VIEW_BINDINGS.add(new KeyBinding(RIGHT, "ExpandRow")); 63 TREE_TABLE_VIEW_BINDINGS.add(new KeyBinding(KP_RIGHT, "ExpandRow")); 64 65 TREE_TABLE_VIEW_BINDINGS.add(new KeyBinding(MULTIPLY, "ExpandAll")); 66 TREE_TABLE_VIEW_BINDINGS.add(new KeyBinding(ADD, "ExpandRow")); 67 TREE_TABLE_VIEW_BINDINGS.add(new KeyBinding(SUBTRACT, "CollapseRow")); 68 69 TREE_TABLE_VIEW_BINDINGS.addAll(TABLE_VIEW_BINDINGS); 70 } 71 72 @Override protected /*final*/ String matchActionForEvent(KeyEvent e) { 73 String action = super.matchActionForEvent(e); 74 if (getControl().getEffectiveNodeOrientation() == NodeOrientation.RIGHT_TO_LEFT) { 75 // Rather than switching the result of the action lookup in this way, the preferred 76 // way to do this according to the current architecture would be to hoist the 77 // getEffectiveNodeOrientation call up into the key bindings, the same way that ListView 78 // orientation (horizontal vs. vertical) is handled with the OrientedKeyBinding class. 79 if ("CollapseRow".equals(action) && (e.getCode() == LEFT || e.getCode() == KP_LEFT)) { 80 action = "ExpandRow"; 81 } else if ("ExpandRow".equals(action) && (e.getCode() == RIGHT || e.getCode() == KP_RIGHT)) { 82 action = "CollapseRow"; 83 } 84 } 85 return action; 86 } 87 88 @Override protected void callAction(String name) { 89 if ("ExpandRow".equals(name)) rightArrowPressed(); 90 else if ("CollapseRow".equals(name)) leftArrowPressed(); 91 else if ("ExpandAll".equals(name)) expandAll(); 92 else super.callAction(name); 93 } 94 95 96 97 /************************************************************************** 98 * * 99 * Listeners * 100 * * 101 *************************************************************************/ 102 103 private final ChangeListener<TreeTableView.TreeTableViewSelectionModel<T>> selectionModelListener = 104 (observable, oldValue, newValue) -> { 105 if (oldValue != null) { 106 oldValue.getSelectedCells().removeListener(weakSelectedCellsListener); 107 } 108 if (newValue != null) { 109 newValue.getSelectedCells().addListener(weakSelectedCellsListener); 110 } 111 }; 112 113 private final WeakChangeListener<TreeTableView.TreeTableViewSelectionModel<T>> weakSelectionModelListener = 114 new WeakChangeListener<TreeTableView.TreeTableViewSelectionModel<T>>(selectionModelListener); 115 116 117 118 /************************************************************************** 119 * * 120 * Constructors * 121 * * 122 *************************************************************************/ 123 124 public TreeTableViewBehavior(TreeTableView<T> control) { 125 super(control, TREE_TABLE_VIEW_BINDINGS); 126 127 // Fix for RT-16565 128 control.selectionModelProperty().addListener(weakSelectionModelListener); 129 if (getSelectionModel() != null) { 130 control.getSelectionModel().getSelectedCells().addListener(selectedCellsListener); 131 } 132 } 133 134 135 136 /************************************************************************** 137 * * 138 * Implement TableViewBehaviorBase abstract methods * 139 * * 140 *************************************************************************/ 141 142 /** {@inheritDoc} */ 143 @Override protected int getItemCount() { 144 return getControl().getExpandedItemCount(); 145 } 146 147 /** {@inheritDoc} */ 148 @Override protected TableFocusModel getFocusModel() { 149 return getControl().getFocusModel(); 150 } 151 152 /** {@inheritDoc} */ 153 @Override protected TableSelectionModel<TreeItem<T>> getSelectionModel() { 154 return getControl().getSelectionModel(); 155 } 156 157 /** {@inheritDoc} */ 158 @Override protected ObservableList<TreeTablePosition<T,?>> getSelectedCells() { 159 return getControl().getSelectionModel().getSelectedCells(); 160 } 161 162 /** {@inheritDoc} */ 163 @Override protected TablePositionBase getFocusedCell() { 164 return getControl().getFocusModel().getFocusedCell(); 165 } 166 167 /** {@inheritDoc} */ 168 @Override protected int getVisibleLeafIndex(TableColumnBase tc) { 169 return getControl().getVisibleLeafIndex((TreeTableColumn)tc); 170 } 171 172 /** {@inheritDoc} */ 173 @Override protected TreeTableColumn getVisibleLeafColumn(int index) { 174 return getControl().getVisibleLeafColumn(index); 175 } 176 177 /** {@inheritDoc} */ 178 @Override protected void editCell(int row, TableColumnBase tc) { 179 getControl().edit(row, (TreeTableColumn)tc); 180 } 181 182 /** {@inheritDoc} */ 183 @Override protected ObservableList<TreeTableColumn<T,?>> getVisibleLeafColumns() { 184 return getControl().getVisibleLeafColumns(); 185 } 186 187 /** {@inheritDoc} */ 188 @Override protected TablePositionBase<TreeTableColumn<T, ?>> 189 getTablePosition(int row, TableColumnBase<TreeItem<T>, ?> tc) { 190 return new TreeTablePosition(getControl(), row, (TreeTableColumn)tc); 191 } 192 193 194 195 /************************************************************************** 196 * * 197 * Modify TableViewBehaviorBase behavior * 198 * * 199 *************************************************************************/ 200 201 /** {@inheritDoc} */ 202 @Override protected void selectAllToFocus(boolean setAnchorToFocusIndex) { 203 // Fix for RT-31241 204 if (getControl().getEditingCell() != null) return; 205 206 super.selectAllToFocus(setAnchorToFocusIndex); 207 } 208 209 /************************************************************************** 210 * * 211 * Tree-related implementation * 212 * * 213 *************************************************************************/ 214 215 /** 216 * The next methods handle the left/right arrow input differently depending 217 * on whether we are in row or cell selection. 218 */ 219 private void rightArrowPressed() { 220 if (getControl().getSelectionModel().isCellSelectionEnabled()) { 221 if (isRTL()) { 222 selectLeftCell(); 223 } else { 224 selectRightCell(); 225 } 226 } else { 227 expandRow(); 228 } 229 } 230 231 private void leftArrowPressed() { 232 if (getControl().getSelectionModel().isCellSelectionEnabled()) { 233 if (isRTL()) { 234 selectRightCell(); 235 } else { 236 selectLeftCell(); 237 } 238 } else { 239 collapseRow(); 240 } 241 } 242 243 private void expandRow() { 244 Callback<TreeItem<T>, Integer> getIndex = p -> getControl().getRow(p); 245 TreeViewBehavior.expandRow(getControl().getSelectionModel(), getIndex); 246 } 247 248 private void expandAll() { 249 TreeViewBehavior.expandAll(getControl().getRoot()); 250 } 251 252 private void collapseRow() { 253 TreeTableView<T> control = getControl(); 254 TreeViewBehavior.collapseRow(control.getSelectionModel(), control.getRoot(), control.isShowRoot()); 255 } 256 }