1 /* 2 * Copyright (c) 2012, 2015, 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.scene.control.TableColumnBase; 34 import javafx.scene.control.TableFocusModel; 35 import javafx.scene.control.TablePositionBase; 36 import javafx.scene.control.TableSelectionModel; 37 import javafx.scene.control.TreeItem; 38 import javafx.scene.control.TreeTableColumn; 39 import javafx.scene.control.TreeTablePosition; 40 import javafx.scene.control.TreeTableView; 41 import com.sun.javafx.scene.control.inputmap.InputMap; 42 import javafx.util.Callback; 43 44 public class TreeTableViewBehavior<T> extends TableViewBehaviorBase<TreeTableView<T>, TreeItem<T>, TreeTableColumn<T, ?>> { 45 46 /************************************************************************** 47 * * 48 * Listeners * 49 * * 50 *************************************************************************/ 51 52 private final ChangeListener<TreeTableView.TreeTableViewSelectionModel<T>> selectionModelListener = 53 (observable, oldValue, newValue) -> { 54 if (oldValue != null) { 55 oldValue.getSelectedCells().removeListener(weakSelectedCellsListener); 56 } 57 if (newValue != null) { 58 newValue.getSelectedCells().addListener(weakSelectedCellsListener); 59 } 60 }; 61 62 private final WeakChangeListener<TreeTableView.TreeTableViewSelectionModel<T>> weakSelectionModelListener = 63 new WeakChangeListener<>(selectionModelListener); 64 65 66 67 /************************************************************************** 68 * * 69 * Constructors * 70 * * 71 *************************************************************************/ 72 73 public TreeTableViewBehavior(TreeTableView<T> control) { 74 super(control); 75 76 // Add these bindings as a child input map, so they take precedence 77 InputMap<TreeTableView<T>> expandCollapseInputMap = new InputMap<>(control); 78 expandCollapseInputMap.getMappings().addAll( 79 // these should be read as 'if RTL, use the first method, otherwise use the second' 80 new InputMap.KeyMapping(LEFT, e -> rtl(control, this::expandRow, this::collapseRow)), 81 new InputMap.KeyMapping(KP_LEFT, e -> rtl(control, this::expandRow, this::collapseRow)), 82 new InputMap.KeyMapping(RIGHT, e -> rtl(control, this::collapseRow, this::expandRow)), 83 new InputMap.KeyMapping(KP_RIGHT, e -> rtl(control, this::collapseRow, this::expandRow)), 84 85 new InputMap.KeyMapping(MULTIPLY, e -> expandAll()), 86 new InputMap.KeyMapping(ADD, e -> expandRow()), 87 new InputMap.KeyMapping(SUBTRACT, e -> collapseRow()) 88 ); 89 addDefaultChildMap(getInputMap(), expandCollapseInputMap); 90 91 92 // Fix for RT-16565 93 control.selectionModelProperty().addListener(weakSelectionModelListener); 94 if (getSelectionModel() != null) { 95 control.getSelectionModel().getSelectedCells().addListener(selectedCellsListener); 96 } 97 } 98 99 100 101 /************************************************************************** 102 * * 103 * Implement TableViewBehaviorBase abstract methods * 104 * * 105 *************************************************************************/ 106 107 /** {@inheritDoc} */ 108 @Override protected int getItemCount() { 109 return getNode().getExpandedItemCount(); 110 } 111 112 /** {@inheritDoc} */ 113 @Override protected TableFocusModel getFocusModel() { 114 return getNode().getFocusModel(); 115 } 116 117 /** {@inheritDoc} */ 118 @Override protected TableSelectionModel<TreeItem<T>> getSelectionModel() { 119 return getNode().getSelectionModel(); 120 } 121 122 /** {@inheritDoc} */ 123 @Override protected ObservableList<TreeTablePosition<T,?>> getSelectedCells() { 124 return getNode().getSelectionModel().getSelectedCells(); 125 } 126 127 /** {@inheritDoc} */ 128 @Override protected TablePositionBase getFocusedCell() { 129 return getNode().getFocusModel().getFocusedCell(); 130 } 131 132 /** {@inheritDoc} */ 133 @Override protected int getVisibleLeafIndex(TableColumnBase tc) { 134 return getNode().getVisibleLeafIndex((TreeTableColumn)tc); 135 } 136 137 /** {@inheritDoc} */ 138 @Override protected TreeTableColumn getVisibleLeafColumn(int index) { 139 return getNode().getVisibleLeafColumn(index); 140 } 141 142 /** {@inheritDoc} */ 143 @Override protected void editCell(int row, TableColumnBase tc) { 144 getNode().edit(row, (TreeTableColumn)tc); 145 } 146 147 /** {@inheritDoc} */ 148 @Override protected ObservableList<TreeTableColumn<T,?>> getVisibleLeafColumns() { 149 return getNode().getVisibleLeafColumns(); 150 } 151 152 /** {@inheritDoc} */ 153 @Override protected TablePositionBase<TreeTableColumn<T, ?>> 154 getTablePosition(int row, TableColumnBase<TreeItem<T>, ?> tc) { 155 return new TreeTablePosition(getNode(), row, (TreeTableColumn)tc); 156 } 157 158 159 160 /************************************************************************** 161 * * 162 * Modify TableViewBehaviorBase behavior * 163 * * 164 *************************************************************************/ 165 166 /** {@inheritDoc} */ 167 @Override protected void selectAllToFocus(boolean setAnchorToFocusIndex) { 168 // Fix for RT-31241 169 if (getNode().getEditingCell() != null) return; 170 171 super.selectAllToFocus(setAnchorToFocusIndex); 172 } 173 174 /************************************************************************** 175 * * 176 * Tree-related implementation * 177 * * 178 *************************************************************************/ 179 180 /** 181 * The next methods handle the left/right arrow input differently depending 182 * on whether we are in row or cell selection. 183 */ 184 private void rightArrowPressed() { 185 if (getNode().getSelectionModel().isCellSelectionEnabled()) { 186 if (isRTL()) { 187 selectLeftCell(); 188 } else { 189 selectRightCell(); 190 } 191 } else { 192 expandRow(); 193 } 194 } 195 196 private void leftArrowPressed() { 197 if (getNode().getSelectionModel().isCellSelectionEnabled()) { 198 if (isRTL()) { 199 selectRightCell(); 200 } else { 201 selectLeftCell(); 202 } 203 } else { 204 collapseRow(); 205 } 206 } 207 208 private void expandRow() { 209 Callback<TreeItem<T>, Integer> getIndex = p -> getNode().getRow(p); 210 TreeViewBehavior.expandRow(getNode().getSelectionModel(), getIndex); 211 } 212 213 private void expandAll() { 214 TreeViewBehavior.expandAll(getNode().getRoot()); 215 } 216 217 private void collapseRow() { 218 TreeTableView<T> control = getNode(); 219 TreeViewBehavior.collapseRow(control.getSelectionModel(), control.getRoot(), control.isShowRoot()); 220 } 221 }