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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 703: public void removePropertyChangeListener(PropertyChangeListener l) 704: { 705: // Nothing to do here. 706: } 707: 708: /** 709: * Requests focus for this object. 710: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 847: public void setVisible(boolean b) 848: { 849: // Nothing to do here. 850: } 851: } 852: 853: /** 854: * Constructor 855: */ 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: */ 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: */ 874: public void clearAccessibleSelection() 875: { 876: clearSelection(); 877: } 878: 879: /** 880: * Fire a visible data property change notification. 881: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: 1101: public DynamicUtilTreeNode(Object value, Object children) 1102: { 1103: super(value); 1104: childValue = children; 1105: loadedChildren = false; 1106: } 1107: 1108: public int getChildCount() 1109: { 1110: loadChildren(); 1111: return super.getChildCount(); 1112: } 1113: 1114: protected void loadChildren() 1115: { 1116: if (!loadedChildren) 1117: { 1118: createChildren(this, childValue); 1119: loadedChildren = true; 1120: } 1121: } 1122: 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: */ 1137: public TreeNode getChildAt(int pos) 1138: { 1139: loadChildren(); 1140: return super.getChildAt(pos); 1141: } 1142: 1143: public boolean isLeaf() 1144: { 1145: return childValue == null || !(childValue instanceof Hashtable 1146: || childValue instanceof Vector 1147: || childValue.getClass().isArray()); 1148: } 1149: 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 1604: public JTree(Vector<?> value) 1605: { 1606: this(createTreeModel(value)); 1607: } 1608: 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: 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: 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: 1783: public boolean getScrollableTracksViewportHeight() 1784: { 1785: if (getParent() instanceof JViewport) 1786: return ((JViewport) getParent()).getHeight() > getPreferredSize().height; 1787: return false; 1788: } 1789: 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 2041: public boolean isRootVisible() 2042: { 2043: return rootVisible; 2044: } 2045: 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: 2066: public boolean getShowsRootHandles() 2067: { 2068: return showsRootHandles; 2069: } 2070: 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: 2083: public TreeCellEditor getCellEditor() 2084: { 2085: return cellEditor; 2086: } 2087: 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: 2098: public TreeCellRenderer getCellRenderer() 2099: { 2100: return cellRenderer; 2101: } 2102: 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: 2113: public TreeSelectionModel getSelectionModel() 2114: { 2115: return selectionModel; 2116: } 2117: 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: 2139: public int getVisibleRowCount() 2140: { 2141: return visibleRowCount; 2142: } 2143: 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: 2154: public boolean isLargeModel() 2155: { 2156: return largeModel; 2157: } 2158: 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: 2169: public int getRowHeight() 2170: { 2171: return rowHeight; 2172: } 2173: 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: 2186: public boolean isFixedRowHeight() 2187: { 2188: return rowHeight > 0; 2189: } 2190: 2191: public boolean getInvokesStopCellEditing() 2192: { 2193: return invokesStopCellEditing; 2194: } 2195: 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: */ 2210: public int getToggleClickCount() 2211: { 2212: return toggleClickCount; 2213: } 2214: 2215: /** 2216: * @since 1.3 2217: */ 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: 2228: public void scrollPathToVisible(TreePath path) 2229: { 2230: if (path == null) 2231: return; 2232: Rectangle rect = getPathBounds(path); 2233: scrollRectToVisible(rect); 2234: } 2235: 2236: public void scrollRowToVisible(int row) 2237: { 2238: scrollPathToVisible(getPathForRow(row)); 2239: } 2240: 2241: public boolean getScrollsOnExpand() 2242: { 2243: return scrollsOnExpand; 2244: } 2245: 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: 2257: public void setSelectionPath(TreePath path) 2258: { 2259: clearSelectionPathStates(); 2260: selectionModel.setSelectionPath(path); 2261: } 2262: 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: */ 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: 2282: public void setSelectionRow(int row) 2283: { 2284: TreePath path = getPathForRow(row); 2285: 2286: if (path != null) 2287: setSelectionPath(path); 2288: } 2289: 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: 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: 2312: public void addSelectionPath(TreePath path) 2313: { 2314: selectionModel.addSelectionPath(path); 2315: } 2316: 2317: public void addSelectionPaths(TreePath[] paths) 2318: { 2319: selectionModel.addSelectionPaths(paths); 2320: } 2321: 2322: public void addSelectionRow(int row) 2323: { 2324: TreePath path = getPathForRow(row); 2325: 2326: if (path != null) 2327: selectionModel.addSelectionPath(path); 2328: } 2329: 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: */ 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: 2360: public void removeSelectionPath(TreePath path) 2361: { 2362: clearSelectionPathStates(); 2363: selectionModel.removeSelectionPath(path); 2364: } 2365: 2366: public void removeSelectionPaths(TreePath[] paths) 2367: { 2368: clearSelectionPathStates(); 2369: selectionModel.removeSelectionPaths(paths); 2370: } 2371: 2372: public void removeSelectionRow(int row) 2373: { 2374: TreePath path = getPathForRow(row); 2375: 2376: if (path != null) 2377: removeSelectionPath(path); 2378: } 2379: 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: 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: 2401: public void clearSelection() 2402: { 2403: selectionModel.clearSelection(); 2404: setLeadSelectionPath(null); 2405: } 2406: 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: */ 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: */ 2443: public TreePath getAnchorSelectionPath() 2444: { 2445: return anchorSelectionPath; 2446: } 2447: 2448: /** 2449: * @since 1.3 2450: */ 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: 2461: public int getLeadSelectionRow() 2462: { 2463: return selectionModel.getLeadSelectionRow(); 2464: } 2465: 2466: public int getMaxSelectionRow() 2467: { 2468: return selectionModel.getMaxSelectionRow(); 2469: } 2470: 2471: public int getMinSelectionRow() 2472: { 2473: return selectionModel.getMinSelectionRow(); 2474: } 2475: 2476: public int getSelectionCount() 2477: { 2478: return selectionModel.getSelectionCount(); 2479: } 2480: 2481: public TreePath getSelectionPath() 2482: { 2483: return selectionModel.getSelectionPath(); 2484: } 2485: 2486: public TreePath[] getSelectionPaths() 2487: { 2488: return selectionModel.getSelectionPaths(); 2489: } 2490: 2491: public int[] getSelectionRows() 2492: { 2493: return selectionModel.getSelectionRows(); 2494: } 2495: 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: */ 2511: public boolean isRowSelected(int row) 2512: { 2513: return selectionModel.isRowSelected(row); 2514: } 2515: 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: */ 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: */ 2540: public void setDragEnabled(boolean enabled) 2541: { 2542: dragEnabled = enabled; 2543: } 2544: 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: 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: 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: 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: 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: 2611: public boolean isCollapsed(TreePath path) 2612: { 2613: return !isExpanded(path); 2614: } 2615: 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: 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: 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: */ 2663: public boolean getExpandsSelectedPaths() 2664: { 2665: return expandsSelectedPaths; 2666: } 2667: 2668: /** 2669: * @since 1.3 2670: */ 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: 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: 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: 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: 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: 2722: public void cancelEditing() 2723: { 2724: TreeUI ui = getUI(); 2725: 2726: if (isEditing()) 2727: if (ui != null) 2728: ui.cancelEditing(this); 2729: } 2730: 2731: public void startEditingAtPath(TreePath path) 2732: { 2733: TreeUI ui = getUI(); 2734: 2735: if (ui != null) 2736: ui.startEditingAtPath(this, path); 2737: } 2738: 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: 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: 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: 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: 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: 2794: public Object getLastSelectedPathComponent() 2795: { 2796: TreePath path = getSelectionPath(); 2797: 2798: if (path != null) 2799: return path.getLastPathComponent(); 2800: 2801: return null; 2802: } 2803: 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: 2814: protected void setExpandedState(TreePath path, boolean state) 2815: { 2816: if (path == null) 2817: return; 2818: 2819: doExpandParents(path, state); 2820: } 2821: 2822: protected void clearToggledPaths() 2823: { 2824: nodeStates.clear(); 2825: } 2826: 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: 2846: public boolean hasBeenExpanded(TreePath path) 2847: { 2848: if (path == null) 2849: return false; 2850: 2851: return nodeStates.get(path) != null; 2852: } 2853: 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: 2867: public void makeVisible(TreePath path) 2868: { 2869: if (path == null) 2870: return; 2871: 2872: expandPath(path.getParentPath()); 2873: } 2874: 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 3104: protected void removeDescendantToggledPaths(Enumeration<TreePath> toRemove) 3105: { 3106: while (toRemove.hasMoreElements()) 3107: { 3108: TreePath current = (TreePath) 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: */ 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: */ 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: }