1 /* JTree.java 2 Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc. 3 4 This file is part of GNU Classpath. 5 6 GNU Classpath is free software; you can redistribute it and/or modify 7 it under the terms of the GNU General Public License as published by 8 the Free Software Foundation; either version 2, or (at your option) 9 any later version. 10 11 GNU Classpath is distributed in the hope that it will be useful, but 12 WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 General Public License for more details. 15 16 You should have received a copy of the GNU General Public License 17 along with GNU Classpath; see the file COPYING. If not, write to the 18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 19 02110-1301 USA. 20 21 Linking this library statically or dynamically with other modules is 22 making a combined work based on this library. Thus, the terms and 23 conditions of the GNU General Public License cover the whole 24 combination. 25 26 As a special exception, the copyright holders of this library give you 27 permission to link this library with independent modules to produce an 28 executable, regardless of the license terms of these independent 29 modules, and to copy and distribute the resulting executable under 30 terms of your choice, provided that you also meet, for each linked 31 independent module, the terms and conditions of the license of that 32 module. An independent module is a module which is not derived from 33 or based on this library. If you modify this library, you may extend 34 this exception to your version of the library, but you are not 35 obligated to do so. If you do not wish to do so, delete this 36 exception statement from your version. */ 37 38 package javax.swing; 39 40 import java.awt.Color; 41 import java.awt.Cursor; 42 import java.awt.Dimension; 43 import java.awt.Font; 44 import java.awt.FontMetrics; 45 import java.awt.Point; 46 import java.awt.Rectangle; 47 import java.awt.event.FocusListener; 48 import java.beans.PropertyChangeListener; 49 import java.io.Serializable; 50 import java.util.Enumeration; 51 import java.util.Hashtable; 52 import java.util.Iterator; 53 import java.util.Locale; 54 import java.util.Vector; 55 56 import javax.accessibility.Accessible; 57 import javax.accessibility.AccessibleAction; 58 import javax.accessibility.AccessibleComponent; 59 import javax.accessibility.AccessibleContext; 60 import javax.accessibility.AccessibleRole; 61 import javax.accessibility.AccessibleSelection; 62 import javax.accessibility.AccessibleState; 63 import javax.accessibility.AccessibleStateSet; 64 import javax.accessibility.AccessibleText; 65 import javax.accessibility.AccessibleValue; 66 import javax.swing.event.TreeExpansionEvent; 67 import javax.swing.event.TreeExpansionListener; 68 import javax.swing.event.TreeModelEvent; 69 import javax.swing.event.TreeModelListener; 70 import javax.swing.event.TreeSelectionEvent; 71 import javax.swing.event.TreeSelectionListener; 72 import javax.swing.event.TreeWillExpandListener; 73 import javax.swing.plaf.TreeUI; 74 import javax.swing.text.Position; 75 import javax.swing.tree.DefaultMutableTreeNode; 76 import javax.swing.tree.DefaultTreeModel; 77 import javax.swing.tree.DefaultTreeSelectionModel; 78 import javax.swing.tree.ExpandVetoException; 79 import javax.swing.tree.TreeCellEditor; 80 import javax.swing.tree.TreeCellRenderer; 81 import javax.swing.tree.TreeModel; 82 import javax.swing.tree.TreeNode; 83 import javax.swing.tree.TreePath; 84 import javax.swing.tree.TreeSelectionModel; 85 86 public class JTree extends JComponent implements Scrollable, Accessible 87 { 88 89 /** 90 * This class implements accessibility support for the JTree class. It 91 * provides an implementation of the Java Accessibility API appropriate 92 * to tree user-interface elements. 93 */ 94 protected class AccessibleJTree extends JComponent.AccessibleJComponent 95 implements AccessibleSelection, TreeSelectionListener, TreeModelListener, 96 TreeExpansionListener 97 { 98 99 /** 100 * This class implements accessibility support for the JTree child. It provides 101 * an implementation of the Java Accessibility API appropriate to tree nodes. 102 */ 103 protected class AccessibleJTreeNode extends AccessibleContext 104 implements Accessible, AccessibleComponent, AccessibleSelection, 105 AccessibleAction 106 { 107 108 private JTree tree; 109 private TreePath tp; 110 private Accessible acc; 111 private AccessibleStateSet states; 112 private Vector selectionList; 113 private Vector actionList; 114 private TreeModel mod; 115 private Cursor cursor; 116 117 /** 118 * Constructs an AccessibleJTreeNode 119 * 120 * @param t - the current tree 121 * @param p - the current path to be dealt with 122 * @param ap - the accessible object to use 123 */ AccessibleJTreeNode(JTree t, TreePath p, Accessible ap)124 public AccessibleJTreeNode(JTree t, TreePath p, Accessible ap) 125 { 126 states = new AccessibleStateSet(); 127 selectionList = new Vector(); 128 actionList = new Vector(); 129 mod = tree.getModel(); 130 cursor = JTree.this.getCursor(); 131 132 tree = t; 133 tp = p; 134 acc = ap; 135 136 // Add all the children of this path that may already be 137 // selected to the selection list. 138 TreePath[] selected = tree.getSelectionPaths(); 139 for (int i = 0; i < selected.length; i++) 140 { 141 TreePath sel = selected[i]; 142 if ((sel.getParentPath()).equals(tp)) 143 selectionList.add(sel); 144 } 145 146 // Add all the actions available for a node to 147 // the action list. 148 actionList.add("EXPAND"); 149 actionList.add("COLLAPSE"); 150 actionList.add("EDIT"); 151 actionList.add("SELECT"); 152 actionList.add("DESELECT"); 153 } 154 155 /** 156 * Adds the specified selected item in the object to the object's 157 * selection. 158 * 159 * @param i - the i-th child of this node. 160 */ addAccessibleSelection(int i)161 public void addAccessibleSelection(int i) 162 { 163 if (mod != null) 164 { 165 Object child = mod.getChild(tp.getLastPathComponent(), i); 166 if (child != null) 167 { 168 if (!states.contains(AccessibleState.MULTISELECTABLE)) 169 clearAccessibleSelection(); 170 selectionList.add(child); 171 tree.addSelectionPath(tp.pathByAddingChild(child)); 172 } 173 } 174 } 175 176 /** 177 * Adds the specified focus listener to receive focus events 178 * from this component. 179 * 180 * @param l - the new focus listener 181 */ addFocusListener(FocusListener l)182 public void addFocusListener(FocusListener l) 183 { 184 tree.addFocusListener(l); 185 } 186 187 /** 188 * Add a PropertyChangeListener to the listener list. 189 * 190 * @param l - the new property change listener 191 */ addPropertyChangeListener(PropertyChangeListener l)192 public void addPropertyChangeListener(PropertyChangeListener l) 193 { 194 // Nothing to do here. 195 } 196 197 /** 198 * Clears the selection in the object, so that nothing in the 199 * object is selected. 200 */ clearAccessibleSelection()201 public void clearAccessibleSelection() 202 { 203 selectionList.clear(); 204 } 205 206 /** 207 * Checks whether the specified point is within this object's 208 * bounds, where the point's x and y coordinates are defined to be 209 * relative to the coordinate system of the object. 210 * 211 * @param p - the point to check 212 * @return true if p is in the bounds 213 */ contains(Point p)214 public boolean contains(Point p) 215 { 216 return getBounds().contains(p); 217 } 218 219 /** 220 * Perform the specified Action on the tree node. 221 * 222 * @param i - the i-th action to perform 223 * @return true if the the action was performed; else false. 224 */ doAccessibleAction(int i)225 public boolean doAccessibleAction(int i) 226 { 227 if (i >= actionList.size() || i < 0) 228 return false; 229 230 if (actionList.get(i).equals("EXPAND")) 231 tree.expandPath(tp); 232 else if (actionList.get(i).equals("COLLAPSE")) 233 tree.collapsePath(tp); 234 else if (actionList.get(i).equals("SELECT")) 235 tree.addSelectionPath(tp); 236 else if (actionList.get(i).equals("DESELECT")) 237 tree.removeSelectionPath(tp); 238 else if (actionList.get(i).equals("EDIT")) 239 tree.startEditingAtPath(tp); 240 else 241 return false; 242 return true; 243 } 244 245 /** 246 * Get the AccessibleAction associated with this object. 247 * 248 * @return the action 249 */ getAccessibleAction()250 public AccessibleAction getAccessibleAction() 251 { 252 return this; 253 } 254 255 /** 256 * Returns the number of accessible actions available in this tree node. 257 * 258 * @return the number of actions 259 */ getAccessibleActionCount()260 public int getAccessibleActionCount() 261 { 262 return actionList.size(); 263 } 264 265 /** 266 * Return a description of the specified action of the tree node. 267 * 268 * @param i - the i-th action's description 269 * @return a description of the action 270 */ getAccessibleActionDescription(int i)271 public String getAccessibleActionDescription(int i) 272 { 273 if (i < 0 || i >= actionList.size()) 274 return (actionList.get(i)).toString(); 275 return super.getAccessibleDescription(); 276 } 277 278 /** 279 * Returns the Accessible child, if one exists, contained at the 280 * local coordinate Point. 281 * 282 * @param p - the point of the accessible 283 * @return the accessible at point p if it exists 284 */ getAccessibleAt(Point p)285 public Accessible getAccessibleAt(Point p) 286 { 287 TreePath acc = tree.getClosestPathForLocation(p.x, p.y); 288 if (acc != null) 289 return new AccessibleJTreeNode(tree, acc, this); 290 return null; 291 } 292 293 /** 294 * Return the specified Accessible child of the object. 295 * 296 * @param i - the i-th child of the current path 297 * @return the child if it exists 298 */ getAccessibleChild(int i)299 public Accessible getAccessibleChild(int i) 300 { 301 if (mod != null) 302 { 303 Object child = mod.getChild(tp.getLastPathComponent(), i); 304 if (child != null) 305 return new AccessibleJTreeNode(tree, tp.pathByAddingChild(child), 306 acc); 307 } 308 return null; 309 } 310 311 /** 312 * Returns the number of accessible children in the object. 313 * 314 * @return the number of children the current node has 315 */ getAccessibleChildrenCount()316 public int getAccessibleChildrenCount() 317 { 318 TreeModel mod = getModel(); 319 if (mod != null) 320 return mod.getChildCount(tp.getLastPathComponent()); 321 return 0; 322 } 323 324 /** 325 * Get the AccessibleComponent associated with this object. 326 * 327 * @return the accessible component if it is supported. 328 */ getAccessibleComponent()329 public AccessibleComponent getAccessibleComponent() 330 { 331 return this; 332 } 333 334 /** 335 * Get the AccessibleContext associated with this tree node. 336 * 337 * @return an instance of this class 338 */ getAccessibleContext()339 public AccessibleContext getAccessibleContext() 340 { 341 return this; 342 } 343 344 /** 345 * Get the accessible description of this object. 346 * 347 * @return the accessible description 348 */ getAccessibleDescription()349 public String getAccessibleDescription() 350 { 351 return super.getAccessibleDescription(); 352 } 353 354 /** 355 * Get the index of this object in its accessible parent. 356 * 357 * @return the index of this in the parent. 358 */ getAccessibleIndexInParent()359 public int getAccessibleIndexInParent() 360 { 361 AccessibleContext parent = getAccessibleParent().getAccessibleContext(); 362 if (parent != null) 363 for (int i = 0; i < parent.getAccessibleChildrenCount(); i++) 364 { 365 if ((parent.getAccessibleChild(i)).equals(this)) 366 return i; 367 } 368 return -1; 369 } 370 371 /** 372 * Get the accessible name of this object. 373 * 374 * @return the accessible name 375 */ getAccessibleName()376 public String getAccessibleName() 377 { 378 return super.getAccessibleName(); 379 } 380 381 /** 382 * Get the Accessible parent of this object. 383 * 384 * @return the accessible parent if it exists. 385 */ getAccessibleParent()386 public Accessible getAccessibleParent() 387 { 388 return super.getAccessibleParent(); 389 } 390 391 /** 392 * Get the role of this object. 393 * 394 * @return the accessible role 395 */ getAccessibleRole()396 public AccessibleRole getAccessibleRole() 397 { 398 return AccessibleJTree.this.getAccessibleRole(); 399 } 400 401 /** 402 * Get the AccessibleSelection associated with this object if one exists. 403 * 404 * @return the accessible selection for this. 405 */ getAccessibleSelection()406 public AccessibleSelection getAccessibleSelection() 407 { 408 return this; 409 } 410 411 /** 412 * Returns an Accessible representing the specified selected item 413 * in the object. 414 * 415 * @return the accessible representing a certain selected item. 416 */ getAccessibleSelection(int i)417 public Accessible getAccessibleSelection(int i) 418 { 419 if (i > 0 && i < getAccessibleSelectionCount()) 420 return new AccessibleJTreeNode(tree, 421 tp.pathByAddingChild(selectionList.get(i)), acc); 422 return null; 423 } 424 425 /** 426 * Returns the number of items currently selected. 427 * 428 * @return the number of items selected. 429 */ getAccessibleSelectionCount()430 public int getAccessibleSelectionCount() 431 { 432 return selectionList.size(); 433 } 434 435 /** 436 * Get the state set of this object. 437 * 438 * @return the state set for this object 439 */ getAccessibleStateSet()440 public AccessibleStateSet getAccessibleStateSet() 441 { 442 if (isVisible()) 443 states.add(AccessibleState.VISIBLE); 444 if (tree.isCollapsed(tp)) 445 states.add(AccessibleState.COLLAPSED); 446 if (tree.isEditable()) 447 states.add(AccessibleState.EDITABLE); 448 if (mod != null && 449 !mod.isLeaf(tp.getLastPathComponent())) 450 states.add(AccessibleState.EXPANDABLE); 451 if (tree.isExpanded(tp)) 452 states.add(AccessibleState.EXPANDED); 453 if (isFocusable()) 454 states.add(AccessibleState.FOCUSABLE); 455 if (hasFocus()) 456 states.add(AccessibleState.FOCUSED); 457 if (tree.getSelectionModel().getSelectionMode() != 458 TreeSelectionModel.SINGLE_TREE_SELECTION) 459 states.add(AccessibleState.MULTISELECTABLE); 460 if (tree.isOpaque()) 461 states.add(AccessibleState.OPAQUE); 462 if (tree.isPathSelected(tp)) 463 states.add(AccessibleState.SELECTED); 464 if (isShowing()) 465 states.add(AccessibleState.SHOWING); 466 467 states.add(AccessibleState.SELECTABLE); 468 return states; 469 } 470 471 /** 472 * Get the AccessibleText associated with this object if one exists. 473 * 474 * @return the accessible text 475 */ getAccessibleText()476 public AccessibleText getAccessibleText() 477 { 478 return super.getAccessibleText(); 479 } 480 481 /** 482 * Get the AccessibleValue associated with this object if one exists. 483 * 484 * @return the accessible value if it exists 485 */ getAccessibleValue()486 public AccessibleValue getAccessibleValue() 487 { 488 return super.getAccessibleValue(); 489 } 490 491 /** 492 * Get the background color of this object. 493 * 494 * @return the color of the background. 495 */ getBackground()496 public Color getBackground() 497 { 498 return tree.getBackground(); 499 } 500 501 /** 502 * Gets the bounds of this object in the form of a Rectangle object. 503 * 504 * @return the bounds of the current node. 505 */ getBounds()506 public Rectangle getBounds() 507 { 508 return tree.getPathBounds(tp); 509 } 510 511 /** 512 * Gets the Cursor of this object. 513 * 514 * @return the cursor for the current node 515 */ getCursor()516 public Cursor getCursor() 517 { 518 return cursor; 519 } 520 521 /** 522 * Gets the Font of this object. 523 * 524 * @return the font for the current node 525 */ getFont()526 public Font getFont() 527 { 528 return tree.getFont(); 529 } 530 531 /** 532 * Gets the FontMetrics of this object. 533 * 534 * @param f - the current font. 535 * @return the font metrics for the given font. 536 */ getFontMetrics(Font f)537 public FontMetrics getFontMetrics(Font f) 538 { 539 return tree.getFontMetrics(f); 540 } 541 542 /** 543 * Get the foreground color of this object. 544 * 545 * @return the foreground for this object. 546 */ getForeground()547 public Color getForeground() 548 { 549 return tree.getForeground(); 550 } 551 552 /** 553 * Gets the locale of the component. 554 * 555 * @return the locale of the component. 556 */ getLocale()557 public Locale getLocale() 558 { 559 return tree.getLocale(); 560 } 561 562 /** 563 * Gets the location of the object relative to the 564 * parent in the form of a point specifying the object's 565 * top-left corner in the screen's coordinate space. 566 * 567 * @return the location of the current node. 568 */ getLocation()569 public Point getLocation() 570 { 571 return getLocationInJTree(); 572 } 573 574 /** 575 * Returns the location in the tree. 576 * 577 * @return the location in the JTree. 578 */ getLocationInJTree()579 protected Point getLocationInJTree() 580 { 581 Rectangle bounds = tree.getPathBounds(tp); 582 return new Point(bounds.x, bounds.y); 583 } 584 585 /** 586 * Returns the location of the object on the screen. 587 * 588 * @return the location of the object on the screen. 589 */ getLocationOnScreen()590 public Point getLocationOnScreen() 591 { 592 Point loc = getLocation(); 593 SwingUtilities.convertPointToScreen(loc, tree); 594 return loc; 595 } 596 597 /** 598 * Returns the size of this object in the form of a Dimension object. 599 * 600 * @return the size of the object 601 */ getSize()602 public Dimension getSize() 603 { 604 Rectangle b = getBounds(); 605 return b.getSize(); 606 } 607 608 /** 609 * Returns true if the current child of this object is selected. 610 * 611 * @param i - the child of the current node 612 * @return true if the child is selected. 613 */ isAccessibleChildSelected(int i)614 public boolean isAccessibleChildSelected(int i) 615 { 616 Object child = mod.getChild(tp.getLastPathComponent(), i); 617 if (child != null) 618 return tree.isPathSelected(tp.pathByAddingChild(child)); 619 return false; 620 } 621 622 /** 623 * Determines if the object is enabled. 624 * 625 * @return true if the tree is enabled 626 */ isEnabled()627 public boolean isEnabled() 628 { 629 return tree.isEnabled(); 630 } 631 632 /** 633 * Returns whether this object can accept focus or not. 634 * 635 * @return true, it is always focus traversable 636 */ isFocusTraversable()637 public boolean isFocusTraversable() 638 { 639 return true; 640 } 641 642 /** 643 * Determines if the object is showing. 644 * 645 * @return true if the object is visible and the 646 * parent is visible. 647 */ isShowing()648 public boolean isShowing() 649 { 650 return isVisible() && tree.isShowing(); 651 } 652 653 /** 654 * Determines if the object is visible. 655 * 656 * @return true if the object is visible. 657 */ isVisible()658 public boolean isVisible() 659 { 660 return tree.isVisible(tp); 661 } 662 663 /** 664 * Removes the specified selected item in the object from the 665 * object's selection. 666 * 667 * @param i - the specified item to remove 668 */ removeAccessibleSelection(int i)669 public void removeAccessibleSelection(int i) 670 { 671 if (mod != null) 672 { 673 Object child = mod.getChild(tp.getLastPathComponent(), i); 674 if (child != null) 675 { 676 if (!states.contains(AccessibleState.MULTISELECTABLE)) 677 clearAccessibleSelection(); 678 if (selectionList.contains(child)) 679 { 680 selectionList.remove(child); 681 tree.removeSelectionPath(tp.pathByAddingChild(child)); 682 } 683 } 684 } 685 } 686 687 /** 688 * Removes the specified focus listener so it no longer receives focus 689 * events from this component. 690 * 691 * @param l - the focus listener to remove 692 */ removeFocusListener(FocusListener l)693 public void removeFocusListener(FocusListener l) 694 { 695 tree.removeFocusListener(l); 696 } 697 698 /** 699 * Remove a PropertyChangeListener from the listener list. 700 * 701 * @param l - the property change listener to remove. 702 */ removePropertyChangeListener(PropertyChangeListener l)703 public void removePropertyChangeListener(PropertyChangeListener l) 704 { 705 // Nothing to do here. 706 } 707 708 /** 709 * Requests focus for this object. 710 */ requestFocus()711 public void requestFocus() 712 { 713 tree.requestFocus(); 714 } 715 716 /** 717 * Causes every selected item in the object to be selected if the object 718 * supports multiple selections. 719 */ selectAllAccessibleSelection()720 public void selectAllAccessibleSelection() 721 { 722 Object parent = tp.getLastPathComponent(); 723 if (mod != null) 724 { 725 for (int i = 0; i < mod.getChildCount(parent); i++) 726 { 727 Object child = mod.getChild(parent, i); 728 if (child != null) 729 { 730 if (!states.contains(AccessibleState.MULTISELECTABLE)) 731 clearAccessibleSelection(); 732 if (selectionList.contains(child)) 733 { 734 selectionList.add(child); 735 tree.addSelectionPath(tp.pathByAddingChild(child)); 736 } 737 } 738 } 739 } 740 } 741 742 /** 743 * Set the accessible description of this object. 744 * 745 * @param s - the string to set the accessible description to. 746 */ setAccessibleDescription(String s)747 public void setAccessibleDescription(String s) 748 { 749 super.setAccessibleDescription(s); 750 } 751 752 /** 753 * Set the localized accessible name of this object. 754 * 755 * @param s - the string to set the accessible name to. 756 */ setAccessibleName(String s)757 public void setAccessibleName(String s) 758 { 759 super.setAccessibleName(s); 760 } 761 762 /** 763 * Set the background color of this object. 764 * 765 * @param c - the color to set the background to. 766 */ setBackground(Color c)767 public void setBackground(Color c) 768 { 769 // Nothing to do here. 770 } 771 772 /** 773 * Sets the bounds of this object in the form of a Rectangle object. 774 * 775 * @param r - the bounds to set the object o 776 */ setBounds(Rectangle r)777 public void setBounds(Rectangle r) 778 { 779 // Nothing to do here. 780 } 781 782 /** 783 * Sets the Cursor of this object. 784 * 785 * @param c - the new cursor 786 */ setCursor(Cursor c)787 public void setCursor(Cursor c) 788 { 789 cursor = c; 790 } 791 792 /** 793 * Sets the enabled state of the object. 794 * 795 * @param b - boolean to enable or disable object 796 */ setEnabled(boolean b)797 public void setEnabled(boolean b) 798 { 799 // Nothing to do here. 800 } 801 802 /** 803 * Sets the Font of this object. 804 * 805 * @param f - the new font. 806 */ setFont(Font f)807 public void setFont(Font f) 808 { 809 // Nothing to do here. 810 } 811 812 /** 813 * Sets the foreground color of this object. 814 * 815 * @param c - the new foreground color. 816 */ setForeground(Color c)817 public void setForeground(Color c) 818 { 819 // Nothing to do here. 820 } 821 822 /** 823 * Sets the location of the object relative to the parent. 824 * 825 * @param p - the new location for the object. 826 */ setLocation(Point p)827 public void setLocation(Point p) 828 { 829 // Nothing to do here. 830 } 831 832 /** 833 * Resizes this object so that it has width and height. 834 * 835 * @param d - the new size for the object. 836 */ setSize(Dimension d)837 public void setSize(Dimension d) 838 { 839 // Nothing to do here. 840 } 841 842 /** 843 * Sets the visible state of the object. 844 * 845 * @param b - sets the objects visibility. 846 */ setVisible(boolean b)847 public void setVisible(boolean b) 848 { 849 // Nothing to do here. 850 } 851 } 852 853 /** 854 * Constructor 855 */ AccessibleJTree()856 public AccessibleJTree() 857 { 858 // Nothing to do here. 859 } 860 861 /** 862 * Adds the specified selected item in the object to the object's selection. 863 * 864 * @param i - the row to add to the tree's selection 865 */ addAccessibleSelection(int i)866 public void addAccessibleSelection(int i) 867 { 868 addSelectionInterval(i, i); 869 } 870 871 /** 872 * Clears the selection in the object, so that nothing in the object is selected. 873 */ clearAccessibleSelection()874 public void clearAccessibleSelection() 875 { 876 clearSelection(); 877 } 878 879 /** 880 * Fire a visible data property change notification. 881 */ fireVisibleDataPropertyChange()882 public void fireVisibleDataPropertyChange() 883 { 884 treeDidChange(); 885 } 886 887 /** 888 * Returns the Accessible child, if one exists, contained at the local 889 * coordinate Point. 890 * 891 * @param p - the point of the accessible to get. 892 * @return the accessible at point p. 893 */ getAccessibleAt(Point p)894 public Accessible getAccessibleAt(Point p) 895 { 896 TreePath tp = getClosestPathForLocation(p.x, p.y); 897 if (tp != null) 898 return new AccessibleJTreeNode(JTree.this, tp, null); 899 return null; 900 } 901 902 /** 903 * Return the nth Accessible child of the object. 904 * 905 * @param i - the accessible child to get 906 * @return the i-th child 907 */ getAccessibleChild(int i)908 public Accessible getAccessibleChild(int i) 909 { 910 return null; 911 } 912 913 /** 914 * Returns the number of top-level children nodes of this JTree. 915 * 916 * @return the number of top-level children 917 */ getAccessibleChildrenCount()918 public int getAccessibleChildrenCount() 919 { 920 TreeModel model = getModel(); 921 if (model != null) 922 return model.getChildCount(model.getRoot()); 923 return 0; 924 } 925 926 /** 927 * Get the index of this object in its accessible parent. 928 * 929 * @return the index of this object. 930 */ getAccessibleIndexInParent()931 public int getAccessibleIndexInParent() 932 { 933 return 0; 934 } 935 936 /** 937 * Get the role of this object. 938 * 939 * @return the role of this object 940 */ getAccessibleRole()941 public AccessibleRole getAccessibleRole() 942 { 943 return AccessibleRole.TREE; 944 } 945 946 /** 947 * Get the AccessibleSelection associated with this object. 948 * 949 * @return the accessible selection of the tree 950 */ getAccessibleSelection()951 public AccessibleSelection getAccessibleSelection() 952 { 953 TreeModel mod = getModel(); 954 if (mod != null) 955 return (new AccessibleJTreeNode(JTree.this, 956 new TreePath(mod.getRoot()), null)).getAccessibleSelection(); 957 return null; 958 } 959 960 /** 961 * Returns an Accessible representing the specified selected item in the object. 962 * 963 * @return the i-th accessible in the selection 964 */ getAccessibleSelection(int i)965 public Accessible getAccessibleSelection(int i) 966 { 967 TreeModel mod = getModel(); 968 if (mod != null) 969 return (new AccessibleJTreeNode(JTree.this, 970 new TreePath(mod.getRoot()), null)).getAccessibleSelection(i); 971 return null; 972 } 973 974 /** 975 * Returns the number of items currently selected. 976 * 977 * @return the number of selected accessibles. 978 */ getAccessibleSelectionCount()979 public int getAccessibleSelectionCount() 980 { 981 return getSelectionCount(); 982 } 983 984 /** 985 * Returns true if the current child of this object is selected. 986 * 987 * @param i - the child of this object 988 * @return true if the i-th child is selected. 989 */ isAccessibleChildSelected(int i)990 public boolean isAccessibleChildSelected(int i) 991 { 992 // Nothing to do here. 993 return false; 994 } 995 996 /** 997 * Removes the specified selected item in the object from the object's 998 * selection. 999 * 1000 * @param i - the i-th selected item to remove 1001 */ removeAccessibleSelection(int i)1002 public void removeAccessibleSelection(int i) 1003 { 1004 removeSelectionInterval(i, i); 1005 } 1006 1007 /** 1008 * Causes every selected item in the object to be selected if the object 1009 * supports multiple selections. 1010 */ selectAllAccessibleSelection()1011 public void selectAllAccessibleSelection() 1012 { 1013 if (getSelectionModel().getSelectionMode() != 1014 TreeSelectionModel.SINGLE_TREE_SELECTION) 1015 addSelectionInterval(0, getVisibleRowCount()); 1016 } 1017 1018 /** 1019 * Tree Collapsed notification 1020 * 1021 * @param e - the event 1022 */ treeCollapsed(TreeExpansionEvent e)1023 public void treeCollapsed(TreeExpansionEvent e) 1024 { 1025 fireTreeCollapsed(e.getPath()); 1026 } 1027 1028 /** 1029 * Tree Model Expansion notification. 1030 * 1031 * @param e - the event 1032 */ treeExpanded(TreeExpansionEvent e)1033 public void treeExpanded(TreeExpansionEvent e) 1034 { 1035 fireTreeExpanded(e.getPath()); 1036 } 1037 1038 /** 1039 * Tree Model Node change notification. 1040 * 1041 * @param e - the event 1042 */ treeNodesChanged(TreeModelEvent e)1043 public void treeNodesChanged(TreeModelEvent e) 1044 { 1045 // Nothing to do here. 1046 } 1047 1048 /** 1049 * Tree Model Node change notification. 1050 * 1051 * @param e - the event 1052 */ treeNodesInserted(TreeModelEvent e)1053 public void treeNodesInserted(TreeModelEvent e) 1054 { 1055 // Nothing to do here. 1056 } 1057 1058 /** 1059 * Tree Model Node change notification. 1060 * 1061 * @param e - the event 1062 */ treeNodesRemoved(TreeModelEvent e)1063 public void treeNodesRemoved(TreeModelEvent e) 1064 { 1065 // Nothing to do here. 1066 } 1067 1068 /** 1069 * Tree Model structure change change notification. 1070 * 1071 * @param e - the event 1072 */ treeStructureChanged(TreeModelEvent e)1073 public void treeStructureChanged(TreeModelEvent e) 1074 { 1075 // Nothing to do here. 1076 } 1077 1078 /** 1079 * Tree Selection Listener value change method. 1080 * 1081 * @param e - the event 1082 */ valueChanged(TreeSelectionEvent e)1083 public void valueChanged(TreeSelectionEvent e) 1084 { 1085 fireValueChanged(e); 1086 } 1087 } 1088 1089 public static class DynamicUtilTreeNode extends DefaultMutableTreeNode 1090 { 1091 protected Object childValue; 1092 1093 protected boolean loadedChildren; 1094 1095 /** 1096 * Currently not set or used by this class. It might be set and used in 1097 * later versions of this class. 1098 */ 1099 protected boolean hasChildren; 1100 DynamicUtilTreeNode(Object value, Object children)1101 public DynamicUtilTreeNode(Object value, Object children) 1102 { 1103 super(value); 1104 childValue = children; 1105 loadedChildren = false; 1106 } 1107 getChildCount()1108 public int getChildCount() 1109 { 1110 loadChildren(); 1111 return super.getChildCount(); 1112 } 1113 loadChildren()1114 protected void loadChildren() 1115 { 1116 if (!loadedChildren) 1117 { 1118 createChildren(this, childValue); 1119 loadedChildren = true; 1120 } 1121 } 1122 children()1123 public Enumeration children() 1124 { 1125 loadChildren(); 1126 return super.children(); 1127 } 1128 1129 /** 1130 * Returns the child node at position <code>pos</code>. Subclassed 1131 * here to load the children if necessary. 1132 * 1133 * @param pos the position of the child node to fetch 1134 * 1135 * @return the childnode at the specified position 1136 */ getChildAt(int pos)1137 public TreeNode getChildAt(int pos) 1138 { 1139 loadChildren(); 1140 return super.getChildAt(pos); 1141 } 1142 isLeaf()1143 public boolean isLeaf() 1144 { 1145 return childValue == null || !(childValue instanceof Hashtable 1146 || childValue instanceof Vector 1147 || childValue.getClass().isArray()); 1148 } 1149 createChildren(DefaultMutableTreeNode parent, Object children)1150 public static void createChildren(DefaultMutableTreeNode parent, 1151 Object children) 1152 { 1153 if (children instanceof Hashtable) 1154 { 1155 Hashtable tab = (Hashtable) children; 1156 Enumeration e = tab.keys(); 1157 while (e.hasMoreElements()) 1158 { 1159 Object key = e.nextElement(); 1160 Object val = tab.get(key); 1161 parent.add(new DynamicUtilTreeNode(key, val)); 1162 } 1163 } 1164 else if (children instanceof Vector) 1165 { 1166 Iterator i = ((Vector) children).iterator(); 1167 while (i.hasNext()) 1168 { 1169 Object n = i.next(); 1170 parent.add(new DynamicUtilTreeNode(n, n)); 1171 } 1172 } 1173 else if (children != null && children.getClass().isArray()) 1174 { 1175 Object[] arr = (Object[]) children; 1176 for (int i = 0; i < arr.length; ++i) 1177 parent.add(new DynamicUtilTreeNode(arr[i], arr[i])); 1178 } 1179 } 1180 } 1181 1182 /** 1183 * Listens to the model of the JTree and updates the property 1184 * <code>expandedState</code> if nodes are removed or changed. 1185 */ 1186 protected class TreeModelHandler implements TreeModelListener 1187 { 1188 1189 /** 1190 * Creates a new instance of TreeModelHandler. 1191 */ TreeModelHandler()1192 protected TreeModelHandler() 1193 { 1194 // Nothing to do here. 1195 } 1196 1197 /** 1198 * Notifies when a node has changed in some ways. This does not include 1199 * that a node has changed its location or changed it's children. It 1200 * only means that some attributes of the node have changed that might 1201 * affect its presentation. 1202 * 1203 * This method is called after the actual change occured. 1204 * 1205 * @param ev the TreeModelEvent describing the change 1206 */ treeNodesChanged(TreeModelEvent ev)1207 public void treeNodesChanged(TreeModelEvent ev) 1208 { 1209 // Nothing to do here. 1210 } 1211 1212 /** 1213 * Notifies when a node is inserted into the tree. 1214 * 1215 * This method is called after the actual change occured. 1216 * 1217 * @param ev the TreeModelEvent describing the change 1218 */ treeNodesInserted(TreeModelEvent ev)1219 public void treeNodesInserted(TreeModelEvent ev) 1220 { 1221 // nothing to do here 1222 } 1223 1224 /** 1225 * Notifies when a node is removed from the tree. 1226 * 1227 * This method is called after the actual change occured. 1228 * 1229 * @param ev the TreeModelEvent describing the change 1230 */ treeNodesRemoved(TreeModelEvent ev)1231 public void treeNodesRemoved(TreeModelEvent ev) 1232 { 1233 if (ev != null) 1234 { 1235 TreePath parent = ev.getTreePath(); 1236 Object[] children = ev.getChildren(); 1237 TreeSelectionModel sm = getSelectionModel(); 1238 if (children != null) 1239 { 1240 TreePath path; 1241 Vector toRemove = new Vector(); 1242 // Collect items that we must remove. 1243 for (int i = children.length - 1; i >= 0; i--) 1244 { 1245 path = parent.pathByAddingChild(children[i]); 1246 if (nodeStates.containsKey(path)) 1247 toRemove.add(path); 1248 // Clear selection while we are at it. 1249 if (sm != null) 1250 removeDescendantSelectedPaths(path, true); 1251 } 1252 if (toRemove.size() > 0) 1253 removeDescendantToggledPaths(toRemove.elements()); 1254 TreeModel model = getModel(); 1255 if (model == null || model.isLeaf(parent.getLastPathComponent())) 1256 nodeStates.remove(parent); 1257 } 1258 } 1259 } 1260 1261 /** 1262 * Notifies when the structure of the tree is changed. 1263 * 1264 * This method is called after the actual change occured. 1265 * 1266 * @param ev the TreeModelEvent describing the change 1267 */ treeStructureChanged(TreeModelEvent ev)1268 public void treeStructureChanged(TreeModelEvent ev) 1269 { 1270 if (ev != null) 1271 { 1272 TreePath parent = ev.getTreePath(); 1273 if (parent != null) 1274 { 1275 if (parent.getPathCount() == 1) 1276 { 1277 // We have a new root, clear everything. 1278 clearToggledPaths(); 1279 Object root = treeModel.getRoot(); 1280 if (root != null && treeModel.isLeaf(root)) 1281 nodeStates.put(parent, Boolean.TRUE); 1282 } 1283 else if (nodeStates.containsKey(parent)) 1284 { 1285 Vector toRemove = new Vector(); 1286 boolean expanded = isExpanded(parent); 1287 toRemove.add(parent); 1288 removeDescendantToggledPaths(toRemove.elements()); 1289 if (expanded) 1290 { 1291 TreeModel model = getModel(); 1292 if (model != null 1293 || model.isLeaf(parent.getLastPathComponent())) 1294 collapsePath(parent); 1295 else 1296 nodeStates.put(parent, Boolean.TRUE); 1297 } 1298 } 1299 removeDescendantSelectedPaths(parent, false); 1300 } 1301 } 1302 } 1303 } 1304 1305 /** 1306 * This redirects TreeSelectionEvents and rewrites the source of it to be 1307 * this JTree. This is typically done when the tree model generates an 1308 * event, but the JTree object associated with that model should be listed 1309 * as the actual source of the event. 1310 */ 1311 protected class TreeSelectionRedirector implements TreeSelectionListener, 1312 Serializable 1313 { 1314 /** The serial version UID. */ 1315 private static final long serialVersionUID = -3505069663646241664L; 1316 1317 /** 1318 * Creates a new instance of TreeSelectionRedirector 1319 */ TreeSelectionRedirector()1320 protected TreeSelectionRedirector() 1321 { 1322 // Nothing to do here. 1323 } 1324 1325 /** 1326 * Notifies when the tree selection changes. 1327 * 1328 * @param ev the TreeSelectionEvent that describes the change 1329 */ valueChanged(TreeSelectionEvent ev)1330 public void valueChanged(TreeSelectionEvent ev) 1331 { 1332 TreeSelectionEvent rewritten = 1333 (TreeSelectionEvent) ev.cloneWithSource(JTree.this); 1334 fireValueChanged(rewritten); 1335 } 1336 } 1337 1338 /** 1339 * A TreeModel that does not allow anything to be selected. 1340 */ 1341 protected static class EmptySelectionModel extends DefaultTreeSelectionModel 1342 { 1343 /** The serial version UID. */ 1344 private static final long serialVersionUID = -5815023306225701477L; 1345 1346 /** 1347 * The shared instance of this model. 1348 */ 1349 protected static final EmptySelectionModel sharedInstance = 1350 new EmptySelectionModel(); 1351 1352 /** 1353 * Creates a new instance of EmptySelectionModel. 1354 */ EmptySelectionModel()1355 protected EmptySelectionModel() 1356 { 1357 // Nothing to do here. 1358 } 1359 1360 /** 1361 * Returns the shared instance of EmptySelectionModel. 1362 * 1363 * @return the shared instance of EmptySelectionModel 1364 */ sharedInstance()1365 public static EmptySelectionModel sharedInstance() 1366 { 1367 return sharedInstance; 1368 } 1369 1370 /** 1371 * This catches attempts to set a selection and sets nothing instead. 1372 * 1373 * @param paths not used here 1374 */ setSelectionPaths(TreePath[] paths)1375 public void setSelectionPaths(TreePath[] paths) 1376 { 1377 // We don't allow selections in this class. 1378 } 1379 1380 /** 1381 * This catches attempts to add something to the selection. 1382 * 1383 * @param paths not used here 1384 */ addSelectionPaths(TreePath[] paths)1385 public void addSelectionPaths(TreePath[] paths) 1386 { 1387 // We don't allow selections in this class. 1388 } 1389 1390 /** 1391 * This catches attempts to remove something from the selection. 1392 * 1393 * @param paths not used here 1394 */ removeSelectionPaths(TreePath[] paths)1395 public void removeSelectionPaths(TreePath[] paths) 1396 { 1397 // We don't allow selections in this class. 1398 } 1399 } 1400 1401 private static final long serialVersionUID = 7559816092864483649L; 1402 1403 public static final String CELL_EDITOR_PROPERTY = "cellEditor"; 1404 1405 public static final String CELL_RENDERER_PROPERTY = "cellRenderer"; 1406 1407 public static final String EDITABLE_PROPERTY = "editable"; 1408 1409 public static final String INVOKES_STOP_CELL_EDITING_PROPERTY = 1410 "invokesStopCellEditing"; 1411 1412 public static final String LARGE_MODEL_PROPERTY = "largeModel"; 1413 1414 public static final String ROOT_VISIBLE_PROPERTY = "rootVisible"; 1415 1416 public static final String ROW_HEIGHT_PROPERTY = "rowHeight"; 1417 1418 public static final String SCROLLS_ON_EXPAND_PROPERTY = "scrollsOnExpand"; 1419 1420 public static final String SELECTION_MODEL_PROPERTY = "selectionModel"; 1421 1422 public static final String SHOWS_ROOT_HANDLES_PROPERTY = "showsRootHandles"; 1423 1424 public static final String TOGGLE_CLICK_COUNT_PROPERTY = "toggleClickCount"; 1425 1426 public static final String TREE_MODEL_PROPERTY = "model"; 1427 1428 public static final String VISIBLE_ROW_COUNT_PROPERTY = "visibleRowCount"; 1429 1430 /** @since 1.3 */ 1431 public static final String ANCHOR_SELECTION_PATH_PROPERTY = 1432 "anchorSelectionPath"; 1433 1434 /** @since 1.3 */ 1435 public static final String LEAD_SELECTION_PATH_PROPERTY = "leadSelectionPath"; 1436 1437 /** @since 1.3 */ 1438 public static final String EXPANDS_SELECTED_PATHS_PROPERTY = 1439 "expandsSelectedPaths"; 1440 1441 private static final Object EXPANDED = Boolean.TRUE; 1442 1443 private static final Object COLLAPSED = Boolean.FALSE; 1444 1445 private boolean dragEnabled; 1446 1447 private boolean expandsSelectedPaths; 1448 1449 private TreePath anchorSelectionPath; 1450 1451 /** 1452 * This contains the state of all nodes in the tree. Al/ entries map the 1453 * TreePath of a note to to its state. Valid states are EXPANDED and 1454 * COLLAPSED. Nodes not in this Hashtable are assumed state COLLAPSED. 1455 * 1456 * This is package private to avoid accessor methods. 1457 */ 1458 Hashtable nodeStates = new Hashtable(); 1459 1460 protected transient TreeCellEditor cellEditor; 1461 1462 protected transient TreeCellRenderer cellRenderer; 1463 1464 protected boolean editable; 1465 1466 protected boolean invokesStopCellEditing; 1467 1468 protected boolean largeModel; 1469 1470 protected boolean rootVisible; 1471 1472 protected int rowHeight; 1473 1474 protected boolean scrollsOnExpand; 1475 1476 protected transient TreeSelectionModel selectionModel; 1477 1478 protected boolean showsRootHandles; 1479 1480 protected int toggleClickCount; 1481 1482 protected transient TreeModel treeModel; 1483 1484 protected int visibleRowCount; 1485 1486 /** 1487 * Handles TreeModelEvents to update the expandedState. 1488 */ 1489 protected transient TreeModelListener treeModelListener; 1490 1491 /** 1492 * Redirects TreeSelectionEvents so that the source is this JTree. 1493 */ 1494 protected TreeSelectionRedirector selectionRedirector = 1495 new TreeSelectionRedirector(); 1496 1497 /** 1498 * Indicates if the rowHeight property has been set by a client 1499 * program or by the UI. 1500 * 1501 * @see #setUIProperty(String, Object) 1502 * @see LookAndFeel#installProperty(JComponent, String, Object) 1503 */ 1504 private boolean clientRowHeightSet = false; 1505 1506 /** 1507 * Indicates if the scrollsOnExpand property has been set by a client 1508 * program or by the UI. 1509 * 1510 * @see #setUIProperty(String, Object) 1511 * @see LookAndFeel#installProperty(JComponent, String, Object) 1512 */ 1513 private boolean clientScrollsOnExpandSet = false; 1514 1515 /** 1516 * Indicates if the showsRootHandles property has been set by a client 1517 * program or by the UI. 1518 * 1519 * @see #setUIProperty(String, Object) 1520 * @see LookAndFeel#installProperty(JComponent, String, Object) 1521 */ 1522 private boolean clientShowsRootHandlesSet = false; 1523 1524 /** 1525 * Creates a new <code>JTree</code> object. 1526 */ JTree()1527 public JTree() 1528 { 1529 this(getDefaultTreeModel()); 1530 } 1531 1532 /** 1533 * Creates a new <code>JTree</code> object. 1534 * 1535 * @param value the initial nodes in the tree 1536 */ JTree(Hashtable<?, ?> value)1537 public JTree(Hashtable<?, ?> value) 1538 { 1539 this(createTreeModel(value)); 1540 } 1541 1542 /** 1543 * Creates a new <code>JTree</code> object. 1544 * 1545 * @param value the initial nodes in the tree 1546 */ JTree(Object[] value)1547 public JTree(Object[] value) 1548 { 1549 this(createTreeModel(value)); 1550 } 1551 1552 /** 1553 * Creates a new <code>JTree</code> object. 1554 * 1555 * @param model the model to use 1556 */ JTree(TreeModel model)1557 public JTree(TreeModel model) 1558 { 1559 setRootVisible(true); 1560 setSelectionModel( new DefaultTreeSelectionModel() ); 1561 1562 // The root node appears expanded by default. 1563 nodeStates = new Hashtable(); 1564 1565 // The cell renderer gets set by the UI. 1566 cellRenderer = null; 1567 1568 // Install the UI before installing the model. This way we avoid double 1569 // initialization of lots of UI and model stuff inside the UI and related 1570 // classes. The necessary UI updates are performed via property change 1571 // events to the UI. 1572 updateUI(); 1573 setModel(model); 1574 } 1575 1576 /** 1577 * Creates a new <code>JTree</code> object. 1578 * 1579 * @param root the root node 1580 */ JTree(TreeNode root)1581 public JTree(TreeNode root) 1582 { 1583 this(root, false); 1584 } 1585 1586 /** 1587 * Creates a new <code>JTree</code> object. 1588 * 1589 * @param root the root node 1590 * @param asksAllowChildren if false, all nodes without children are leaf 1591 * nodes. If true, only nodes that do not allow children are leaf 1592 * nodes. 1593 */ JTree(TreeNode root, boolean asksAllowChildren)1594 public JTree(TreeNode root, boolean asksAllowChildren) 1595 { 1596 this(new DefaultTreeModel(root, asksAllowChildren)); 1597 } 1598 1599 /** 1600 * Creates a new <code>JTree</code> object. 1601 * 1602 * @param value the initial nodes in the tree 1603 */ JTree(Vector<?> value)1604 public JTree(Vector<?> value) 1605 { 1606 this(createTreeModel(value)); 1607 } 1608 getRowForPath(TreePath path)1609 public int getRowForPath(TreePath path) 1610 { 1611 TreeUI ui = getUI(); 1612 1613 if (ui != null) 1614 return ui.getRowForPath(this, path); 1615 1616 return -1; 1617 } 1618 getPathForRow(int row)1619 public TreePath getPathForRow(int row) 1620 { 1621 TreeUI ui = getUI(); 1622 return ui != null ? ui.getPathForRow(this, row) : null; 1623 } 1624 1625 /** 1626 * Get the pathes that are displayes between the two given rows. 1627 * 1628 * @param index0 the starting row, inclusive 1629 * @param index1 the ending row, inclusive 1630 * 1631 * @return the array of the tree pathes 1632 */ getPathBetweenRows(int index0, int index1)1633 protected TreePath[] getPathBetweenRows(int index0, int index1) 1634 { 1635 TreeUI ui = getUI(); 1636 1637 if (ui == null) 1638 return null; 1639 1640 int minIndex = Math.min(index0, index1); 1641 int maxIndex = Math.max(index0, index1); 1642 TreePath[] paths = new TreePath[maxIndex - minIndex + 1]; 1643 1644 for (int i = minIndex; i <= maxIndex; ++i) 1645 paths[i - minIndex] = ui.getPathForRow(this, i); 1646 1647 return paths; 1648 } 1649 1650 /** 1651 * Creates a new <code>TreeModel</code> object. 1652 * 1653 * @param value the values stored in the model 1654 */ createTreeModel(Object value)1655 protected static TreeModel createTreeModel(Object value) 1656 { 1657 return new DefaultTreeModel(new DynamicUtilTreeNode(value, value)); 1658 } 1659 1660 /** 1661 * Return the UI associated with this <code>JTree</code> object. 1662 * 1663 * @return the associated <code>TreeUI</code> object 1664 */ getUI()1665 public TreeUI getUI() 1666 { 1667 return (TreeUI) ui; 1668 } 1669 1670 /** 1671 * Sets the UI associated with this <code>JTree</code> object. 1672 * 1673 * @param ui the <code>TreeUI</code> to associate 1674 */ setUI(TreeUI ui)1675 public void setUI(TreeUI ui) 1676 { 1677 super.setUI(ui); 1678 } 1679 1680 /** 1681 * This method resets the UI used to the Look and Feel defaults.. 1682 */ updateUI()1683 public void updateUI() 1684 { 1685 setUI((TreeUI) UIManager.getUI(this)); 1686 } 1687 1688 /** 1689 * This method returns the String ID of the UI class of Separator. 1690 * 1691 * @return The UI class' String ID. 1692 */ getUIClassID()1693 public String getUIClassID() 1694 { 1695 return "TreeUI"; 1696 } 1697 1698 /** 1699 * Gets the AccessibleContext associated with this 1700 * <code>JTree</code>. 1701 * 1702 * @return the associated context 1703 */ getAccessibleContext()1704 public AccessibleContext getAccessibleContext() 1705 { 1706 return new AccessibleJTree(); 1707 } 1708 1709 /** 1710 * Returns the preferred viewport size. 1711 * 1712 * @return the preferred size 1713 */ getPreferredScrollableViewportSize()1714 public Dimension getPreferredScrollableViewportSize() 1715 { 1716 return getPreferredSize(); 1717 } 1718 1719 /** 1720 * Return the preferred scrolling amount (in pixels) for the given scrolling 1721 * direction and orientation. This method handles a partially exposed row by 1722 * returning the distance required to completely expose the item. 1723 * 1724 * @param visibleRect the currently visible part of the component. 1725 * @param orientation the scrolling orientation 1726 * @param direction the scrolling direction (negative - up, positive -down). 1727 * The values greater than one means that more mouse wheel or similar 1728 * events were generated, and hence it is better to scroll the longer 1729 * distance. 1730 * @author Audrius Meskauskas (audriusa@bioinformatics.org) 1731 */ getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction)1732 public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, 1733 int direction) 1734 { 1735 int delta = 0; 1736 1737 // Round so that the top would start from the row boundary 1738 if (orientation == SwingConstants.VERTICAL) 1739 { 1740 int row = getClosestRowForLocation(0, visibleRect.y); 1741 if (row != -1) 1742 { 1743 Rectangle b = getRowBounds(row); 1744 if (b.y != visibleRect.y) 1745 { 1746 if (direction < 0) 1747 delta = Math.max(0, visibleRect.y - b.y); 1748 else 1749 delta = b.y + b.height - visibleRect.y; 1750 } 1751 else 1752 { 1753 if (direction < 0) 1754 { 1755 if (row != 0) 1756 { 1757 b = getRowBounds(row - 1); 1758 delta = b.height; 1759 } 1760 } 1761 else 1762 delta = b.height; 1763 } 1764 } 1765 } 1766 else 1767 // The RI always returns 4 for HORIZONTAL scrolling. 1768 delta = 4; 1769 return delta; 1770 } 1771 getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction)1772 public int getScrollableBlockIncrement(Rectangle visibleRect, 1773 int orientation, int direction) 1774 { 1775 int block; 1776 if (orientation == SwingConstants.VERTICAL) 1777 block = visibleRect.height; 1778 else 1779 block = visibleRect.width; 1780 return block; 1781 } 1782 getScrollableTracksViewportHeight()1783 public boolean getScrollableTracksViewportHeight() 1784 { 1785 if (getParent() instanceof JViewport) 1786 return ((JViewport) getParent()).getHeight() > getPreferredSize().height; 1787 return false; 1788 } 1789 getScrollableTracksViewportWidth()1790 public boolean getScrollableTracksViewportWidth() 1791 { 1792 if (getParent() instanceof JViewport) 1793 return ((JViewport) getParent()).getWidth() > getPreferredSize().width; 1794 return false; 1795 } 1796 1797 /** 1798 * Adds a <code>TreeExpansionListener</code> object to the tree. 1799 * 1800 * @param listener the listener to add 1801 */ addTreeExpansionListener(TreeExpansionListener listener)1802 public void addTreeExpansionListener(TreeExpansionListener listener) 1803 { 1804 listenerList.add(TreeExpansionListener.class, listener); 1805 } 1806 1807 /** 1808 * Removes a <code>TreeExpansionListener</code> object from the tree. 1809 * 1810 * @param listener the listener to remove 1811 */ removeTreeExpansionListener(TreeExpansionListener listener)1812 public void removeTreeExpansionListener(TreeExpansionListener listener) 1813 { 1814 listenerList.remove(TreeExpansionListener.class, listener); 1815 } 1816 1817 /** 1818 * Returns all added <code>TreeExpansionListener</code> objects. 1819 * 1820 * @return an array of listeners 1821 */ getTreeExpansionListeners()1822 public TreeExpansionListener[] getTreeExpansionListeners() 1823 { 1824 return (TreeExpansionListener[]) getListeners(TreeExpansionListener.class); 1825 } 1826 1827 /** 1828 * Notifies all listeners that the tree was collapsed. 1829 * 1830 * @param path the path to the node that was collapsed 1831 */ fireTreeCollapsed(TreePath path)1832 public void fireTreeCollapsed(TreePath path) 1833 { 1834 TreeExpansionEvent event = new TreeExpansionEvent(this, path); 1835 TreeExpansionListener[] listeners = getTreeExpansionListeners(); 1836 1837 for (int index = 0; index < listeners.length; ++index) 1838 listeners[index].treeCollapsed(event); 1839 } 1840 1841 /** 1842 * Notifies all listeners that the tree was expanded. 1843 * 1844 * @param path the path to the node that was expanded 1845 */ fireTreeExpanded(TreePath path)1846 public void fireTreeExpanded(TreePath path) 1847 { 1848 TreeExpansionEvent event = new TreeExpansionEvent(this, path); 1849 TreeExpansionListener[] listeners = getTreeExpansionListeners(); 1850 1851 for (int index = 0; index < listeners.length; ++index) 1852 listeners[index].treeExpanded(event); 1853 } 1854 1855 /** 1856 * Adds a <code>TreeSelctionListener</code> object to the tree. 1857 * 1858 * @param listener the listener to add 1859 */ addTreeSelectionListener(TreeSelectionListener listener)1860 public void addTreeSelectionListener(TreeSelectionListener listener) 1861 { 1862 listenerList.add(TreeSelectionListener.class, listener); 1863 } 1864 1865 /** 1866 * Removes a <code>TreeSelectionListener</code> object from the tree. 1867 * 1868 * @param listener the listener to remove 1869 */ removeTreeSelectionListener(TreeSelectionListener listener)1870 public void removeTreeSelectionListener(TreeSelectionListener listener) 1871 { 1872 listenerList.remove(TreeSelectionListener.class, listener); 1873 } 1874 1875 /** 1876 * Returns all added <code>TreeSelectionListener</code> objects. 1877 * 1878 * @return an array of listeners 1879 */ getTreeSelectionListeners()1880 public TreeSelectionListener[] getTreeSelectionListeners() 1881 { 1882 return (TreeSelectionListener[]) 1883 getListeners(TreeSelectionListener.class); 1884 } 1885 1886 /** 1887 * Notifies all listeners when the selection of the tree changed. 1888 * 1889 * @param event the event to send 1890 */ fireValueChanged(TreeSelectionEvent event)1891 protected void fireValueChanged(TreeSelectionEvent event) 1892 { 1893 TreeSelectionListener[] listeners = getTreeSelectionListeners(); 1894 1895 for (int index = 0; index < listeners.length; ++index) 1896 listeners[index].valueChanged(event); 1897 } 1898 1899 /** 1900 * Adds a <code>TreeWillExpandListener</code> object to the tree. 1901 * 1902 * @param listener the listener to add 1903 */ addTreeWillExpandListener(TreeWillExpandListener listener)1904 public void addTreeWillExpandListener(TreeWillExpandListener listener) 1905 { 1906 listenerList.add(TreeWillExpandListener.class, listener); 1907 } 1908 1909 /** 1910 * Removes a <code>TreeWillExpandListener</code> object from the tree. 1911 * 1912 * @param listener the listener to remove 1913 */ removeTreeWillExpandListener(TreeWillExpandListener listener)1914 public void removeTreeWillExpandListener(TreeWillExpandListener listener) 1915 { 1916 listenerList.remove(TreeWillExpandListener.class, listener); 1917 } 1918 1919 /** 1920 * Returns all added <code>TreeWillExpandListener</code> objects. 1921 * 1922 * @return an array of listeners 1923 */ getTreeWillExpandListeners()1924 public TreeWillExpandListener[] getTreeWillExpandListeners() 1925 { 1926 return (TreeWillExpandListener[]) 1927 getListeners(TreeWillExpandListener.class); 1928 } 1929 1930 /** 1931 * Notifies all listeners that the tree will collapse. 1932 * 1933 * @param path the path to the node that will collapse 1934 */ fireTreeWillCollapse(TreePath path)1935 public void fireTreeWillCollapse(TreePath path) throws ExpandVetoException 1936 { 1937 TreeExpansionEvent event = new TreeExpansionEvent(this, path); 1938 TreeWillExpandListener[] listeners = getTreeWillExpandListeners(); 1939 1940 for (int index = 0; index < listeners.length; ++index) 1941 listeners[index].treeWillCollapse(event); 1942 } 1943 1944 /** 1945 * Notifies all listeners that the tree will expand. 1946 * 1947 * @param path the path to the node that will expand 1948 */ fireTreeWillExpand(TreePath path)1949 public void fireTreeWillExpand(TreePath path) throws ExpandVetoException 1950 { 1951 TreeExpansionEvent event = new TreeExpansionEvent(this, path); 1952 TreeWillExpandListener[] listeners = getTreeWillExpandListeners(); 1953 1954 for (int index = 0; index < listeners.length; ++index) 1955 listeners[index].treeWillExpand(event); 1956 } 1957 1958 /** 1959 * Returns the model of this <code>JTree</code> object. 1960 * 1961 * @return the associated <code>TreeModel</code> 1962 */ getModel()1963 public TreeModel getModel() 1964 { 1965 return treeModel; 1966 } 1967 1968 /** 1969 * Sets the model to use in <code>JTree</code>. 1970 * 1971 * @param model the <code>TreeModel</code> to use 1972 */ setModel(TreeModel model)1973 public void setModel(TreeModel model) 1974 { 1975 if (treeModel == model) 1976 return; 1977 1978 // Remove listeners from old model. 1979 if (treeModel != null && treeModelListener != null) 1980 treeModel.removeTreeModelListener(treeModelListener); 1981 1982 // add treeModelListener to the new model 1983 if (treeModelListener == null) 1984 treeModelListener = createTreeModelListener(); 1985 if (model != null) // as setModel(null) is allowed 1986 model.addTreeModelListener(treeModelListener); 1987 1988 TreeModel oldValue = treeModel; 1989 treeModel = model; 1990 clearToggledPaths(); 1991 1992 if (treeModel != null) 1993 { 1994 if (treeModelListener == null) 1995 treeModelListener = createTreeModelListener(); 1996 if (treeModelListener != null) 1997 treeModel.addTreeModelListener(treeModelListener); 1998 Object root = treeModel.getRoot(); 1999 if (root != null && !treeModel.isLeaf(root)) 2000 { 2001 nodeStates.put(new TreePath(root), Boolean.TRUE); 2002 } 2003 } 2004 2005 firePropertyChange(TREE_MODEL_PROPERTY, oldValue, model); 2006 } 2007 2008 /** 2009 * Checks if this <code>JTree</code> object is editable. 2010 * 2011 * @return <code>true</code> if this tree object is editable, 2012 * <code>false</code> otherwise 2013 */ isEditable()2014 public boolean isEditable() 2015 { 2016 return editable; 2017 } 2018 2019 /** 2020 * Sets the <code>editable</code> property. 2021 * 2022 * @param flag <code>true</code> to make this tree object editable, 2023 * <code>false</code> otherwise 2024 */ setEditable(boolean flag)2025 public void setEditable(boolean flag) 2026 { 2027 if (editable == flag) 2028 return; 2029 2030 boolean oldValue = editable; 2031 editable = flag; 2032 firePropertyChange(EDITABLE_PROPERTY, oldValue, editable); 2033 } 2034 2035 /** 2036 * Checks if the root element is visible. 2037 * 2038 * @return <code>true</code> if the root element is visible, 2039 * <code>false</code> otherwise 2040 */ isRootVisible()2041 public boolean isRootVisible() 2042 { 2043 return rootVisible; 2044 } 2045 setRootVisible(boolean flag)2046 public void setRootVisible(boolean flag) 2047 { 2048 if (rootVisible == flag) 2049 return; 2050 2051 // If the root is currently selected, unselect it 2052 if (rootVisible && !flag) 2053 { 2054 TreeSelectionModel model = getSelectionModel(); 2055 // The root is always shown in the first row 2056 TreePath rootPath = getPathForRow(0); 2057 model.removeSelectionPath(rootPath); 2058 } 2059 2060 boolean oldValue = rootVisible; 2061 rootVisible = flag; 2062 firePropertyChange(ROOT_VISIBLE_PROPERTY, oldValue, flag); 2063 2064 } 2065 getShowsRootHandles()2066 public boolean getShowsRootHandles() 2067 { 2068 return showsRootHandles; 2069 } 2070 setShowsRootHandles(boolean flag)2071 public void setShowsRootHandles(boolean flag) 2072 { 2073 clientShowsRootHandlesSet = true; 2074 2075 if (showsRootHandles == flag) 2076 return; 2077 2078 boolean oldValue = showsRootHandles; 2079 showsRootHandles = flag; 2080 firePropertyChange(SHOWS_ROOT_HANDLES_PROPERTY, oldValue, flag); 2081 } 2082 getCellEditor()2083 public TreeCellEditor getCellEditor() 2084 { 2085 return cellEditor; 2086 } 2087 setCellEditor(TreeCellEditor editor)2088 public void setCellEditor(TreeCellEditor editor) 2089 { 2090 if (cellEditor == editor) 2091 return; 2092 2093 TreeCellEditor oldValue = cellEditor; 2094 cellEditor = editor; 2095 firePropertyChange(CELL_EDITOR_PROPERTY, oldValue, editor); 2096 } 2097 getCellRenderer()2098 public TreeCellRenderer getCellRenderer() 2099 { 2100 return cellRenderer; 2101 } 2102 setCellRenderer(TreeCellRenderer newRenderer)2103 public void setCellRenderer(TreeCellRenderer newRenderer) 2104 { 2105 if (cellRenderer == newRenderer) 2106 return; 2107 2108 TreeCellRenderer oldValue = cellRenderer; 2109 cellRenderer = newRenderer; 2110 firePropertyChange(CELL_RENDERER_PROPERTY, oldValue, newRenderer); 2111 } 2112 getSelectionModel()2113 public TreeSelectionModel getSelectionModel() 2114 { 2115 return selectionModel; 2116 } 2117 setSelectionModel(TreeSelectionModel model)2118 public void setSelectionModel(TreeSelectionModel model) 2119 { 2120 if (selectionModel == model) 2121 return; 2122 2123 if( model == null ) 2124 model = EmptySelectionModel.sharedInstance(); 2125 2126 if (selectionModel != null) 2127 selectionModel.removeTreeSelectionListener(selectionRedirector); 2128 2129 TreeSelectionModel oldValue = selectionModel; 2130 selectionModel = model; 2131 2132 selectionModel.addTreeSelectionListener(selectionRedirector); 2133 2134 firePropertyChange(SELECTION_MODEL_PROPERTY, oldValue, model); 2135 revalidate(); 2136 repaint(); 2137 } 2138 getVisibleRowCount()2139 public int getVisibleRowCount() 2140 { 2141 return visibleRowCount; 2142 } 2143 setVisibleRowCount(int rows)2144 public void setVisibleRowCount(int rows) 2145 { 2146 if (visibleRowCount == rows) 2147 return; 2148 2149 int oldValue = visibleRowCount; 2150 visibleRowCount = rows; 2151 firePropertyChange(VISIBLE_ROW_COUNT_PROPERTY, oldValue, rows); 2152 } 2153 isLargeModel()2154 public boolean isLargeModel() 2155 { 2156 return largeModel; 2157 } 2158 setLargeModel(boolean large)2159 public void setLargeModel(boolean large) 2160 { 2161 if (largeModel == large) 2162 return; 2163 2164 boolean oldValue = largeModel; 2165 largeModel = large; 2166 firePropertyChange(LARGE_MODEL_PROPERTY, oldValue, large); 2167 } 2168 getRowHeight()2169 public int getRowHeight() 2170 { 2171 return rowHeight; 2172 } 2173 setRowHeight(int height)2174 public void setRowHeight(int height) 2175 { 2176 clientRowHeightSet = true; 2177 2178 if (rowHeight == height) 2179 return; 2180 2181 int oldValue = rowHeight; 2182 rowHeight = height; 2183 firePropertyChange(ROW_HEIGHT_PROPERTY, oldValue, height); 2184 } 2185 isFixedRowHeight()2186 public boolean isFixedRowHeight() 2187 { 2188 return rowHeight > 0; 2189 } 2190 getInvokesStopCellEditing()2191 public boolean getInvokesStopCellEditing() 2192 { 2193 return invokesStopCellEditing; 2194 } 2195 setInvokesStopCellEditing(boolean invoke)2196 public void setInvokesStopCellEditing(boolean invoke) 2197 { 2198 if (invokesStopCellEditing == invoke) 2199 return; 2200 2201 boolean oldValue = invokesStopCellEditing; 2202 invokesStopCellEditing = invoke; 2203 firePropertyChange(INVOKES_STOP_CELL_EDITING_PROPERTY, 2204 oldValue, invoke); 2205 } 2206 2207 /** 2208 * @since 1.3 2209 */ getToggleClickCount()2210 public int getToggleClickCount() 2211 { 2212 return toggleClickCount; 2213 } 2214 2215 /** 2216 * @since 1.3 2217 */ setToggleClickCount(int count)2218 public void setToggleClickCount(int count) 2219 { 2220 if (toggleClickCount == count) 2221 return; 2222 2223 int oldValue = toggleClickCount; 2224 toggleClickCount = count; 2225 firePropertyChange(TOGGLE_CLICK_COUNT_PROPERTY, oldValue, count); 2226 } 2227 scrollPathToVisible(TreePath path)2228 public void scrollPathToVisible(TreePath path) 2229 { 2230 if (path == null) 2231 return; 2232 Rectangle rect = getPathBounds(path); 2233 scrollRectToVisible(rect); 2234 } 2235 scrollRowToVisible(int row)2236 public void scrollRowToVisible(int row) 2237 { 2238 scrollPathToVisible(getPathForRow(row)); 2239 } 2240 getScrollsOnExpand()2241 public boolean getScrollsOnExpand() 2242 { 2243 return scrollsOnExpand; 2244 } 2245 setScrollsOnExpand(boolean scroll)2246 public void setScrollsOnExpand(boolean scroll) 2247 { 2248 clientScrollsOnExpandSet = true; 2249 if (scrollsOnExpand == scroll) 2250 return; 2251 2252 boolean oldValue = scrollsOnExpand; 2253 scrollsOnExpand = scroll; 2254 firePropertyChange(SCROLLS_ON_EXPAND_PROPERTY, oldValue, scroll); 2255 } 2256 setSelectionPath(TreePath path)2257 public void setSelectionPath(TreePath path) 2258 { 2259 clearSelectionPathStates(); 2260 selectionModel.setSelectionPath(path); 2261 } 2262 setSelectionPaths(TreePath[] paths)2263 public void setSelectionPaths(TreePath[] paths) 2264 { 2265 clearSelectionPathStates(); 2266 selectionModel.setSelectionPaths(paths); 2267 } 2268 2269 /** 2270 * This method, and all calls to it, should be removed once the 2271 * DefaultTreeModel fires events properly. Maintenance of the nodeStates 2272 * table should really be done in the TreeModelHandler. 2273 */ clearSelectionPathStates()2274 private void clearSelectionPathStates() 2275 { 2276 TreePath[] oldPaths = selectionModel.getSelectionPaths(); 2277 if (oldPaths != null) 2278 for (int i = 0; i < oldPaths.length; i++) 2279 nodeStates.remove(oldPaths[i]); 2280 } 2281 setSelectionRow(int row)2282 public void setSelectionRow(int row) 2283 { 2284 TreePath path = getPathForRow(row); 2285 2286 if (path != null) 2287 setSelectionPath(path); 2288 } 2289 setSelectionRows(int[] rows)2290 public void setSelectionRows(int[] rows) 2291 { 2292 // Make sure we have an UI so getPathForRow() does not return null. 2293 if (rows == null || getUI() == null) 2294 return; 2295 2296 TreePath[] paths = new TreePath[rows.length]; 2297 2298 for (int i = rows.length - 1; i >= 0; --i) 2299 paths[i] = getPathForRow(rows[i]); 2300 2301 setSelectionPaths(paths); 2302 } 2303 setSelectionInterval(int index0, int index1)2304 public void setSelectionInterval(int index0, int index1) 2305 { 2306 TreePath[] paths = getPathBetweenRows(index0, index1); 2307 2308 if (paths != null) 2309 setSelectionPaths(paths); 2310 } 2311 addSelectionPath(TreePath path)2312 public void addSelectionPath(TreePath path) 2313 { 2314 selectionModel.addSelectionPath(path); 2315 } 2316 addSelectionPaths(TreePath[] paths)2317 public void addSelectionPaths(TreePath[] paths) 2318 { 2319 selectionModel.addSelectionPaths(paths); 2320 } 2321 addSelectionRow(int row)2322 public void addSelectionRow(int row) 2323 { 2324 TreePath path = getPathForRow(row); 2325 2326 if (path != null) 2327 selectionModel.addSelectionPath(path); 2328 } 2329 addSelectionRows(int[] rows)2330 public void addSelectionRows(int[] rows) 2331 { 2332 // Make sure we have an UI so getPathForRow() does not return null. 2333 if (rows == null || getUI() == null) 2334 return; 2335 2336 TreePath[] paths = new TreePath[rows.length]; 2337 2338 for (int i = rows.length - 1; i >= 0; --i) 2339 paths[i] = getPathForRow(rows[i]); 2340 2341 addSelectionPaths(paths); 2342 } 2343 2344 /** 2345 * Select all rows between the two given indexes, inclusive. The method 2346 * will not select the inner leaves and braches of the currently collapsed 2347 * nodes in this interval. 2348 * 2349 * @param index0 the starting row, inclusive 2350 * @param index1 the ending row, inclusive 2351 */ addSelectionInterval(int index0, int index1)2352 public void addSelectionInterval(int index0, int index1) 2353 { 2354 TreePath[] paths = getPathBetweenRows(index0, index1); 2355 2356 if (paths != null) 2357 addSelectionPaths(paths); 2358 } 2359 removeSelectionPath(TreePath path)2360 public void removeSelectionPath(TreePath path) 2361 { 2362 clearSelectionPathStates(); 2363 selectionModel.removeSelectionPath(path); 2364 } 2365 removeSelectionPaths(TreePath[] paths)2366 public void removeSelectionPaths(TreePath[] paths) 2367 { 2368 clearSelectionPathStates(); 2369 selectionModel.removeSelectionPaths(paths); 2370 } 2371 removeSelectionRow(int row)2372 public void removeSelectionRow(int row) 2373 { 2374 TreePath path = getPathForRow(row); 2375 2376 if (path != null) 2377 removeSelectionPath(path); 2378 } 2379 removeSelectionRows(int[] rows)2380 public void removeSelectionRows(int[] rows) 2381 { 2382 if (rows == null || getUI() == null) 2383 return; 2384 2385 TreePath[] paths = new TreePath[rows.length]; 2386 2387 for (int i = rows.length - 1; i >= 0; --i) 2388 paths[i] = getPathForRow(rows[i]); 2389 2390 removeSelectionPaths(paths); 2391 } 2392 removeSelectionInterval(int index0, int index1)2393 public void removeSelectionInterval(int index0, int index1) 2394 { 2395 TreePath[] paths = getPathBetweenRows(index0, index1); 2396 2397 if (paths != null) 2398 removeSelectionPaths(paths); 2399 } 2400 clearSelection()2401 public void clearSelection() 2402 { 2403 selectionModel.clearSelection(); 2404 setLeadSelectionPath(null); 2405 } 2406 getLeadSelectionPath()2407 public TreePath getLeadSelectionPath() 2408 { 2409 if (selectionModel == null) 2410 return null; 2411 else 2412 return selectionModel.getLeadSelectionPath(); 2413 } 2414 2415 /** 2416 * @since 1.3 2417 */ setLeadSelectionPath(TreePath path)2418 public void setLeadSelectionPath(TreePath path) 2419 { 2420 if (selectionModel != null) 2421 { 2422 TreePath oldValue = selectionModel.getLeadSelectionPath(); 2423 if (path == oldValue || path != null && path.equals(oldValue)) 2424 return; 2425 2426 // Repaint the previous and current rows with the lead selection path. 2427 if (path != null) 2428 { 2429 repaint(getPathBounds(path)); 2430 selectionModel.addSelectionPath(path); 2431 } 2432 2433 if (oldValue != null) 2434 repaint(getPathBounds(oldValue)); 2435 2436 firePropertyChange(LEAD_SELECTION_PATH_PROPERTY, oldValue, path); 2437 } 2438 } 2439 2440 /** 2441 * @since 1.3 2442 */ getAnchorSelectionPath()2443 public TreePath getAnchorSelectionPath() 2444 { 2445 return anchorSelectionPath; 2446 } 2447 2448 /** 2449 * @since 1.3 2450 */ setAnchorSelectionPath(TreePath path)2451 public void setAnchorSelectionPath(TreePath path) 2452 { 2453 if (anchorSelectionPath == path) 2454 return; 2455 2456 TreePath oldValue = anchorSelectionPath; 2457 anchorSelectionPath = path; 2458 firePropertyChange(ANCHOR_SELECTION_PATH_PROPERTY, oldValue, path); 2459 } 2460 getLeadSelectionRow()2461 public int getLeadSelectionRow() 2462 { 2463 return selectionModel.getLeadSelectionRow(); 2464 } 2465 getMaxSelectionRow()2466 public int getMaxSelectionRow() 2467 { 2468 return selectionModel.getMaxSelectionRow(); 2469 } 2470 getMinSelectionRow()2471 public int getMinSelectionRow() 2472 { 2473 return selectionModel.getMinSelectionRow(); 2474 } 2475 getSelectionCount()2476 public int getSelectionCount() 2477 { 2478 return selectionModel.getSelectionCount(); 2479 } 2480 getSelectionPath()2481 public TreePath getSelectionPath() 2482 { 2483 return selectionModel.getSelectionPath(); 2484 } 2485 getSelectionPaths()2486 public TreePath[] getSelectionPaths() 2487 { 2488 return selectionModel.getSelectionPaths(); 2489 } 2490 getSelectionRows()2491 public int[] getSelectionRows() 2492 { 2493 return selectionModel.getSelectionRows(); 2494 } 2495 isPathSelected(TreePath path)2496 public boolean isPathSelected(TreePath path) 2497 { 2498 return selectionModel.isPathSelected(path); 2499 } 2500 2501 /** 2502 * Returns <code>true</code> when the specified row is selected, 2503 * <code>false</code> otherwise. This call is delegated to the 2504 * {@link TreeSelectionModel#isRowSelected(int)} method. 2505 * 2506 * @param row the row to check 2507 * 2508 * @return <code>true</code> when the specified row is selected, 2509 * <code>false</code> otherwise 2510 */ isRowSelected(int row)2511 public boolean isRowSelected(int row) 2512 { 2513 return selectionModel.isRowSelected(row); 2514 } 2515 isSelectionEmpty()2516 public boolean isSelectionEmpty() 2517 { 2518 return selectionModel.isSelectionEmpty(); 2519 } 2520 2521 /** 2522 * Return the value of the <code>dragEnabled</code> property. 2523 * 2524 * @return the value 2525 * 2526 * @since 1.4 2527 */ getDragEnabled()2528 public boolean getDragEnabled() 2529 { 2530 return dragEnabled; 2531 } 2532 2533 /** 2534 * Set the <code>dragEnabled</code> property. 2535 * 2536 * @param enabled new value 2537 * 2538 * @since 1.4 2539 */ setDragEnabled(boolean enabled)2540 public void setDragEnabled(boolean enabled) 2541 { 2542 dragEnabled = enabled; 2543 } 2544 getRowCount()2545 public int getRowCount() 2546 { 2547 TreeUI ui = getUI(); 2548 2549 if (ui != null) 2550 return ui.getRowCount(this); 2551 2552 return 0; 2553 } 2554 collapsePath(TreePath path)2555 public void collapsePath(TreePath path) 2556 { 2557 try 2558 { 2559 fireTreeWillCollapse(path); 2560 } 2561 catch (ExpandVetoException ev) 2562 { 2563 // We do nothing if attempt has been vetoed. 2564 } 2565 setExpandedState(path, false); 2566 fireTreeCollapsed(path); 2567 } 2568 collapseRow(int row)2569 public void collapseRow(int row) 2570 { 2571 if (row < 0 || row >= getRowCount()) 2572 return; 2573 2574 TreePath path = getPathForRow(row); 2575 2576 if (path != null) 2577 collapsePath(path); 2578 } 2579 expandPath(TreePath path)2580 public void expandPath(TreePath path) 2581 { 2582 // Don't expand if path is null 2583 // or is already expanded. 2584 if (path == null || isExpanded(path)) 2585 return; 2586 2587 try 2588 { 2589 fireTreeWillExpand(path); 2590 } 2591 catch (ExpandVetoException ev) 2592 { 2593 // We do nothing if attempt has been vetoed. 2594 } 2595 2596 setExpandedState(path, true); 2597 fireTreeExpanded(path); 2598 } 2599 expandRow(int row)2600 public void expandRow(int row) 2601 { 2602 if (row < 0 || row >= getRowCount()) 2603 return; 2604 2605 TreePath path = getPathForRow(row); 2606 2607 if (path != null) 2608 expandPath(path); 2609 } 2610 isCollapsed(TreePath path)2611 public boolean isCollapsed(TreePath path) 2612 { 2613 return !isExpanded(path); 2614 } 2615 isCollapsed(int row)2616 public boolean isCollapsed(int row) 2617 { 2618 if (row < 0 || row >= getRowCount()) 2619 return false; 2620 2621 TreePath path = getPathForRow(row); 2622 2623 if (path != null) 2624 return isCollapsed(path); 2625 2626 return false; 2627 } 2628 isExpanded(TreePath path)2629 public boolean isExpanded(TreePath path) 2630 { 2631 if (path == null) 2632 return false; 2633 2634 Object state = nodeStates.get(path); 2635 2636 if ((state == null) || (state != EXPANDED)) 2637 return false; 2638 2639 TreePath parent = path.getParentPath(); 2640 2641 if (parent != null) 2642 return isExpanded(parent); 2643 2644 return true; 2645 } 2646 isExpanded(int row)2647 public boolean isExpanded(int row) 2648 { 2649 if (row < 0 || row >= getRowCount()) 2650 return false; 2651 2652 TreePath path = getPathForRow(row); 2653 2654 if (path != null) 2655 return isExpanded(path); 2656 2657 return false; 2658 } 2659 2660 /** 2661 * @since 1.3 2662 */ getExpandsSelectedPaths()2663 public boolean getExpandsSelectedPaths() 2664 { 2665 return expandsSelectedPaths; 2666 } 2667 2668 /** 2669 * @since 1.3 2670 */ setExpandsSelectedPaths(boolean flag)2671 public void setExpandsSelectedPaths(boolean flag) 2672 { 2673 if (expandsSelectedPaths == flag) 2674 return; 2675 2676 boolean oldValue = expandsSelectedPaths; 2677 expandsSelectedPaths = flag; 2678 firePropertyChange(EXPANDS_SELECTED_PATHS_PROPERTY, oldValue, flag); 2679 } 2680 getPathBounds(TreePath path)2681 public Rectangle getPathBounds(TreePath path) 2682 { 2683 TreeUI ui = getUI(); 2684 2685 if (ui == null) 2686 return null; 2687 2688 return ui.getPathBounds(this, path); 2689 } 2690 getRowBounds(int row)2691 public Rectangle getRowBounds(int row) 2692 { 2693 TreePath path = getPathForRow(row); 2694 2695 if (path != null) 2696 return getPathBounds(path); 2697 2698 return null; 2699 } 2700 isEditing()2701 public boolean isEditing() 2702 { 2703 TreeUI ui = getUI(); 2704 2705 if (ui != null) 2706 return ui.isEditing(this); 2707 2708 return false; 2709 } 2710 stopEditing()2711 public boolean stopEditing() 2712 { 2713 TreeUI ui = getUI(); 2714 2715 if (isEditing()) 2716 if (ui != null) 2717 return ui.stopEditing(this); 2718 2719 return false; 2720 } 2721 cancelEditing()2722 public void cancelEditing() 2723 { 2724 TreeUI ui = getUI(); 2725 2726 if (isEditing()) 2727 if (ui != null) 2728 ui.cancelEditing(this); 2729 } 2730 startEditingAtPath(TreePath path)2731 public void startEditingAtPath(TreePath path) 2732 { 2733 TreeUI ui = getUI(); 2734 2735 if (ui != null) 2736 ui.startEditingAtPath(this, path); 2737 } 2738 getEditingPath()2739 public TreePath getEditingPath() 2740 { 2741 TreeUI ui = getUI(); 2742 2743 if (ui != null) 2744 return ui.getEditingPath(this); 2745 2746 return null; 2747 } 2748 getPathForLocation(int x, int y)2749 public TreePath getPathForLocation(int x, int y) 2750 { 2751 TreePath path = getClosestPathForLocation(x, y); 2752 2753 if (path != null) 2754 { 2755 Rectangle rect = getPathBounds(path); 2756 2757 if ((rect != null) && rect.contains(x, y)) 2758 return path; 2759 } 2760 2761 return null; 2762 } 2763 getRowForLocation(int x, int y)2764 public int getRowForLocation(int x, int y) 2765 { 2766 TreePath path = getPathForLocation(x, y); 2767 2768 if (path != null) 2769 return getRowForPath(path); 2770 2771 return -1; 2772 } 2773 getClosestPathForLocation(int x, int y)2774 public TreePath getClosestPathForLocation(int x, int y) 2775 { 2776 TreeUI ui = getUI(); 2777 2778 if (ui != null) 2779 return ui.getClosestPathForLocation(this, x, y); 2780 2781 return null; 2782 } 2783 getClosestRowForLocation(int x, int y)2784 public int getClosestRowForLocation(int x, int y) 2785 { 2786 TreePath path = getClosestPathForLocation(x, y); 2787 2788 if (path != null) 2789 return getRowForPath(path); 2790 2791 return -1; 2792 } 2793 getLastSelectedPathComponent()2794 public Object getLastSelectedPathComponent() 2795 { 2796 TreePath path = getSelectionPath(); 2797 2798 if (path != null) 2799 return path.getLastPathComponent(); 2800 2801 return null; 2802 } 2803 doExpandParents(TreePath path, boolean state)2804 private void doExpandParents(TreePath path, boolean state) 2805 { 2806 TreePath parent = path.getParentPath(); 2807 2808 if (!isExpanded(parent) && parent != null) 2809 doExpandParents(parent, false); 2810 2811 nodeStates.put(path, state ? EXPANDED : COLLAPSED); 2812 } 2813 setExpandedState(TreePath path, boolean state)2814 protected void setExpandedState(TreePath path, boolean state) 2815 { 2816 if (path == null) 2817 return; 2818 2819 doExpandParents(path, state); 2820 } 2821 clearToggledPaths()2822 protected void clearToggledPaths() 2823 { 2824 nodeStates.clear(); 2825 } 2826 getDescendantToggledPaths(TreePath parent)2827 protected Enumeration<TreePath> getDescendantToggledPaths(TreePath parent) 2828 { 2829 if (parent == null) 2830 return null; 2831 2832 Enumeration nodes = nodeStates.keys(); 2833 Vector result = new Vector(); 2834 2835 while (nodes.hasMoreElements()) 2836 { 2837 TreePath path = (TreePath) nodes.nextElement(); 2838 2839 if (path.isDescendant(parent)) 2840 result.addElement(path); 2841 } 2842 2843 return result.elements(); 2844 } 2845 hasBeenExpanded(TreePath path)2846 public boolean hasBeenExpanded(TreePath path) 2847 { 2848 if (path == null) 2849 return false; 2850 2851 return nodeStates.get(path) != null; 2852 } 2853 isVisible(TreePath path)2854 public boolean isVisible(TreePath path) 2855 { 2856 if (path == null) 2857 return false; 2858 2859 TreePath parent = path.getParentPath(); 2860 2861 if (parent == null) 2862 return true; // Is root node. 2863 2864 return isExpanded(parent); 2865 } 2866 makeVisible(TreePath path)2867 public void makeVisible(TreePath path) 2868 { 2869 if (path == null) 2870 return; 2871 2872 expandPath(path.getParentPath()); 2873 } 2874 isPathEditable(TreePath path)2875 public boolean isPathEditable(TreePath path) 2876 { 2877 return isEditable(); 2878 } 2879 2880 /** 2881 * Creates and returns an instance of {@link TreeModelHandler}. 2882 * 2883 * @return an instance of {@link TreeModelHandler} 2884 */ createTreeModelListener()2885 protected TreeModelListener createTreeModelListener() 2886 { 2887 return new TreeModelHandler(); 2888 } 2889 2890 /** 2891 * Returns a sample TreeModel that can be used in a JTree. This can be used 2892 * in Bean- or GUI-Builders to show something interesting. 2893 * 2894 * @return a sample TreeModel that can be used in a JTree 2895 */ getDefaultTreeModel()2896 protected static TreeModel getDefaultTreeModel() 2897 { 2898 DefaultMutableTreeNode root = new DefaultMutableTreeNode("Root node"); 2899 DefaultMutableTreeNode child1 = new DefaultMutableTreeNode("Child node 1"); 2900 DefaultMutableTreeNode child11 = 2901 new DefaultMutableTreeNode("Child node 1.1"); 2902 DefaultMutableTreeNode child12 = 2903 new DefaultMutableTreeNode("Child node 1.2"); 2904 DefaultMutableTreeNode child13 = 2905 new DefaultMutableTreeNode("Child node 1.3"); 2906 DefaultMutableTreeNode child2 = new DefaultMutableTreeNode("Child node 2"); 2907 DefaultMutableTreeNode child21 = 2908 new DefaultMutableTreeNode("Child node 2.1"); 2909 DefaultMutableTreeNode child22 = 2910 new DefaultMutableTreeNode("Child node 2.2"); 2911 DefaultMutableTreeNode child23 = 2912 new DefaultMutableTreeNode("Child node 2.3"); 2913 DefaultMutableTreeNode child24 = 2914 new DefaultMutableTreeNode("Child node 2.4"); 2915 2916 DefaultMutableTreeNode child3 = new DefaultMutableTreeNode("Child node 3"); 2917 root.add(child1); 2918 root.add(child2); 2919 root.add(child3); 2920 child1.add(child11); 2921 child1.add(child12); 2922 child1.add(child13); 2923 child2.add(child21); 2924 child2.add(child22); 2925 child2.add(child23); 2926 child2.add(child24); 2927 return new DefaultTreeModel(root); 2928 } 2929 2930 /** 2931 * Converts the specified value to a String. This is used by the renderers 2932 * of this JTree and its nodes. 2933 * 2934 * This implementation simply returns <code>value.toString()</code> and 2935 * ignores all other parameters. Subclass this method to control the 2936 * conversion. 2937 * 2938 * @param value the value that is converted to a String 2939 * @param selected indicates if that value is selected or not 2940 * @param expanded indicates if that value is expanded or not 2941 * @param leaf indicates if that value is a leaf node or not 2942 * @param row the row of the node 2943 * @param hasFocus indicates if that node has focus or not 2944 */ convertValueToText(Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus)2945 public String convertValueToText(Object value, boolean selected, 2946 boolean expanded, boolean leaf, int row, boolean hasFocus) 2947 { 2948 return value.toString(); 2949 } 2950 2951 /** 2952 * A String representation of this JTree. This is intended to be used for 2953 * debugging. The returned string may be empty but may not be 2954 * <code>null</code>. 2955 * 2956 * @return a String representation of this JTree 2957 */ paramString()2958 protected String paramString() 2959 { 2960 // TODO: this is completely legal, but it would possibly be nice 2961 // to return some more content, like the tree structure, some properties 2962 // etc ... 2963 return ""; 2964 } 2965 2966 /** 2967 * Returns all TreePath objects which are a descendants of the given path 2968 * and are exapanded at the moment of the execution of this method. If the 2969 * state of any node is beeing toggled while this method is executing this 2970 * change may be left unaccounted. 2971 * 2972 * @param path The parent of this request 2973 * 2974 * @return An Enumeration containing TreePath objects 2975 */ getExpandedDescendants(TreePath path)2976 public Enumeration<TreePath> getExpandedDescendants(TreePath path) 2977 { 2978 Enumeration paths = nodeStates.keys(); 2979 Vector relevantPaths = new Vector(); 2980 while (paths.hasMoreElements()) 2981 { 2982 TreePath nextPath = (TreePath) paths.nextElement(); 2983 if (nodeStates.get(nextPath) == EXPANDED 2984 && path.isDescendant(nextPath)) 2985 { 2986 relevantPaths.add(nextPath); 2987 } 2988 } 2989 return relevantPaths.elements(); 2990 } 2991 2992 /** 2993 * Returns the next table element (beginning from the row 2994 * <code>startingRow</code> that starts with <code>prefix</code>. 2995 * Searching is done in the direction specified by <code>bias</code>. 2996 * 2997 * @param prefix the prefix to search for in the cell values 2998 * @param startingRow the index of the row where to start searching from 2999 * @param bias the search direction, either {@link Position.Bias#Forward} or 3000 * {@link Position.Bias#Backward} 3001 * 3002 * @return the path to the found element or -1 if no such element has been 3003 * found 3004 * 3005 * @throws IllegalArgumentException if prefix is <code>null</code> or 3006 * startingRow is not valid 3007 * 3008 * @since 1.4 3009 */ getNextMatch(String prefix, int startingRow, Position.Bias bias)3010 public TreePath getNextMatch(String prefix, int startingRow, 3011 Position.Bias bias) 3012 { 3013 if (prefix == null) 3014 throw new IllegalArgumentException("The argument 'prefix' must not be" 3015 + " null."); 3016 if (startingRow < 0) 3017 throw new IllegalArgumentException("The argument 'startingRow' must not" 3018 + " be less than zero."); 3019 3020 int size = getRowCount(); 3021 if (startingRow > size) 3022 throw new IllegalArgumentException("The argument 'startingRow' must not" 3023 + " be greater than the number of" 3024 + " elements in the TreeModel."); 3025 3026 TreePath foundPath = null; 3027 if (bias == Position.Bias.Forward) 3028 { 3029 for (int i = startingRow; i < size; i++) 3030 { 3031 TreePath path = getPathForRow(i); 3032 Object o = path.getLastPathComponent(); 3033 // FIXME: in the following call to convertValueToText the 3034 // last argument (hasFocus) should be done right. 3035 String item = convertValueToText(o, isRowSelected(i), 3036 isExpanded(i), treeModel.isLeaf(o), 3037 i, false); 3038 if (item.startsWith(prefix)) 3039 { 3040 foundPath = path; 3041 break; 3042 } 3043 } 3044 } 3045 else 3046 { 3047 for (int i = startingRow; i >= 0; i--) 3048 { 3049 TreePath path = getPathForRow(i); 3050 Object o = path.getLastPathComponent(); 3051 // FIXME: in the following call to convertValueToText the 3052 // last argument (hasFocus) should be done right. 3053 String item = convertValueToText(o, isRowSelected(i), 3054 isExpanded(i), treeModel.isLeaf(o), i, false); 3055 if (item.startsWith(prefix)) 3056 { 3057 foundPath = path; 3058 break; 3059 } 3060 } 3061 } 3062 return foundPath; 3063 } 3064 3065 /** 3066 * Removes any paths in the current set of selected paths that are 3067 * descendants of <code>path</code>. If <code>includePath</code> is set 3068 * to <code>true</code> and <code>path</code> itself is selected, then 3069 * it will be removed too. 3070 * 3071 * @param path the path from which selected descendants are to be removed 3072 * @param includeSelected if <code>true</code> then <code>path</code> itself 3073 * will also be remove if it's selected 3074 * 3075 * @return <code>true</code> if something has been removed, 3076 * <code>false</code> otherwise 3077 * 3078 * @since 1.3 3079 */ removeDescendantSelectedPaths(TreePath path, boolean includeSelected)3080 protected boolean removeDescendantSelectedPaths(TreePath path, 3081 boolean includeSelected) 3082 { 3083 boolean removedSomething = false; 3084 TreePath[] selected = getSelectionPaths(); 3085 for (int index = 0; index < selected.length; index++) 3086 { 3087 if ((selected[index] == path && includeSelected) 3088 || (selected[index].isDescendant(path))) 3089 { 3090 removeSelectionPath(selected[index]); 3091 removedSomething = true; 3092 } 3093 } 3094 return removedSomething; 3095 } 3096 3097 /** 3098 * Removes any descendants of the TreePaths in toRemove that have been 3099 * expanded. 3100 * 3101 * @param toRemove - Enumeration of TreePaths that need to be removed from 3102 * cache of toggled tree paths. 3103 */ removeDescendantToggledPaths(Enumeration<TreePath> toRemove)3104 protected void removeDescendantToggledPaths(Enumeration<TreePath> toRemove) 3105 { 3106 while (toRemove.hasMoreElements()) 3107 { 3108 TreePath current = toRemove.nextElement(); 3109 Enumeration descendants = getDescendantToggledPaths(current); 3110 3111 while (descendants.hasMoreElements()) 3112 { 3113 TreePath currentDes = (TreePath) descendants.nextElement(); 3114 if (isExpanded(currentDes)) 3115 nodeStates.remove(currentDes); 3116 } 3117 } 3118 } 3119 3120 /** 3121 * <p> 3122 * Sent when the tree has changed enough that we need to resize the bounds, 3123 * but not enough that we need to remove the expanded node set (e.g nodes were 3124 * expanded or collapsed, or nodes were inserted into the tree). You should 3125 * never have to invoke this, the UI will invoke this as it needs to. 3126 * </p> 3127 * <p> 3128 * If the tree uses {@link DefaultTreeModel}, you must call 3129 * {@link DefaultTreeModel#reload(TreeNode)} or 3130 * {@link DefaultTreeModel#reload()} after adding or removing nodes. Following 3131 * the official Java 1.5 API standard, just calling treeDidChange, repaint() 3132 * or revalidate() does <i>not</i> update the tree appearance properly. 3133 * 3134 * @see DefaultTreeModel#reload() 3135 */ treeDidChange()3136 public void treeDidChange() 3137 { 3138 repaint(); 3139 } 3140 3141 /** 3142 * Helper method for 3143 * {@link LookAndFeel#installProperty(JComponent, String, Object)}. 3144 * 3145 * @param propertyName the name of the property 3146 * @param value the value of the property 3147 * 3148 * @throws IllegalArgumentException if the specified property cannot be set 3149 * by this method 3150 * @throws ClassCastException if the property value does not match the 3151 * property type 3152 * @throws NullPointerException if <code>c</code> or 3153 * <code>propertyValue</code> is <code>null</code> 3154 */ setUIProperty(String propertyName, Object value)3155 void setUIProperty(String propertyName, Object value) 3156 { 3157 if (propertyName.equals("rowHeight")) 3158 { 3159 if (! clientRowHeightSet) 3160 { 3161 setRowHeight(((Integer) value).intValue()); 3162 clientRowHeightSet = false; 3163 } 3164 } 3165 else if (propertyName.equals("scrollsOnExpand")) 3166 { 3167 if (! clientScrollsOnExpandSet) 3168 { 3169 setScrollsOnExpand(((Boolean) value).booleanValue()); 3170 clientScrollsOnExpandSet = false; 3171 } 3172 } 3173 else if (propertyName.equals("showsRootHandles")) 3174 { 3175 if (! clientShowsRootHandlesSet) 3176 { 3177 setShowsRootHandles(((Boolean) value).booleanValue()); 3178 clientShowsRootHandlesSet = false; 3179 } 3180 } 3181 else 3182 { 3183 super.setUIProperty(propertyName, value); 3184 } 3185 } 3186 } 3187