Source for javax.swing.JTree

   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: }