alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Java example source code file (DefaultTreeSelectionModel.java)

This example Java source code file (DefaultTreeSelectionModel.java) is included in the alvinalexander.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Learn more about this Java project at its project page.

Java - Java tags/keywords

bean, bitset, boolean, defaultlistselectionmodel, defaulttreeselectionmodel, event, eventlistenerlist, gui, hashtable, javabean, object, pathplaceholder, propertychangelistener, rowmapper, string, swing, treepath, treeselectionevent, util, vector

The DefaultTreeSelectionModel.java Java example source code

/*
 * Copyright (c) 1997, 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 javax.swing.tree;

import java.beans.PropertyChangeListener;
import java.io.*;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.Enumeration;
import java.util.EventListener;
import java.util.Hashtable;
import java.util.List;
import java.util.Vector;
import javax.swing.event.*;
import javax.swing.DefaultListSelectionModel;

/**
 * Default implementation of TreeSelectionModel.  Listeners are notified
 * whenever
 * the paths in the selection change, not the rows. In order
 * to be able to track row changes you may wish to become a listener
 * for expansion events on the tree and test for changes from there.
 * <p>resetRowSelection is called from any of the methods that update
 * the selected paths. If you subclass any of these methods to
 * filter what is allowed to be selected, be sure and message
 * <code>resetRowSelection if you do not message super.
 *
 * <strong>Warning:
 * Serialized objects of this class will not be compatible with
 * future Swing releases. The current serialization support is
 * appropriate for short term storage or RMI between applications running
 * the same version of Swing.  As of 1.4, support for long term storage
 * of all JavaBeans™
 * has been added to the <code>java.beans package.
 * Please see {@link java.beans.XMLEncoder}.
 *
 * @see javax.swing.JTree
 *
 * @author Scott Violet
 */
@SuppressWarnings("serial")
public class DefaultTreeSelectionModel implements Cloneable, Serializable, TreeSelectionModel
{
    /** Property name for selectionMode. */
    public static final String          SELECTION_MODE_PROPERTY = "selectionMode";

    /** Used to messaged registered listeners. */
    protected SwingPropertyChangeSupport     changeSupport;

    /** Paths that are currently selected.  Will be null if nothing is
      * currently selected. */
    protected TreePath[]                selection;

    /** Event listener list. */
    protected EventListenerList   listenerList = new EventListenerList();

    /** Provides a row for a given path. */
    transient protected RowMapper               rowMapper;

    /** Handles maintaining the list selection model. The RowMapper is used
     * to map from a TreePath to a row, and the value is then placed here. */
    protected DefaultListSelectionModel     listSelectionModel;

    /** Mode for the selection, will be either SINGLE_TREE_SELECTION,
     * CONTIGUOUS_TREE_SELECTION or DISCONTIGUOUS_TREE_SELECTION.
     */
    protected int                           selectionMode;

    /** Last path that was added. */
    protected TreePath                      leadPath;
    /** Index of the lead path in selection. */
    protected int                           leadIndex;
    /** Lead row. */
    protected int                           leadRow;

    /** Used to make sure the paths are unique, will contain all the paths
     * in <code>selection.
     */
    private Hashtable<TreePath, Boolean>    uniquePaths;
    private Hashtable<TreePath, Boolean>    lastPaths;
    private TreePath[]                      tempPaths;


    /**
     * Creates a new instance of DefaultTreeSelectionModel that is
     * empty, with a selection mode of DISCONTIGUOUS_TREE_SELECTION.
     */
    public DefaultTreeSelectionModel() {
        listSelectionModel = new DefaultListSelectionModel();
        selectionMode = DISCONTIGUOUS_TREE_SELECTION;
        leadIndex = leadRow = -1;
        uniquePaths = new Hashtable<TreePath, Boolean>();
        lastPaths = new Hashtable<TreePath, Boolean>();
        tempPaths = new TreePath[1];
    }

    /**
     * Sets the RowMapper instance. This instance is used to determine
     * the row for a particular TreePath.
     */
    public void setRowMapper(RowMapper newMapper) {
        rowMapper = newMapper;
        resetRowSelection();
    }

    /**
     * Returns the RowMapper instance that is able to map a TreePath to a
     * row.
     */
    public RowMapper getRowMapper() {
        return rowMapper;
    }

    /**
     * Sets the selection model, which must be one of SINGLE_TREE_SELECTION,
     * CONTIGUOUS_TREE_SELECTION or DISCONTIGUOUS_TREE_SELECTION. If mode
     * is not one of the defined value,
     * <code>DISCONTIGUOUS_TREE_SELECTION is assumed.
     * <p>This may change the selection if the current selection is not valid
     * for the new mode. For example, if three TreePaths are
     * selected when the mode is changed to <code>SINGLE_TREE_SELECTION,
     * only one TreePath will remain selected. It is up to the particular
     * implementation to decide what TreePath remains selected.
     * <p>
     * Setting the mode to something other than the defined types will
     * result in the mode becoming <code>DISCONTIGUOUS_TREE_SELECTION.
     */
    public void setSelectionMode(int mode) {
        int            oldMode = selectionMode;

        selectionMode = mode;
        if(selectionMode != TreeSelectionModel.SINGLE_TREE_SELECTION &&
           selectionMode != TreeSelectionModel.CONTIGUOUS_TREE_SELECTION &&
           selectionMode != TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION)
            selectionMode = TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION;
        if(oldMode != selectionMode && changeSupport != null)
            changeSupport.firePropertyChange(SELECTION_MODE_PROPERTY,
                                             Integer.valueOf(oldMode),
                                             Integer.valueOf(selectionMode));
    }

    /**
     * Returns the selection mode, one of <code>SINGLE_TREE_SELECTION,
     * <code>DISCONTIGUOUS_TREE_SELECTION or
     * <code>CONTIGUOUS_TREE_SELECTION.
     */
    public int getSelectionMode() {
        return selectionMode;
    }

    /**
      * Sets the selection to path. If this represents a change, then
      * the TreeSelectionListeners are notified. If <code>path is
      * null, this has the same effect as invoking <code>clearSelection.
      *
      * @param path new path to select
      */
    public void setSelectionPath(TreePath path) {
        if(path == null)
            setSelectionPaths(null);
        else {
            TreePath[]          newPaths = new TreePath[1];

            newPaths[0] = path;
            setSelectionPaths(newPaths);
        }
    }

    /**
     * Sets the selection. Whether the supplied paths are taken as the
     * new selection depends upon the selection mode. If the supplied
     * array is {@code null}, or empty, the selection is cleared. If
     * the selection mode is {@code SINGLE_TREE_SELECTION}, only the
     * first path in {@code pPaths} is used. If the selection
     * mode is {@code CONTIGUOUS_TREE_SELECTION} and the supplied paths
     * are not contiguous, then only the first path in {@code pPaths} is
     * used. If the selection mode is
     * {@code DISCONTIGUOUS_TREE_SELECTION}, then all paths are used.
     * <p>
     * All {@code null} paths in {@code pPaths} are ignored.
     * <p>
     * If this represents a change, all registered {@code
     * TreeSelectionListener}s are notified.
     * <p>
     * The lead path is set to the last unique path.
     * <p>
     * The paths returned from {@code getSelectionPaths} are in the same
     * order as those supplied to this method.
     *
     * @param pPaths the new selection
     */
    public void setSelectionPaths(TreePath[] pPaths) {
        int            newCount, newCounter, oldCount, oldCounter;
        TreePath[]     paths = pPaths;

        if(paths == null)
            newCount = 0;
        else
            newCount = paths.length;
        if(selection == null)
            oldCount = 0;
        else
            oldCount = selection.length;
        if((newCount + oldCount) != 0) {
            if(selectionMode == TreeSelectionModel.SINGLE_TREE_SELECTION) {
                /* If single selection and more than one path, only allow
                   first. */
                if(newCount > 1) {
                    paths = new TreePath[1];
                    paths[0] = pPaths[0];
                    newCount = 1;
                }
            }
            else if(selectionMode ==
                    TreeSelectionModel.CONTIGUOUS_TREE_SELECTION) {
                /* If contiguous selection and paths aren't contiguous,
                   only select the first path item. */
                if(newCount > 0 && !arePathsContiguous(paths)) {
                    paths = new TreePath[1];
                    paths[0] = pPaths[0];
                    newCount = 1;
                }
            }

            TreePath         beginLeadPath = leadPath;
            Vector<PathPlaceHolder> cPaths = new Vector(newCount + oldCount);
            List<TreePath> newSelectionAsList =
                    new ArrayList<TreePath>(newCount);

            lastPaths.clear();
            leadPath = null;
            /* Find the paths that are new. */
            for(newCounter = 0; newCounter < newCount; newCounter++) {
                TreePath path = paths[newCounter];
                if (path != null && lastPaths.get(path) == null) {
                    lastPaths.put(path, Boolean.TRUE);
                    if (uniquePaths.get(path) == null) {
                        cPaths.addElement(new PathPlaceHolder(path, true));
                    }
                    leadPath = path;
                    newSelectionAsList.add(path);
                }
            }

            TreePath[] newSelection = newSelectionAsList.toArray(
                    new TreePath[newSelectionAsList.size()]);

            /* Get the paths that were selected but no longer selected. */
            for(oldCounter = 0; oldCounter < oldCount; oldCounter++)
                if(selection[oldCounter] != null &&
                    lastPaths.get(selection[oldCounter]) == null)
                    cPaths.addElement(new PathPlaceHolder
                                      (selection[oldCounter], false));

            selection = newSelection;

            Hashtable<TreePath, Boolean>  tempHT = uniquePaths;

            uniquePaths = lastPaths;
            lastPaths = tempHT;
            lastPaths.clear();

            // No reason to do this now, but will still call it.
            insureUniqueness();

            updateLeadIndex();

            resetRowSelection();
            /* Notify of the change. */
            if(cPaths.size() > 0)
                notifyPathChange(cPaths, beginLeadPath);
        }
    }

    /**
      * Adds path to the current selection. If path is not currently
      * in the selection the TreeSelectionListeners are notified. This has
      * no effect if <code>path is null.
      *
      * @param path the new path to add to the current selection
      */
    public void addSelectionPath(TreePath path) {
        if(path != null) {
            TreePath[]            toAdd = new TreePath[1];

            toAdd[0] = path;
            addSelectionPaths(toAdd);
        }
    }

    /**
      * Adds paths to the current selection. If any of the paths in
      * paths are not currently in the selection the TreeSelectionListeners
      * are notified. This has
      * no effect if <code>paths is null.
      * <p>The lead path is set to the last element in paths.
      * <p>If the selection mode is CONTIGUOUS_TREE_SELECTION,
      * and adding the new paths would make the selection discontiguous.
      * Then two things can result: if the TreePaths in <code>paths
      * are contiguous, then the selection becomes these TreePaths,
      * otherwise the TreePaths aren't contiguous and the selection becomes
      * the first TreePath in <code>paths.
      *
      * @param paths the new path to add to the current selection
      */
    public void addSelectionPaths(TreePath[] paths) {
        int       newPathLength = ((paths == null) ? 0 : paths.length);

        if(newPathLength > 0) {
            if(selectionMode == TreeSelectionModel.SINGLE_TREE_SELECTION) {
                setSelectionPaths(paths);
            }
            else if(selectionMode == TreeSelectionModel.
                    CONTIGUOUS_TREE_SELECTION && !canPathsBeAdded(paths)) {
                if(arePathsContiguous(paths)) {
                    setSelectionPaths(paths);
                }
                else {
                    TreePath[]          newPaths = new TreePath[1];

                    newPaths[0] = paths[0];
                    setSelectionPaths(newPaths);
                }
            }
            else {
                int               counter, validCount;
                int               oldCount;
                TreePath          beginLeadPath = leadPath;
                Vector<PathPlaceHolder>  cPaths = null;

                if(selection == null)
                    oldCount = 0;
                else
                    oldCount = selection.length;
                /* Determine the paths that aren't currently in the
                   selection. */
                lastPaths.clear();
                for(counter = 0, validCount = 0; counter < newPathLength;
                    counter++) {
                    if(paths[counter] != null) {
                        if (uniquePaths.get(paths[counter]) == null) {
                            validCount++;
                            if(cPaths == null)
                                cPaths = new Vector<PathPlaceHolder>();
                            cPaths.addElement(new PathPlaceHolder
                                              (paths[counter], true));
                            uniquePaths.put(paths[counter], Boolean.TRUE);
                            lastPaths.put(paths[counter], Boolean.TRUE);
                        }
                        leadPath = paths[counter];
                    }
                }

                if(leadPath == null) {
                    leadPath = beginLeadPath;
                }

                if(validCount > 0) {
                    TreePath         newSelection[] = new TreePath[oldCount +
                                                                  validCount];

                    /* And build the new selection. */
                    if(oldCount > 0)
                        System.arraycopy(selection, 0, newSelection, 0,
                                         oldCount);
                    if(validCount != paths.length) {
                        /* Some of the paths in paths are already in
                           the selection. */
                        Enumeration<TreePath> newPaths = lastPaths.keys();

                        counter = oldCount;
                        while (newPaths.hasMoreElements()) {
                            newSelection[counter++] = newPaths.nextElement();
                        }
                    }
                    else {
                        System.arraycopy(paths, 0, newSelection, oldCount,
                                         validCount);
                    }

                    selection = newSelection;

                    insureUniqueness();

                    updateLeadIndex();

                    resetRowSelection();

                    notifyPathChange(cPaths, beginLeadPath);
                }
                else
                    leadPath = beginLeadPath;
                lastPaths.clear();
            }
        }
    }

    /**
      * Removes path from the selection. If path is in the selection
      * The TreeSelectionListeners are notified. This has no effect if
      * <code>path is null.
      *
      * @param path the path to remove from the selection
      */
    public void removeSelectionPath(TreePath path) {
        if(path != null) {
            TreePath[]             rPath = new TreePath[1];

            rPath[0] = path;
            removeSelectionPaths(rPath);
        }
    }

    /**
      * Removes paths from the selection.  If any of the paths in paths
      * are in the selection the TreeSelectionListeners are notified.
      * This has no effect if <code>paths is null.
      *
      * @param paths the paths to remove from the selection
      */
    public void removeSelectionPaths(TreePath[] paths) {
        if (paths != null && selection != null && paths.length > 0) {
            if(!canPathsBeRemoved(paths)) {
                /* Could probably do something more interesting here! */
                clearSelection();
            }
            else {
                Vector<PathPlaceHolder> pathsToRemove = null;

                /* Find the paths that can be removed. */
                for (int removeCounter = paths.length - 1; removeCounter >= 0;
                     removeCounter--) {
                    if(paths[removeCounter] != null) {
                        if (uniquePaths.get(paths[removeCounter]) != null) {
                            if(pathsToRemove == null)
                                pathsToRemove = new Vector<PathPlaceHolder>(paths.length);
                            uniquePaths.remove(paths[removeCounter]);
                            pathsToRemove.addElement(new PathPlaceHolder
                                         (paths[removeCounter], false));
                        }
                    }
                }
                if(pathsToRemove != null) {
                    int         removeCount = pathsToRemove.size();
                    TreePath    beginLeadPath = leadPath;

                    if(removeCount == selection.length) {
                        selection = null;
                    }
                    else {
                        Enumeration<TreePath> pEnum = uniquePaths.keys();
                        int                  validCount = 0;

                        selection = new TreePath[selection.length -
                                                removeCount];
                        while (pEnum.hasMoreElements()) {
                            selection[validCount++] = pEnum.nextElement();
                        }
                    }
                    if (leadPath != null &&
                        uniquePaths.get(leadPath) == null) {
                        if (selection != null) {
                            leadPath = selection[selection.length - 1];
                        }
                        else {
                            leadPath = null;
                        }
                    }
                    else if (selection != null) {
                        leadPath = selection[selection.length - 1];
                    }
                    else {
                        leadPath = null;
                    }
                    updateLeadIndex();

                    resetRowSelection();

                    notifyPathChange(pathsToRemove, beginLeadPath);
                }
            }
        }
    }

    /**
      * Returns the first path in the selection. This is useful if there
      * if only one item currently selected.
      */
    public TreePath getSelectionPath() {
        if (selection != null && selection.length > 0) {
            return selection[0];
        }
        return null;
    }

    /**
      * Returns the selection.
      *
      * @return the selection
      */
    public TreePath[] getSelectionPaths() {
        if(selection != null) {
            int                 pathSize = selection.length;
            TreePath[]          result = new TreePath[pathSize];

            System.arraycopy(selection, 0, result, 0, pathSize);
            return result;
        }
        return new TreePath[0];
    }

    /**
     * Returns the number of paths that are selected.
     */
    public int getSelectionCount() {
        return (selection == null) ? 0 : selection.length;
    }

    /**
      * Returns true if the path, <code>path,
      * is in the current selection.
      */
    public boolean isPathSelected(TreePath path) {
        return (path != null) ? (uniquePaths.get(path) != null) : false;
    }

    /**
      * Returns true if the selection is currently empty.
      */
    public boolean isSelectionEmpty() {
        return (selection == null || selection.length == 0);
    }

    /**
      * Empties the current selection.  If this represents a change in the
      * current selection, the selection listeners are notified.
      */
    public void clearSelection() {
        if (selection != null && selection.length > 0) {
            int                    selSize = selection.length;
            boolean[]              newness = new boolean[selSize];

            for(int counter = 0; counter < selSize; counter++)
                newness[counter] = false;

            TreeSelectionEvent     event = new TreeSelectionEvent
                (this, selection, newness, leadPath, null);

            leadPath = null;
            leadIndex = leadRow = -1;
            uniquePaths.clear();
            selection = null;
            resetRowSelection();
            fireValueChanged(event);
        }
    }

    /**
      * Adds x to the list of listeners that are notified each time the
      * set of selected TreePaths changes.
      *
      * @param x the new listener to be added
      */
    public void addTreeSelectionListener(TreeSelectionListener x) {
        listenerList.add(TreeSelectionListener.class, x);
    }

    /**
      * Removes x from the list of listeners that are notified each time
      * the set of selected TreePaths changes.
      *
      * @param x the listener to remove
      */
    public void removeTreeSelectionListener(TreeSelectionListener x) {
        listenerList.remove(TreeSelectionListener.class, x);
    }

    /**
     * Returns an array of all the tree selection listeners
     * registered on this model.
     *
     * @return all of this model's <code>TreeSelectionListeners
     *         or an empty
     *         array if no tree selection listeners are currently registered
     *
     * @see #addTreeSelectionListener
     * @see #removeTreeSelectionListener
     *
     * @since 1.4
     */
    public TreeSelectionListener[] getTreeSelectionListeners() {
        return listenerList.getListeners(TreeSelectionListener.class);
    }

    /**
     * Notifies all listeners that are registered for
     * tree selection events on this object.
     * @see #addTreeSelectionListener
     * @see EventListenerList
     */
    protected void fireValueChanged(TreeSelectionEvent e) {
        // Guaranteed to return a non-null array
        Object[] listeners = listenerList.getListenerList();
        // TreeSelectionEvent e = null;
        // Process the listeners last to first, notifying
        // those that are interested in this event
        for (int i = listeners.length-2; i>=0; i-=2) {
            if (listeners[i]==TreeSelectionListener.class) {
                // Lazily create the event:
                // if (e == null)
                // e = new ListSelectionEvent(this, firstIndex, lastIndex);
                ((TreeSelectionListener)listeners[i+1]).valueChanged(e);
            }
        }
    }

    /**
     * Returns an array of all the objects currently registered
     * as <code>FooListeners
     * upon this model.
     * <code>FooListeners are registered using the
     * <code>addFooListener method.
     *
     * <p>
     *
     * You can specify the <code>listenerType argument
     * with a class literal,
     * such as
     * <code>FooListener.class.
     * For example, you can query a
     * <code>DefaultTreeSelectionModel m
     * for its tree selection listeners with the following code:
     *
     * <pre>TreeSelectionListener[] tsls = (TreeSelectionListener[])(m.getListeners(TreeSelectionListener.class));
* * If no such listeners exist, this method returns an empty array. * * @param listenerType the type of listeners requested; this parameter * should specify an interface that descends from * <code>java.util.EventListener * @return an array of all objects registered as * <code>FooListeners on this component, * or an empty array if no such * listeners have been added * @exception ClassCastException if <code>listenerType * doesn't specify a class or interface that implements * <code>java.util.EventListener * * @see #getTreeSelectionListeners * @see #getPropertyChangeListeners * * @since 1.3 */ public <T extends EventListener> T[] getListeners(Class listenerType) { return listenerList.getListeners(listenerType); } /** * Returns the selection in terms of rows. There is not * necessarily a one-to-one mapping between the {@code TreePath}s * returned from {@code getSelectionPaths} and this method. In * particular, if a {@code TreePath} is not viewable (the {@code * RowMapper} returns {@code -1} for the row corresponding to the * {@code TreePath}), then the corresponding row is not included * in the returned array. For example, if the selection consists * of two paths, {@code A} and {@code B}, with {@code A} at row * {@code 10}, and {@code B} not currently viewable, then this method * returns an array with the single entry {@code 10}. * * @return the selection in terms of rows */ public int[] getSelectionRows() { // This is currently rather expensive. Needs // to be better support from ListSelectionModel to speed this up. if (rowMapper != null && selection != null && selection.length > 0) { int[] rows = rowMapper.getRowsForPaths(selection); if (rows != null) { int invisCount = 0; for (int counter = rows.length - 1; counter >= 0; counter--) { if (rows[counter] == -1) { invisCount++; } } if (invisCount > 0) { if (invisCount == rows.length) { rows = null; } else { int[] tempRows = new int[rows.length - invisCount]; for (int counter = rows.length - 1, visCounter = 0; counter >= 0; counter--) { if (rows[counter] != -1) { tempRows[visCounter++] = rows[counter]; } } rows = tempRows; } } } return rows; } return new int[0]; } /** * Returns the smallest value obtained from the RowMapper for the * current set of selected TreePaths. If nothing is selected, * or there is no RowMapper, this will return -1. */ public int getMinSelectionRow() { return listSelectionModel.getMinSelectionIndex(); } /** * Returns the largest value obtained from the RowMapper for the * current set of selected TreePaths. If nothing is selected, * or there is no RowMapper, this will return -1. */ public int getMaxSelectionRow() { return listSelectionModel.getMaxSelectionIndex(); } /** * Returns true if the row identified by <code>row is selected. */ public boolean isRowSelected(int row) { return listSelectionModel.isSelectedIndex(row); } /** * Updates this object's mapping from TreePath to rows. This should * be invoked when the mapping from TreePaths to integers has changed * (for example, a node has been expanded). * <p>You do not normally have to call this, JTree and its associated * Listeners will invoke this for you. If you are implementing your own * View class, then you will have to invoke this. * <p>This will invoke insureRowContinuity to make sure * the currently selected TreePaths are still valid based on the * selection mode. */ public void resetRowSelection() { listSelectionModel.clearSelection(); if(selection != null && rowMapper != null) { int aRow; int validCount = 0; int[] rows = rowMapper.getRowsForPaths(selection); for(int counter = 0, maxCounter = selection.length; counter < maxCounter; counter++) { aRow = rows[counter]; if(aRow != -1) { listSelectionModel.addSelectionInterval(aRow, aRow); } } if(leadIndex != -1 && rows != null) { leadRow = rows[leadIndex]; } else if (leadPath != null) { // Lead selection path doesn't have to be in the selection. tempPaths[0] = leadPath; rows = rowMapper.getRowsForPaths(tempPaths); leadRow = (rows != null) ? rows[0] : -1; } else { leadRow = -1; } insureRowContinuity(); } else leadRow = -1; } /** * Returns the lead selection index. That is the last index that was * added. */ public int getLeadSelectionRow() { return leadRow; } /** * Returns the last path that was added. This may differ from the * leadSelectionPath property maintained by the JTree. */ public TreePath getLeadSelectionPath() { return leadPath; } /** * Adds a PropertyChangeListener to the listener list. * The listener is registered for all properties. * <p> * A PropertyChangeEvent will get fired when the selection mode * changes. * * @param listener the PropertyChangeListener to be added */ public synchronized void addPropertyChangeListener( PropertyChangeListener listener) { if (changeSupport == null) { changeSupport = new SwingPropertyChangeSupport(this); } changeSupport.addPropertyChangeListener(listener); } /** * Removes a PropertyChangeListener from the listener list. * This removes a PropertyChangeListener that was registered * for all properties. * * @param listener the PropertyChangeListener to be removed */ public synchronized void removePropertyChangeListener( PropertyChangeListener listener) { if (changeSupport == null) { return; } changeSupport.removePropertyChangeListener(listener); } /** * Returns an array of all the property change listeners * registered on this <code>DefaultTreeSelectionModel. * * @return all of this model's <code>PropertyChangeListeners * or an empty * array if no property change listeners are currently registered * * @see #addPropertyChangeListener * @see #removePropertyChangeListener * * @since 1.4 */ public PropertyChangeListener[] getPropertyChangeListeners() { if (changeSupport == null) { return new PropertyChangeListener[0]; } return changeSupport.getPropertyChangeListeners(); } /** * Makes sure the currently selected <code>TreePaths are valid * for the current selection mode. * If the selection mode is <code>CONTIGUOUS_TREE_SELECTION * and a <code>RowMapper exists, this will make sure all * the rows are contiguous, that is, when sorted all the rows are * in order with no gaps. * If the selection isn't contiguous, the selection is * reset to contain the first set, when sorted, of contiguous rows. * <p> * If the selection mode is <code>SINGLE_TREE_SELECTION and * more than one TreePath is selected, the selection is reset to * contain the first path currently selected. */ protected void insureRowContinuity() { if(selectionMode == TreeSelectionModel.CONTIGUOUS_TREE_SELECTION && selection != null && rowMapper != null) { DefaultListSelectionModel lModel = listSelectionModel; int min = lModel.getMinSelectionIndex(); if(min != -1) { for(int counter = min, maxCounter = lModel.getMaxSelectionIndex(); counter <= maxCounter; counter++) { if(!lModel.isSelectedIndex(counter)) { if(counter == min) { clearSelection(); } else { TreePath[] newSel = new TreePath[counter - min]; int selectionIndex[] = rowMapper.getRowsForPaths(selection); // find the actual selection pathes corresponded to the // rows of the new selection for (int i = 0; i < selectionIndex.length; i++) { if (selectionIndex[i]<counter) { newSel[selectionIndex[i]-min] = selection[i]; } } setSelectionPaths(newSel); break; } } } } } else if(selectionMode == TreeSelectionModel.SINGLE_TREE_SELECTION && selection != null && selection.length > 1) { setSelectionPath(selection[0]); } } /** * Returns true if the paths are contiguous, * or this object has no RowMapper. */ protected boolean arePathsContiguous(TreePath[] paths) { if(rowMapper == null || paths.length < 2) return true; else { BitSet bitSet = new BitSet(32); int anIndex, counter, min; int pathCount = paths.length; int validCount = 0; TreePath[] tempPath = new TreePath[1]; tempPath[0] = paths[0]; min = rowMapper.getRowsForPaths(tempPath)[0]; for(counter = 0; counter < pathCount; counter++) { if(paths[counter] != null) { tempPath[0] = paths[counter]; int[] rows = rowMapper.getRowsForPaths(tempPath); if (rows == null) { return false; } anIndex = rows[0]; if(anIndex == -1 || anIndex < (min - pathCount) || anIndex > (min + pathCount)) return false; if(anIndex < min) min = anIndex; if(!bitSet.get(anIndex)) { bitSet.set(anIndex); validCount++; } } } int maxCounter = validCount + min; for(counter = min; counter < maxCounter; counter++) if(!bitSet.get(counter)) return false; } return true; } /** * Used to test if a particular set of <code>TreePaths can * be added. This will return true if <code>paths is null (or * empty), or this object has no RowMapper, or nothing is currently selected, * or the selection mode is <code>DISCONTIGUOUS_TREE_SELECTION, or * adding the paths to the current selection still results in a * contiguous set of <code>TreePaths. */ protected boolean canPathsBeAdded(TreePath[] paths) { if(paths == null || paths.length == 0 || rowMapper == null || selection == null || selectionMode == TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION) return true; else { BitSet bitSet = new BitSet(); DefaultListSelectionModel lModel = listSelectionModel; int anIndex; int counter; int min = lModel.getMinSelectionIndex(); int max = lModel.getMaxSelectionIndex(); TreePath[] tempPath = new TreePath[1]; if(min != -1) { for(counter = min; counter <= max; counter++) { if(lModel.isSelectedIndex(counter)) bitSet.set(counter); } } else { tempPath[0] = paths[0]; min = max = rowMapper.getRowsForPaths(tempPath)[0]; } for(counter = paths.length - 1; counter >= 0; counter--) { if(paths[counter] != null) { tempPath[0] = paths[counter]; int[] rows = rowMapper.getRowsForPaths(tempPath); if (rows == null) { return false; } anIndex = rows[0]; min = Math.min(anIndex, min); max = Math.max(anIndex, max); if(anIndex == -1) return false; bitSet.set(anIndex); } } for(counter = min; counter <= max; counter++) if(!bitSet.get(counter)) return false; } return true; } /** * Returns true if the paths can be removed without breaking the * continuity of the model. * This is rather expensive. */ protected boolean canPathsBeRemoved(TreePath[] paths) { if(rowMapper == null || selection == null || selectionMode == TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION) return true; else { BitSet bitSet = new BitSet(); int counter; int pathCount = paths.length; int anIndex; int min = -1; int validCount = 0; TreePath[] tempPath = new TreePath[1]; int[] rows; /* Determine the rows for the removed entries. */ lastPaths.clear(); for (counter = 0; counter < pathCount; counter++) { if (paths[counter] != null) { lastPaths.put(paths[counter], Boolean.TRUE); } } for(counter = selection.length - 1; counter >= 0; counter--) { if(lastPaths.get(selection[counter]) == null) { tempPath[0] = selection[counter]; rows = rowMapper.getRowsForPaths(tempPath); if(rows != null && rows[0] != -1 && !bitSet.get(rows[0])) { validCount++; if(min == -1) min = rows[0]; else min = Math.min(min, rows[0]); bitSet.set(rows[0]); } } } lastPaths.clear(); /* Make sure they are contiguous. */ if(validCount > 1) { for(counter = min + validCount - 1; counter >= min; counter--) if(!bitSet.get(counter)) return false; } } return true; } /** * Notifies listeners of a change in path. changePaths should contain * instances of PathPlaceHolder. * * @deprecated As of JDK version 1.7 */ @Deprecated protected void notifyPathChange(Vector<?> changedPaths, TreePath oldLeadSelection) { int cPathCount = changedPaths.size(); boolean[] newness = new boolean[cPathCount]; TreePath[] paths = new TreePath[cPathCount]; PathPlaceHolder placeholder; for(int counter = 0; counter < cPathCount; counter++) { placeholder = (PathPlaceHolder) changedPaths.elementAt(counter); newness[counter] = placeholder.isNew; paths[counter] = placeholder.path; } TreeSelectionEvent event = new TreeSelectionEvent (this, paths, newness, oldLeadSelection, leadPath); fireValueChanged(event); } /** * Updates the leadIndex instance variable. */ protected void updateLeadIndex() { if(leadPath != null) { if(selection == null) { leadPath = null; leadIndex = leadRow = -1; } else { leadRow = leadIndex = -1; for(int counter = selection.length - 1; counter >= 0; counter--) { // Can use == here since we know leadPath came from // selection if(selection[counter] == leadPath) { leadIndex = counter; break; } } } } else { leadIndex = -1; } } /** * This method is obsolete and its implementation is now a noop. It's * still called by setSelectionPaths and addSelectionPaths, but only * for backwards compatibility. */ protected void insureUniqueness() { } /** * Returns a string that displays and identifies this * object's properties. * * @return a String representation of this object */ public String toString() { int selCount = getSelectionCount(); StringBuffer retBuffer = new StringBuffer(); int[] rows; if(rowMapper != null) rows = rowMapper.getRowsForPaths(selection); else rows = null; retBuffer.append(getClass().getName() + " " + hashCode() + " [ "); for(int counter = 0; counter < selCount; counter++) { if(rows != null) retBuffer.append(selection[counter].toString() + "@" + Integer.toString(rows[counter])+ " "); else retBuffer.append(selection[counter].toString() + " "); } retBuffer.append("]"); return retBuffer.toString(); } /** * Returns a clone of this object with the same selection. * This method does not duplicate * selection listeners and property listeners. * * @exception CloneNotSupportedException never thrown by instances of * this class */ public Object clone() throws CloneNotSupportedException { DefaultTreeSelectionModel clone = (DefaultTreeSelectionModel) super.clone(); clone.changeSupport = null; if(selection != null) { int selLength = selection.length; clone.selection = new TreePath[selLength]; System.arraycopy(selection, 0, clone.selection, 0, selLength); } clone.listenerList = new EventListenerList(); clone.listSelectionModel = (DefaultListSelectionModel) listSelectionModel.clone(); clone.uniquePaths = new Hashtable<TreePath, Boolean>(); clone.lastPaths = new Hashtable<TreePath, Boolean>(); clone.tempPaths = new TreePath[1]; return clone; } // Serialization support. private void writeObject(ObjectOutputStream s) throws IOException { Object[] tValues; s.defaultWriteObject(); // Save the rowMapper, if it implements Serializable if(rowMapper != null && rowMapper instanceof Serializable) { tValues = new Object[2]; tValues[0] = "rowMapper"; tValues[1] = rowMapper; } else tValues = new Object[0]; s.writeObject(tValues); } private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException { Object[] tValues; s.defaultReadObject(); tValues = (Object[])s.readObject(); if(tValues.length > 0 && tValues[0].equals("rowMapper")) rowMapper = (RowMapper)tValues[1]; } } /** * Holds a path and whether or not it is new. */ class PathPlaceHolder { protected boolean isNew; protected TreePath path; PathPlaceHolder(TreePath path, boolean isNew) { this.path = path; this.isNew = isNew; } }

Other Java examples (source code examples)

Here is a short list of links related to this Java DefaultTreeSelectionModel.java source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.