/* * JTree.java -- Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc. * * This file is part of GNU Classpath. * * GNU Classpath is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2, or (at your option) any later version. * * GNU Classpath 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 for more * details. * * You should have received a copy of the GNU General Public License along with * GNU Classpath; see the file COPYING. If not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * Linking this library statically or dynamically with other modules is making a * combined work based on this library. Thus, the terms and conditions of the * GNU General Public License cover the whole combination. * * As a special exception, the copyright holders of this library give you * permission to link this library with independent modules to produce an * executable, regardless of the license terms of these independent modules, and * to copy and distribute the resulting executable under terms of your choice, * provided that you also meet, for each linked independent module, the terms * and conditions of the license of that module. An independent module is a * module which is not derived from or based on this library. If you modify this * library, you may extend this exception to your version of the library, but * you are not obligated to do so. If you do not wish to do so, delete this * exception statement from your version. */ package javax.swing; import java.awt.Dimension; import java.awt.Rectangle; import java.io.Serializable; import java.util.Enumeration; import java.util.Hashtable; import java.util.Iterator; import java.util.Vector; import javax.accessibility.Accessible; import javax.accessibility.AccessibleContext; import javax.swing.event.TreeExpansionEvent; import javax.swing.event.TreeExpansionListener; import javax.swing.event.TreeModelEvent; import javax.swing.event.TreeModelListener; import javax.swing.event.TreeSelectionEvent; import javax.swing.event.TreeSelectionListener; import javax.swing.event.TreeWillExpandListener; import javax.swing.plaf.TreeUI; import javax.swing.text.Position; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.DefaultTreeCellRenderer; import javax.swing.tree.DefaultTreeModel; import javax.swing.tree.DefaultTreeSelectionModel; import javax.swing.tree.ExpandVetoException; import javax.swing.tree.TreeCellEditor; import javax.swing.tree.TreeCellRenderer; import javax.swing.tree.TreeModel; import javax.swing.tree.TreeNode; import javax.swing.tree.TreePath; import javax.swing.tree.TreeSelectionModel; public class JTree extends JComponent implements Scrollable, Accessible { /** * Listens to the model of the JTree and updates the property * expandedState if nodes are removed or changed. */ protected class TreeModelHandler implements TreeModelListener { /** * Creates a new instance of TreeModelHandler. */ protected TreeModelHandler() { } /** * Notifies when a node has changed in some ways. This does not include * that a node has changed its location or changed it's children. It * only means that some attributes of the node have changed that might * affect its presentation. * * This method is called after the actual change occured. * * @param ev the TreeModelEvent describing the change */ public void treeNodesChanged(TreeModelEvent ev) { // nothing to do here } /** * Notifies when a node is inserted into the tree. * * This method is called after the actual change occured. * * @param ev the TreeModelEvent describing the change */ public void treeNodesInserted(TreeModelEvent ev) { // nothing to do here } /** * Notifies when a node is removed from the tree. * * This method is called after the actual change occured. * * @param ev the TreeModelEvent describing the change */ public void treeNodesRemoved(TreeModelEvent ev) { // TODO: The API docs suggest that this method should do something // but I cannot really see what has to be done here ... } /** * Notifies when the structure of the tree is changed. * * This method is called after the actual change occured. * * @param ev the TreeModelEvent describing the change */ public void treeStructureChanged(TreeModelEvent ev) { // set state of new path TreePath path = ev.getTreePath(); setExpandedState(path, isExpanded(path)); } } // TreeModelHandler /** * This redirects TreeSelectionEvents and rewrites the source of it to be * this JTree. This is typically done when the tree model generates an * event, but the JTree object associated with that model should be listed * as the actual source of the event. */ protected class TreeSelectionRedirector implements TreeSelectionListener, Serializable { /** The serial version UID. */ private static final long serialVersionUID = -3505069663646241664L; /** * Creates a new instance of TreeSelectionRedirector */ protected TreeSelectionRedirector() { } /** * Notifies when the tree selection changes. * * @param ev the TreeSelectionEvent that describes the change */ public void valueChanged(TreeSelectionEvent ev) { TreeSelectionEvent rewritten = (TreeSelectionEvent) ev .cloneWithSource(JTree.this); fireValueChanged(rewritten); JTree.this.repaint(); } } // TreeSelectionRedirector /** * A TreeModel that does not allow anything to be selected. */ protected static class EmptySelectionModel extends DefaultTreeSelectionModel { /** The serial version UID. */ private static final long serialVersionUID = -5815023306225701477L; /** * The shared instance of this model. */ protected static final EmptySelectionModel sharedInstance = new EmptySelectionModel(); /** * Creates a new instance of EmptySelectionModel. */ protected EmptySelectionModel() { } /** * Returns the shared instance of EmptySelectionModel. * * @return the shared instance of EmptySelectionModel */ public static EmptySelectionModel sharedInstance() { return sharedInstance; } /** * This catches attempts to set a selection and sets nothing instead. * * @param paths not used here */ public void setSelectionPaths(TreePath[] paths) { // we don't allow selections in this class } /** * This catches attempts to add something to the selection. * * @param paths not used here */ public void addSelectionPaths(TreePath[] paths) { // we don't allow selections in this class } /** * This catches attempts to remove something from the selection. * * @param paths not used here */ public void removeSelectionPaths(TreePath[] paths) { // we don't allow selections in this class } }// EmptySelectionModel private static final long serialVersionUID = 7559816092864483649L; public static final String CELL_EDITOR_PROPERTY = "cellEditor"; public static final String CELL_RENDERER_PROPERTY = "cellRenderer"; public static final String EDITABLE_PROPERTY = "editable"; public static final String INVOKES_STOP_CELL_EDITING_PROPERTY = "invokesStopCellEditing"; public static final String LARGE_MODEL_PROPERTY = "largeModel"; public static final String ROOT_VISIBLE_PROPERTY = "rootVisible"; public static final String ROW_HEIGHT_PROPERTY = "rowHeight"; public static final String SCROLLS_ON_EXPAND_PROPERTY = "scrollsOnExpand"; public static final String SELECTION_MODEL_PROPERTY = "selectionModel"; public static final String SHOWS_ROOT_HANDLES_PROPERTY = "showsRootHandles"; public static final String TOGGLE_CLICK_COUNT_PROPERTY = "toggleClickCount"; public static final String TREE_MODEL_PROPERTY = "model"; public static final String VISIBLE_ROW_COUNT_PROPERTY = "visibleRowCount"; /** @since 1.3 */ public static final String ANCHOR_SELECTION_PATH_PROPERTY = "anchorSelectionPath"; /** @since 1.3 */ public static final String LEAD_SELECTION_PATH_PROPERTY = "leadSelectionPath"; /** @since 1.3 */ public static final String EXPANDS_SELECTED_PATHS_PROPERTY = "expandsSelectedPaths"; private static final Object EXPANDED = new Object(); private static final Object COLLAPSED = new Object(); private boolean dragEnabled; private boolean expandsSelectedPaths; private TreePath anchorSelectionPath; private TreePath leadSelectionPath; /* * This contains the state of all nodes in the tree. Al/ entries map the * TreePath of a note to to its state. Valid states are EXPANDED and * COLLAPSED. Nodes not in this Hashtable are assumed state COLLAPSED. */ private Hashtable nodeStates; protected transient TreeCellEditor cellEditor; protected transient TreeCellRenderer cellRenderer; protected boolean editable; protected boolean invokesStopCellEditing; protected boolean largeModel; protected boolean rootVisible; protected int rowHeight; protected boolean scrollsOnExpand; protected transient TreeSelectionModel selectionModel; protected boolean showsRootHandles; protected int toggleClickCount; protected transient TreeModel treeModel; protected int visibleRowCount; /** * Handles TreeModelEvents to update the expandedState. */ protected transient TreeModelListener treeModelListener; /** * Redirects TreeSelectionEvents so that the source is this JTree. */ protected TreeSelectionRedirector selectionRedirector = new TreeSelectionRedirector(); /** * Creates a new JTree object. */ public JTree() { this(createTreeModel(null)); } /** * Creates a new JTree object. * * @param value the initial nodes in the tree */ public JTree(Hashtable value) { this(createTreeModel(value)); } /** * Creates a new JTree object. * * @param value the initial nodes in the tree */ public JTree(Object[] value) { this(createTreeModel(value)); } /** * Creates a new JTree object. * * @param model the model to use */ public JTree(TreeModel model) { setModel(model); setSelectionModel(EmptySelectionModel.sharedInstance()); selectionModel.addTreeSelectionListener(selectionRedirector); setCellRenderer(new DefaultTreeCellRenderer()); updateUI(); } /** * Creates a new JTree object. * * @param root the root node */ public JTree(TreeNode root) { this(root, false); } /** * Creates a new JTree object. * * @param root the root node * @param asksAllowChildren if false, all nodes without children are leaf * nodes. If true, only nodes that do not allow children are leaf * nodes. */ public JTree(TreeNode root, boolean asksAllowChildren) { this(new DefaultTreeModel(root, asksAllowChildren)); } /** * Creates a new JTree object. * * @param value the initial nodes in the tree */ public JTree(Vector value) { this(createTreeModel(value)); } public static class DynamicUtilTreeNode extends DefaultMutableTreeNode { protected Object childValue; protected boolean loadedChildren; /** * Currently not set or used by this class. It might be set and used in * later versions of this class. */ protected boolean hasChildren; public DynamicUtilTreeNode(Object value, Object children) { super(value); childValue = children; loadedChildren = false; } public int getChildCount() { loadChildren(); return super.getChildCount(); } protected void loadChildren() { if (!loadedChildren) { createChildren(this, childValue); loadedChildren = true; } } public Enumeration children() { loadChildren(); return super.children(); } /** * Returns the child node at position pos. Subclassed * here to load the children if necessary. * * @param pos the position of the child node to fetch * * @return the childnode at the specified position */ public TreeNode getChildAt(int pos) { loadChildren(); return super.getChildAt(pos); } public boolean isLeaf() { return (childValue == null || !(childValue instanceof Hashtable || childValue instanceof Vector || childValue.getClass() .isArray())); } public static void createChildren(DefaultMutableTreeNode parent, Object children) { if (children instanceof Hashtable) { Hashtable tab = (Hashtable) children; Enumeration e = tab.keys(); while (e.hasMoreElements()) { Object key = e.nextElement(); Object val = tab.get(key); parent.add(new DynamicUtilTreeNode(key, val)); } } else if (children instanceof Vector) { Iterator i = ((Vector) children).iterator(); while (i.hasNext()) { Object n = i.next(); parent.add(new DynamicUtilTreeNode(n, n)); } } else if (children != null && children.getClass().isArray()) { Object[] arr = (Object[]) children; for (int i = 0; i < arr.length; ++i) parent.add(new DynamicUtilTreeNode(arr[i], arr[i])); } } } public int getRowForPath(TreePath path) { TreeUI ui = getUI(); if (ui != null) return ui.getRowForPath(this, path); return -1; } public TreePath getPathForRow(int row) { TreeUI ui = getUI(); return ui != null ? ui.getPathForRow(this, row) : null; } protected TreePath[] getPathBetweenRows(int index0, int index1) { TreeUI ui = getUI(); if (ui == null) return null; int minIndex = Math.min(index0, index1); int maxIndex = Math.max(index0, index1); TreePath[] paths = new TreePath[maxIndex - minIndex + 1]; for (int i = minIndex; i <= maxIndex; ++i) paths[i - minIndex] = ui.getPathForRow(this, i); return paths; } /** * Creates a new TreeModel object. * * @param value the values stored in the model */ protected static TreeModel createTreeModel(Object value) { return new DefaultTreeModel(new DynamicUtilTreeNode(value, value)); } /** * Return the UI associated with this JTree object. * * @return the associated TreeUI object */ public TreeUI getUI() { return (TreeUI) ui; } /** * Sets the UI associated with this JTree object. * * @param ui the TreeUI to associate */ public void setUI(TreeUI ui) { super.setUI(ui); } /** * This method resets the UI used to the Look and Feel defaults.. */ public void updateUI() { setUI((TreeUI) UIManager.getUI(this)); revalidate(); repaint(); } /** * This method returns the String ID of the UI class of Separator. * * @return The UI class' String ID. */ public String getUIClassID() { return "TreeUI"; } /** * Gets the AccessibleContext associated with this * JToggleButton. * * @return the associated context */ public AccessibleContext getAccessibleContext() { return null; } /** * Returns the preferred viewport size.. * * @return the preferred size */ public Dimension getPreferredScrollableViewportSize() { return null; } public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) { return 1; } public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) { return 1; } public boolean getScrollableTracksViewportWidth() { return false; } public boolean getScrollableTracksViewportHeight() { return false; } /** * Adds a TreeExpansionListener object to the tree. * * @param listener the listener to add */ public void addTreeExpansionListener(TreeExpansionListener listener) { listenerList.add(TreeExpansionListener.class, listener); } /** * Removes a TreeExpansionListener object from the tree. * * @param listener the listener to remove */ public void removeTreeExpansionListener(TreeExpansionListener listener) { listenerList.remove(TreeExpansionListener.class, listener); } /** * Returns all added TreeExpansionListener objects. * * @return an array of listeners */ public TreeExpansionListener[] getTreeExpansionListeners() { return (TreeExpansionListener[]) getListeners(TreeExpansionListener.class); } /** * Notifies all listeners that the tree was collapsed. * * @param path the path to the node that was collapsed */ public void fireTreeCollapsed(TreePath path) { TreeExpansionEvent event = new TreeExpansionEvent(this, path); TreeExpansionListener[] listeners = getTreeExpansionListeners(); for (int index = 0; index < listeners.length; ++index) listeners[index].treeCollapsed(event); } /** * Notifies all listeners that the tree was expanded. * * @param path the path to the node that was expanded */ public void fireTreeExpanded(TreePath path) { TreeExpansionEvent event = new TreeExpansionEvent(this, path); TreeExpansionListener[] listeners = getTreeExpansionListeners(); for (int index = 0; index < listeners.length; ++index) listeners[index].treeExpanded(event); } /** * Adds a TreeSelctionListener object to the tree. * * @param listener the listener to add */ public void addTreeSelectionListener(TreeSelectionListener listener) { listenerList.add(TreeSelectionListener.class, listener); } /** * Removes a TreeSelectionListener object from the tree. * * @param listener the listener to remove */ public void removeTreeSelectionListener(TreeSelectionListener listener) { listenerList.remove(TreeSelectionListener.class, listener); } /** * Returns all added TreeSelectionListener objects. * * @return an array of listeners */ public TreeSelectionListener[] getTreeSelectionListeners() { return (TreeSelectionListener[]) getListeners(TreeSelectionListener.class); } /** * Notifies all listeners when the selection of the tree changed. * * @param event the event to send */ protected void fireValueChanged(TreeSelectionEvent event) { TreeSelectionListener[] listeners = getTreeSelectionListeners(); for (int index = 0; index < listeners.length; ++index) listeners[index].valueChanged(event); } /** * Adds a TreeWillExpandListener object to the tree. * * @param listener the listener to add */ public void addTreeWillExpandListener(TreeWillExpandListener listener) { listenerList.add(TreeWillExpandListener.class, listener); } /** * Removes a TreeWillExpandListener object from the tree. * * @param listener the listener to remove */ public void removeTreeWillExpandListener(TreeWillExpandListener listener) { listenerList.remove(TreeWillExpandListener.class, listener); } /** * Returns all added TreeWillExpandListener objects. * * @return an array of listeners */ public TreeWillExpandListener[] getTreeWillExpandListeners() { return (TreeWillExpandListener[]) getListeners(TreeWillExpandListener.class); } /** * Notifies all listeners that the tree will collapse. * * @param path the path to the node that will collapse */ public void fireTreeWillCollapse(TreePath path) throws ExpandVetoException { TreeExpansionEvent event = new TreeExpansionEvent(this, path); TreeWillExpandListener[] listeners = getTreeWillExpandListeners(); for (int index = 0; index < listeners.length; ++index) listeners[index].treeWillCollapse(event); } /** * Notifies all listeners that the tree will expand. * * @param path the path to the node that will expand */ public void fireTreeWillExpand(TreePath path) throws ExpandVetoException { TreeExpansionEvent event = new TreeExpansionEvent(this, path); TreeWillExpandListener[] listeners = getTreeWillExpandListeners(); for (int index = 0; index < listeners.length; ++index) listeners[index].treeWillExpand(event); } /** * Returns the model of this JTree object. * * @return the associated TreeModel */ public TreeModel getModel() { return treeModel; } /** * Sets the model to use in JTree. * * @param model the TreeModel to use */ public void setModel(TreeModel model) { if (treeModel == model) return; TreeModel oldValue = treeModel; treeModel = model; firePropertyChange(TREE_MODEL_PROPERTY, oldValue, model); // add treeModelListener to the new model if (treeModelListener == null) treeModelListener = createTreeModelListener(); model.addTreeModelListener(treeModelListener); } /** * Checks if this JTree object is editable. * * @return true if this tree object is editable, * false otherwise */ public boolean isEditable() { return editable; } /** * Sets the editable property. * * @param flag true to make this tree object editable, * false otherwise */ public void setEditable(boolean flag) { if (editable == flag) return; boolean oldValue = editable; editable = flag; firePropertyChange(EDITABLE_PROPERTY, oldValue, editable); } /** * Checks if the root element is visible. * * @return true if the root element is visible, * false otherwise */ public boolean isRootVisible() { return rootVisible; } public void setRootVisible(boolean flag) { if (rootVisible == flag) return; boolean oldValue = rootVisible; rootVisible = flag; firePropertyChange(ROOT_VISIBLE_PROPERTY, oldValue, flag); } public boolean getShowsRootHandles() { return showsRootHandles; } public void setShowsRootHandles(boolean flag) { if (showsRootHandles == flag) return; boolean oldValue = showsRootHandles; showsRootHandles = flag; firePropertyChange(SHOWS_ROOT_HANDLES_PROPERTY, oldValue, flag); } public TreeCellEditor getCellEditor() { return cellEditor; } public void setCellEditor(TreeCellEditor editor) { if (cellEditor == editor) return; TreeCellEditor oldValue = cellEditor; cellEditor = editor; firePropertyChange(CELL_EDITOR_PROPERTY, oldValue, editor); } public TreeCellRenderer getCellRenderer() { return cellRenderer; } public void setCellRenderer(TreeCellRenderer newRenderer) { if (cellRenderer == newRenderer) return; TreeCellRenderer oldValue = cellRenderer; cellRenderer = newRenderer; firePropertyChange(CELL_RENDERER_PROPERTY, oldValue, newRenderer); } public TreeSelectionModel getSelectionModel() { return selectionModel; } public void setSelectionModel(TreeSelectionModel model) { if (selectionModel == model) return; if (selectionModel != null) selectionModel.removeTreeSelectionListener(selectionRedirector); TreeSelectionModel oldValue = selectionModel; selectionModel = model; if (selectionModel != null) selectionModel.addTreeSelectionListener(selectionRedirector); firePropertyChange(SELECTION_MODEL_PROPERTY, oldValue, model); revalidate(); repaint(); } public int getVisibleRowCount() { return visibleRowCount; } public void setVisibleRowCount(int rows) { if (visibleRowCount == rows) return; int oldValue = visibleRowCount; visibleRowCount = rows; firePropertyChange(VISIBLE_ROW_COUNT_PROPERTY, oldValue, rows); } public boolean isLargeModel() { return largeModel; } public void setLargeModel(boolean large) { if (largeModel == large) return; boolean oldValue = largeModel; largeModel = large; firePropertyChange(LARGE_MODEL_PROPERTY, oldValue, large); } public int getRowHeight() { return rowHeight; } public void setRowHeight(int height) { if (rowHeight == height) return; int oldValue = rowHeight; rowHeight = height; firePropertyChange(ROW_HEIGHT_PROPERTY, oldValue, height); } public boolean isFixedRowHeight() { return rowHeight > 0; } public boolean getInvokesStopCellEditing() { return invokesStopCellEditing; } public void setInvokesStopCellEditing(boolean invoke) { if (invokesStopCellEditing == invoke) return; boolean oldValue = invokesStopCellEditing; invokesStopCellEditing = invoke; firePropertyChange(INVOKES_STOP_CELL_EDITING_PROPERTY, oldValue, invoke); } /** * @since 1.3 */ public int getToggleClickCount() { return toggleClickCount; } /** * @since 1.3 */ public void setToggleClickCount(int count) { if (toggleClickCount == count) return; int oldValue = toggleClickCount; toggleClickCount = count; firePropertyChange(TOGGLE_CLICK_COUNT_PROPERTY, oldValue, count); } public void scrollPathToVisible(TreePath path) { if (path == null) return; Rectangle rect = getPathBounds(path); if (rect == null) return; scrollRectToVisible(rect); } public void scrollRowToVisible(int row) { scrollPathToVisible(getPathForRow(row)); } public boolean getScrollsOnExpand() { return scrollsOnExpand; } public void setScrollsOnExpand(boolean scroll) { if (scrollsOnExpand == scroll) return; boolean oldValue = scrollsOnExpand; scrollsOnExpand = scroll; firePropertyChange(SCROLLS_ON_EXPAND_PROPERTY, oldValue, scroll); } public void setSelectionPath(TreePath path) { selectionModel.setSelectionPath(path); } public void setSelectionPaths(TreePath[] paths) { selectionModel.setSelectionPaths(paths); } public void setSelectionRow(int row) { TreePath path = getPathForRow(row); if (path != null) selectionModel.setSelectionPath(path); } public void setSelectionRows(int[] rows) { // Make sure we have an UI so getPathForRow() does not return null. if (rows == null || getUI() == null) return; TreePath[] paths = new TreePath[rows.length]; for (int i = rows.length - 1; i >= 0; --i) paths[i] = getPathForRow(rows[i]); setSelectionPaths(paths); } public void setSelectionInterval(int index0, int index1) { TreePath[] paths = getPathBetweenRows(index0, index1); if (paths != null) setSelectionPaths(paths); } public void addSelectionPath(TreePath path) { selectionModel.addSelectionPath(path); } public void addSelectionPaths(TreePath[] paths) { selectionModel.addSelectionPaths(paths); } public void addSelectionRow(int row) { TreePath path = getPathForRow(row); if (path != null) selectionModel.addSelectionPath(path); } public void addSelectionRows(int[] rows) { // Make sure we have an UI so getPathForRow() does not return null. if (rows == null || getUI() == null) return; TreePath[] paths = new TreePath[rows.length]; for (int i = rows.length - 1; i >= 0; --i) paths[i] = getPathForRow(rows[i]); addSelectionPaths(paths); } public void addSelectionInterval(int index0, int index1) { TreePath[] paths = getPathBetweenRows(index0, index1); if (paths != null) addSelectionPaths(paths); } public void removeSelectionPath(TreePath path) { selectionModel.removeSelectionPath(path); } public void removeSelectionPaths(TreePath[] paths) { selectionModel.removeSelectionPaths(paths); } public void removeSelectionRow(int row) { TreePath path = getPathForRow(row); if (path != null) selectionModel.removeSelectionPath(path); } public void removeSelectionRows(int[] rows) { if (rows == null || getUI() == null) return; TreePath[] paths = new TreePath[rows.length]; for (int i = rows.length - 1; i >= 0; --i) paths[i] = getPathForRow(rows[i]); removeSelectionPaths(paths); } public void removeSelectionInterval(int index0, int index1) { TreePath[] paths = getPathBetweenRows(index0, index1); if (paths != null) removeSelectionPaths(paths); } public void clearSelection() { selectionModel.clearSelection(); } public TreePath getLeadSelectionPath() { return leadSelectionPath; } /** * @since 1.3 */ public void setLeadSelectionPath(TreePath path) { if (leadSelectionPath == path) return; TreePath oldValue = leadSelectionPath; leadSelectionPath = path; firePropertyChange(LEAD_SELECTION_PATH_PROPERTY, oldValue, path); } /** * @since 1.3 */ public TreePath getAnchorSelectionPath() { return anchorSelectionPath; } /** * @since 1.3 */ public void setAnchorSelectionPath(TreePath path) { if (anchorSelectionPath == path) return; TreePath oldValue = anchorSelectionPath; anchorSelectionPath = path; firePropertyChange(ANCHOR_SELECTION_PATH_PROPERTY, oldValue, path); } public int getLeadSelectionRow() { return selectionModel.getLeadSelectionRow(); } public int getMaxSelectionRow() { return selectionModel.getMaxSelectionRow(); } public int getMinSelectionRow() { return selectionModel.getMinSelectionRow(); } public int getSelectionCount() { return selectionModel.getSelectionCount(); } public TreePath getSelectionPath() { return selectionModel.getSelectionPath(); } public TreePath[] getSelectionPaths() { return selectionModel.getSelectionPaths(); } public int[] getSelectionRows() { return selectionModel.getSelectionRows(); } public boolean isPathSelected(TreePath path) { return selectionModel.isPathSelected(path); } public boolean isRowSelected(int row) { return selectionModel.isPathSelected(getPathForRow(row)); } public boolean isSelectionEmpty() { return selectionModel.isSelectionEmpty(); } /** * Return the value of the dragEnabled property. * * @return the value * * @since 1.4 */ public boolean getDragEnabled() { return dragEnabled; } /** * Set the dragEnabled property. * * @param enabled new value * * @since 1.4 */ public void setDragEnabled(boolean enabled) { dragEnabled = enabled; } public int getRowCount() { TreeUI ui = getUI(); if (ui != null) return ui.getRowCount(this); return 0; } public void collapsePath(TreePath path) { setExpandedState(path, false); } public void collapseRow(int row) { if (row < 0 || row >= getRowCount()) return; TreePath path = getPathForRow(row); if (path != null) collapsePath(path); } public void expandPath(TreePath path) { // Don't expand if last path component is a leaf node. if ((path == null) || (treeModel.isLeaf(path.getLastPathComponent()))) return; setExpandedState(path, true); } public void expandRow(int row) { if (row < 0 || row >= getRowCount()) return; TreePath path = getPathForRow(row); if (path != null) expandPath(path); } public boolean isCollapsed(TreePath path) { return !isExpanded(path); } public boolean isCollapsed(int row) { if (row < 0 || row >= getRowCount()) return false; TreePath path = getPathForRow(row); if (path != null) return isCollapsed(path); return false; } public boolean isExpanded(TreePath path) { if (path == null) return false; Object state = nodeStates.get(path); if ((state == null) || (state != EXPANDED)) return false; TreePath parent = path.getParentPath(); if (parent != null) return isExpanded(parent); return true; } public boolean isExpanded(int row) { if (row < 0 || row >= getRowCount()) return false; TreePath path = getPathForRow(row); if (path != null) return isExpanded(path); return false; } /** * @since 1.3 */ public boolean getExpandsSelectedPaths() { return expandsSelectedPaths; } /** * @since 1.3 */ public void setExpandsSelectedPaths(boolean flag) { if (expandsSelectedPaths == flag) return; boolean oldValue = expandsSelectedPaths; expandsSelectedPaths = flag; firePropertyChange(EXPANDS_SELECTED_PATHS_PROPERTY, oldValue, flag); } public Rectangle getPathBounds(TreePath path) { TreeUI ui = getUI(); if (ui == null) return null; return ui.getPathBounds(this, path); } public Rectangle getRowBounds(int row) { TreePath path = getPathForRow(row); if (path != null) return getPathBounds(path); return null; } public boolean isEditing() { TreeUI ui = getUI(); if (ui != null) return ui.isEditing(this); return false; } public boolean stopEditing() { TreeUI ui = getUI(); if (ui != null) return ui.stopEditing(this); return false; } public void cancelEditing() { TreeUI ui = getUI(); if (ui != null) ui.cancelEditing(this); } public void startEditingAtPath(TreePath path) { TreeUI ui = getUI(); if (ui != null) ui.startEditingAtPath(this, path); } public TreePath getEditingPath() { TreeUI ui = getUI(); if (ui != null) return ui.getEditingPath(this); return null; } public TreePath getPathForLocation(int x, int y) { TreePath path = getClosestPathForLocation(x, y); if (path != null) { Rectangle rect = getPathBounds(path); if ((rect != null) && rect.contains(x, y)) return path; } return null; } public int getRowForLocation(int x, int y) { TreePath path = getPathForLocation(x, y); if (path != null) return getRowForPath(path); return -1; } public TreePath getClosestPathForLocation(int x, int y) { TreeUI ui = getUI(); if (ui != null) return ui.getClosestPathForLocation(this, x, y); return null; } public int getClosestRowForLocation(int x, int y) { TreePath path = getClosestPathForLocation(x, y); if (path != null) return getRowForPath(path); return -1; } public Object getLastSelectedPathComponent() { TreePath path = getSelectionPath(); if (path != null) return path.getLastPathComponent(); return null; } private void checkExpandParents(TreePath path) throws ExpandVetoException { TreePath parent = path.getParentPath(); if (parent != null) checkExpandParents(parent); fireTreeWillExpand(path); } private void doExpandParents(TreePath path, boolean state) { TreePath parent = path.getParentPath(); if (isExpanded(parent)) return; if (parent != null) doExpandParents(parent, false); nodeStates.put(path, state ? EXPANDED : COLLAPSED); } protected void setExpandedState(TreePath path, boolean state) { if (path == null) return; TreePath parent = path.getParentPath(); try { while (parent != null) checkExpandParents(parent); } catch (ExpandVetoException e) { // Expansion vetoed. return; } doExpandParents(path, state); } protected void clearToggledPaths() { nodeStates.clear(); } protected Enumeration getDescendantToggledPaths(TreePath parent) { if (parent == null) return null; Enumeration nodes = nodeStates.keys(); Vector result = new Vector(); while (nodes.hasMoreElements()) { TreePath path = (TreePath) nodes.nextElement(); if (path.isDescendant(parent)) result.addElement(path); } return result.elements(); } public boolean hasBeenExpanded(TreePath path) { if (path == null) return false; return nodeStates.get(path) != null; } public boolean isVisible(TreePath path) { if (path == null) return false; TreePath parent = path.getParentPath(); if (parent == null) return true; // Is root node. return isExpanded(parent); } public void makeVisible(TreePath path) { if (path == null) return; expandPath(path.getParentPath()); } public boolean isPathEditable(TreePath path) { return isEditable(); } /** * Creates and returns an instance of {@link TreeModelHandler}. * * @returns an instance of {@link TreeModelHandler} */ protected TreeModelListener createTreeModelListener() { return new TreeModelHandler(); } /** * Returns a sample TreeModel that can be used in a JTree. This can be used * in Bean- or GUI-Builders to show something interesting. * * @return a sample TreeModel that can be used in a JTree */ protected static TreeModel getDefaultTreeModel() { DefaultMutableTreeNode root = new DefaultMutableTreeNode("Root node"); DefaultMutableTreeNode child1 = new DefaultMutableTreeNode( "Child node 1"); DefaultMutableTreeNode child11 = new DefaultMutableTreeNode( "Child node 1.1"); DefaultMutableTreeNode child12 = new DefaultMutableTreeNode( "Child node 1.2"); DefaultMutableTreeNode child13 = new DefaultMutableTreeNode( "Child node 1.3"); DefaultMutableTreeNode child2 = new DefaultMutableTreeNode( "Child node 2"); DefaultMutableTreeNode child21 = new DefaultMutableTreeNode( "Child node 2.1"); DefaultMutableTreeNode child22 = new DefaultMutableTreeNode( "Child node 2.2"); DefaultMutableTreeNode child23 = new DefaultMutableTreeNode( "Child node 2.3"); DefaultMutableTreeNode child24 = new DefaultMutableTreeNode( "Child node 2.4"); DefaultMutableTreeNode child3 = new DefaultMutableTreeNode( "Child node 3"); root.add(child1); root.add(child2); root.add(child3); child1.add(child11); child1.add(child12); child1.add(child13); child2.add(child21); child2.add(child22); child2.add(child23); child2.add(child24); return new DefaultTreeModel(root); } /** * Converts the specified value to a String. This is used by the renderers * of this JTree and its nodes. * * This implementation simply returns value.toString() and * ignores all other parameters. Subclass this method to control the * conversion. * * @param value the value that is converted to a String * @param selected indicates if that value is selected or not * @param expanded indicates if that value is expanded or not * @param leaf indicates if that value is a leaf node or not * @param row the row of the node * @param hasFocus indicates if that node has focus or not */ public String convertValueToText(Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) { return value.toString(); } /** * A String representation of this JTree. This is intended to be used for * debugging. The returned string may be empty but may not be * null. * * @return a String representation of this JTree */ public String paramString() { // TODO: this is completely legal, but it would possibly be nice // to return some more content, like the tree structure, some properties // etc ... return ""; } /** * Returns all TreePath objects which are a descendants of the given path * and are exapanded at the moment of the execution of this method. If the * state of any node is beeing toggled while this method is executing this * change may be left unaccounted. * * @param path The parent of this request * @return An Enumeration containing TreePath objects */ public Enumeration getExpandedDescendants(TreePath path) { Enumeration paths = nodeStates.keys(); Vector relevantPaths = new Vector(); while (paths.hasMoreElements()) { TreePath nextPath = (TreePath) paths.nextElement(); if (nodeStates.get(nextPath) == EXPANDED && path.isDescendant(nextPath)) { relevantPaths.add(nextPath); } } return relevantPaths.elements(); } /** * Returns the next table element (beginning from the row * startingRow that starts with prefix. * Searching is done in the direction specified by bias. * * @param prefix the prefix to search for in the cell values * @param startingRow the index of the row where to start searching from * @param bias the search direction, either {@link Position.Bias.Forward} or * {@link Position.Bias.Backward} * * @return the path to the found element or -1 if no such element has been * found * * @throws IllegalArgumentException if prefix is null or * startingRow is not valid * * @since 1.4 */ public TreePath getNextMatch(String prefix, int startingRow, Position.Bias bias) { if (prefix == null) throw new IllegalArgumentException( "The argument 'prefix' must not be" + " null."); if (startingRow < 0) throw new IllegalArgumentException( "The argument 'startingRow' must not" + " be less than zero."); int size = getRowCount(); if (startingRow > size) throw new IllegalArgumentException( "The argument 'startingRow' must not" + " be greater than the number of" + " elements in the TreeModel."); TreePath foundPath = null; if (bias == Position.Bias.Forward) { for (int i = startingRow; i < size; i++) { TreePath path = getPathForRow(i); Object o = path.getLastPathComponent(); // FIXME: in the following call to convertValueToText the // last argument (hasFocus) should be done right. String item = convertValueToText(o, isRowSelected(i), isExpanded(i), treeModel.isLeaf(o), i, false); if (item.startsWith(prefix)) { foundPath = path; break; } } } else { for (int i = startingRow; i >= 0; i--) { TreePath path = getPathForRow(i); Object o = path.getLastPathComponent(); // FIXME: in the following call to convertValueToText the // last argument (hasFocus) should be done right. String item = convertValueToText(o, isRowSelected(i), isExpanded(i), treeModel.isLeaf(o), i, false); if (item.startsWith(prefix)) { foundPath = path; break; } } } return foundPath; } /** * Removes any paths in the current set of selected paths that are * descendants of path. If includePath is set * to true and path itself is selected, then * it will be removed too. * * @param path the path from which selected descendants are to be removed * @param includePath if true then path itself * will also be remove if it's selected * * @return true if something has been removed, * false otherwise * * @since 1.3 */ protected boolean removeDescendantSelectedPaths(TreePath path, boolean includeSelected) { boolean removedSomething = false; TreePath[] selected = getSelectionPaths(); for (int index = 0; index < selected.length; index++) { if ((selected[index] == path && includeSelected) || (selected[index].isDescendant(path))) { removeSelectionPath(selected[index]); removedSomething = true; } } return removedSomething; } }