All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.sun.javafx.scene.control.behavior.TreeCellBehavior Maven / Gradle / Ivy

/*
 * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package com.sun.javafx.scene.control.behavior;

import java.util.List;
import javafx.application.ConditionalFeature;
import javafx.scene.Node;
import javafx.scene.control.FocusModel;
import javafx.scene.control.MultipleSelectionModel;
import javafx.scene.control.SelectionMode;
import javafx.scene.control.TreeCell;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import com.sun.javafx.application.PlatformImpl;
import com.sun.javafx.scene.control.Logging;
import sun.util.logging.PlatformLogger;

/**
 */
public class TreeCellBehavior extends CellBehaviorBase> {

    /***************************************************************************
     *                                                                         *
     * Private static implementation                                           *
     *                                                                         *
     **************************************************************************/

    private static final String ANCHOR_PROPERTY_KEY = "list.anchor";

    static int getAnchor(TreeView tree) {
        FocusModel fm = tree.getFocusModel();
        if (fm == null) return -1;

        return hasAnchor(tree) ?
                (int)tree.getProperties().get(ANCHOR_PROPERTY_KEY) :
                fm.getFocusedIndex();
    }

    static void setAnchor(TreeView tree, int anchor) {
        if (tree != null && anchor < 0) {
            removeAnchor(tree);
        } else {
            tree.getProperties().put(ANCHOR_PROPERTY_KEY, anchor);
        }
    }

    static boolean hasAnchor(TreeView tree) {
        return tree.getProperties().get(ANCHOR_PROPERTY_KEY) != null;
    }

    static void removeAnchor(TreeView tree) {
        tree.getProperties().remove(ANCHOR_PROPERTY_KEY);
    }



    /***************************************************************************
     *                                                                         *
     * Private fields                                                          *
     *                                                                         *
     **************************************************************************/

    // For RT-17456: have selection occur as fast as possible with mouse input.
    // The idea is (consistently with some native applications we've tested) to
    // do the action as soon as you can. It takes a bit more coding but provides
    // the best feel:
    //  - when you click on a not-selected item, you can select immediately on press
    //  - when you click on a selected item, you need to wait whether DragDetected or Release comes first
    // To support touch devices, we have to slightly modify this behavior, such
    // that selection only happens on mouse release, if only minimal dragging
    // has occurred.
    private boolean latePress = false;
    private final boolean isTouch = PlatformImpl.isSupported(ConditionalFeature.INPUT_TOUCH);
    private boolean wasSelected = false;




    /***************************************************************************
     *                                                                         *
     * Constructors                                                            *
     *                                                                         *
     **************************************************************************/

    public TreeCellBehavior(final TreeCell control) {
        super(control);
    }



    /***************************************************************************
     *                                                                         *
     * Public API                                                              *
     *                                                                         *
     **************************************************************************/

    @Override public void mousePressed(MouseEvent event) {
        boolean selectedBefore = getControl().isSelected();

        if (getControl().isSelected()) {
            latePress = true;
            return;
        }

        doSelect(event);

        if (isTouch && selectedBefore) {
            wasSelected = getControl().isSelected();
        }
    }

    @Override public void mouseReleased(MouseEvent event) {
        if (latePress) {
            latePress = false;
            doSelect(event);
        }

        wasSelected = false;
    }

    @Override public void mouseDragged(MouseEvent event) {
        latePress = false;

        TreeView treeView = getControl().getTreeView();
        if (treeView == null || treeView.getSelectionModel() == null) return;

        // the mouse has now been dragged on a touch device, we should
        // remove the selection if we just added it in the last mouse press
        // event
        if (isTouch && ! wasSelected && getControl().isSelected()) {
            treeView.getSelectionModel().clearSelection(getControl().getIndex());
        }
    }



    /***************************************************************************
     *                                                                         *
     * Private implementation                                                  *
     *                                                                         *
     **************************************************************************/

    private void doSelect(MouseEvent event) {
        // we update the cell to point to the new tree node
        TreeCell treeCell = getControl();
        TreeView treeView = treeCell.getTreeView();
        if (treeView == null) return;

        // If the mouse event is not contained within this TreeCell, then
        // we don't want to react to it.
        if (treeCell.isEmpty() || ! treeCell.contains(event.getX(), event.getY())) {
            final PlatformLogger logger = Logging.getControlsLogger();
            if (treeCell.isEmpty() && logger.isLoggable(PlatformLogger.WARNING)) {
//                logger.warning("TreeCell is empty, so mouse pressed event is "
//                        + "ignored. If you've created a custom cell and overridden "
//                        + "updateItem, be sure to call super.updateItem(item, empty)");
            }
            return;
        }

        int index = treeCell.getIndex();
        boolean selected = treeCell.isSelected();
        MultipleSelectionModel> sm = treeView.getSelectionModel();
        if (sm == null) return;

        FocusModel> fm = treeView.getFocusModel();
        if (fm == null) return;

        // if the user has clicked on the disclosure node, we do nothing other
        // than expand/collapse the tree item (if applicable). We do not do editing!
        Node disclosureNode = treeCell.getDisclosureNode();
        if (disclosureNode != null) {
            if (disclosureNode.getBoundsInParent().contains(event.getX(), event.getY())) {
                if (treeCell.getTreeItem() != null) {
                    treeCell.getTreeItem().setExpanded(! treeCell.getTreeItem().isExpanded());
                }
                return;
            }
        }

        // if shift is down, and we don't already have the initial focus index
        // recorded, we record the focus index now so that subsequent shift+clicks
        // result in the correct selection occuring (whilst the focus index moves
        // about).
        if (event.isShiftDown()) {
            if (! hasAnchor(treeView)) {
                setAnchor(treeView, fm.getFocusedIndex());
            }
        } else {
            removeAnchor(treeView);
        }

        MouseButton button = event.getButton();
        if (button == MouseButton.PRIMARY || (button == MouseButton.SECONDARY && !selected)) {
            if (sm.getSelectionMode() == SelectionMode.SINGLE) {
                simpleSelect(event);
            } else {
                if (event.isControlDown() || event.isMetaDown()) {
                    if (selected) {
                        // we remove this row from the current selection
                        sm.clearSelection(index);
                        fm.focus(index);
                    } else {
                        // We add this row to the current selection
                        sm.select(index);
                    }
                } else if (event.isShiftDown() && event.getClickCount() == 1) {
                    // we add all rows between the current selection focus and
                    // this row (inclusive) to the current selection.
                    final int focusedIndex = getAnchor(treeView);

                    // and then determine all row and columns which must be selected
                    int minRow = Math.min(focusedIndex, index);
                    int maxRow = Math.max(focusedIndex, index);

                    // and then perform the selection
                    // We do this by deselecting the elements that are not in
                    // range, and then selecting all elements that are in range
                    List selectedIndices = sm.getSelectedIndices();
                    for (int i = 0, max = selectedIndices.size(); i < max; i++) {
                        int selectedIndex = selectedIndices.get(i);
                        if (selectedIndex < minRow || selectedIndex > maxRow) {
                            sm.clearSelection(selectedIndex);
                        }
                    }
                    sm.selectRange(minRow, maxRow+1);

                    fm.focus(index);
                } else {
                    simpleSelect(event);
                }
            }
        }
    }

    private void simpleSelect(MouseEvent e) {
        TreeView tv = getControl().getTreeView();
        TreeItem treeItem = getControl().getTreeItem();
        int index = getControl().getIndex();
        MultipleSelectionModel> sm = tv.getSelectionModel();
        boolean isAlreadySelected = sm.isSelected(index);

        if (isAlreadySelected && (e.isControlDown() || e.isMetaDown())) {
            sm.clearSelection(index);
            isAlreadySelected = false;
        } else {
            sm.clearAndSelect(index);
        }

        // handle editing, which only occurs with the primary mouse button
        if (e.getButton() == MouseButton.PRIMARY) {
            if (e.getClickCount() == 1 && isAlreadySelected) {
                tv.edit(treeItem);
            } else if (e.getClickCount() == 1) {
                // cancel editing
                tv.edit(null);
            } else if (e.getClickCount() == 2 && treeItem.isLeaf()) {
                // attempt to edit
                tv.edit(treeItem);
            } else if (e.getClickCount() % 2 == 0) {
                // try to expand/collapse branch tree item
                treeItem.setExpanded(! treeItem.isExpanded());
            }
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy