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 }