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.Dimension;
  41: import java.awt.Rectangle;
  42: import java.io.Serializable;
  43: import java.util.Enumeration;
  44: import java.util.Hashtable;
  45: import java.util.Iterator;
  46: import java.util.Vector;
  47: 
  48: import javax.accessibility.Accessible;
  49: import javax.accessibility.AccessibleContext;
  50: import javax.swing.event.TreeExpansionEvent;
  51: import javax.swing.event.TreeExpansionListener;
  52: import javax.swing.event.TreeModelEvent;
  53: import javax.swing.event.TreeModelListener;
  54: import javax.swing.event.TreeSelectionEvent;
  55: import javax.swing.event.TreeSelectionListener;
  56: import javax.swing.event.TreeWillExpandListener;
  57: import javax.swing.plaf.TreeUI;
  58: import javax.swing.text.Position;
  59: import javax.swing.tree.DefaultMutableTreeNode;
  60: import javax.swing.tree.DefaultTreeCellRenderer;
  61: import javax.swing.tree.DefaultTreeModel;
  62: import javax.swing.tree.DefaultTreeSelectionModel;
  63: import javax.swing.tree.ExpandVetoException;
  64: import javax.swing.tree.TreeCellEditor;
  65: import javax.swing.tree.TreeCellRenderer;
  66: import javax.swing.tree.TreeModel;
  67: import javax.swing.tree.TreeNode;
  68: import javax.swing.tree.TreePath;
  69: import javax.swing.tree.TreeSelectionModel;
  70: 
  71: public class JTree
  72:         extends    JComponent
  73:             implements Scrollable, Accessible
  74: {
  75:     /**
  76:      * Listens to the model of the JTree and updates the property
  77:      * <code>expandedState</code> if nodes are removed or changed.
  78:      */
  79:     protected class TreeModelHandler
  80:             implements
  81:             TreeModelListener
  82:     {
  83: 
  84:         /**
  85:          * Creates a new instance of TreeModelHandler.
  86:          */
  87:         protected TreeModelHandler()
  88:         {
  89:         }
  90: 
  91:         /**
  92:          * Notifies when a node has changed in some ways. This does not include
  93:          * that a node has changed its location or changed it's children. It
  94:          * only means that some attributes of the node have changed that might
  95:          * affect its presentation.
  96:          * 
  97:          * This method is called after the actual change occured.
  98:          * 
  99:          * @param ev the TreeModelEvent describing the change
 100:          */
 101:         public void treeNodesChanged(TreeModelEvent ev)
 102:         {
 103:             // nothing to do here
 104:         }
 105: 
 106:         /**
 107:          * Notifies when a node is inserted into the tree.
 108:          * 
 109:          * This method is called after the actual change occured.
 110:          * 
 111:          * @param ev the TreeModelEvent describing the change
 112:          */
 113:         public void treeNodesInserted(TreeModelEvent ev)
 114:         {
 115:             // nothing to do here
 116:         }
 117: 
 118:         /**
 119:          * Notifies when a node is removed from the tree.
 120:          * 
 121:          * This method is called after the actual change occured.
 122:          * 
 123:          * @param ev the TreeModelEvent describing the change
 124:          */
 125:         public void treeNodesRemoved(TreeModelEvent ev)
 126:         {
 127:             // TODO: The API docs suggest that this method should do something
 128:             // but I cannot really see what has to be done here ...
 129:         }
 130: 
 131:         /**
 132:          * Notifies when the structure of the tree is changed.
 133:          * 
 134:          * This method is called after the actual change occured.
 135:          * 
 136:          * @param ev the TreeModelEvent describing the change
 137:          */
 138:         public void treeStructureChanged(TreeModelEvent ev)
 139:         {
 140:             // set state of new path
 141:             TreePath path = ev.getTreePath();
 142:             setExpandedState(path, isExpanded(path));
 143:         }
 144:     } // TreeModelHandler
 145: 
 146:     /**
 147:      * This redirects TreeSelectionEvents and rewrites the source of it to be
 148:      * this JTree. This is typically done when the tree model generates an
 149:      * event, but the JTree object associated with that model should be listed
 150:      * as the actual source of the event.
 151:      */
 152:     protected class TreeSelectionRedirector
 153:             implements
 154:             TreeSelectionListener,
 155:             Serializable
 156:     {
 157:         /** The serial version UID. */
 158:         private static final long serialVersionUID = -3505069663646241664L;
 159: 
 160:         /**
 161:          * Creates a new instance of TreeSelectionRedirector
 162:          */
 163:         protected TreeSelectionRedirector()
 164:         {
 165:         }
 166: 
 167:         /**
 168:          * Notifies when the tree selection changes.
 169:          * 
 170:          * @param ev the TreeSelectionEvent that describes the change
 171:          */
 172:         public void valueChanged(TreeSelectionEvent ev)
 173:         {
 174:             TreeSelectionEvent rewritten = (TreeSelectionEvent) ev
 175:                     .cloneWithSource(JTree.this);
 176:             fireValueChanged(rewritten);
 177:             JTree.this.repaint();
 178:         }
 179:     } // TreeSelectionRedirector
 180: 
 181:     /**
 182:      * A TreeModel that does not allow anything to be selected.
 183:      */
 184:     protected static class EmptySelectionModel
 185:             extends
 186:                 DefaultTreeSelectionModel
 187:     {
 188:         /** The serial version UID. */
 189:         private static final long serialVersionUID = -5815023306225701477L;
 190: 
 191:         /**
 192:          * The shared instance of this model.
 193:          */
 194:         protected static final EmptySelectionModel sharedInstance = new EmptySelectionModel();
 195: 
 196:         /**
 197:          * Creates a new instance of EmptySelectionModel.
 198:          */
 199:         protected EmptySelectionModel()
 200:         {
 201:         }
 202: 
 203:         /**
 204:          * Returns the shared instance of EmptySelectionModel.
 205:          * 
 206:          * @return the shared instance of EmptySelectionModel
 207:          */
 208:         public static EmptySelectionModel sharedInstance()
 209:         {
 210:             return sharedInstance;
 211:         }
 212: 
 213:         /**
 214:          * This catches attempts to set a selection and sets nothing instead.
 215:          * 
 216:          * @param paths not used here
 217:          */
 218:         public void setSelectionPaths(TreePath[] paths)
 219:         {
 220:             // we don't allow selections in this class
 221:         }
 222: 
 223:         /**
 224:          * This catches attempts to add something to the selection.
 225:          * 
 226:          * @param paths not used here
 227:          */
 228:         public void addSelectionPaths(TreePath[] paths)
 229:         {
 230:             // we don't allow selections in this class
 231:         }
 232: 
 233:         /**
 234:          * This catches attempts to remove something from the selection.
 235:          * 
 236:          * @param paths not used here
 237:          */
 238:         public void removeSelectionPaths(TreePath[] paths)
 239:         {
 240:             // we don't allow selections in this class
 241:         }
 242:     }// EmptySelectionModel
 243: 
 244:     private static final long serialVersionUID = 7559816092864483649L;
 245:     public static final String CELL_EDITOR_PROPERTY = "cellEditor";
 246:     public static final String CELL_RENDERER_PROPERTY = "cellRenderer";
 247:     public static final String EDITABLE_PROPERTY = "editable";
 248:     public static final String INVOKES_STOP_CELL_EDITING_PROPERTY = "invokesStopCellEditing";
 249:     public static final String LARGE_MODEL_PROPERTY = "largeModel";
 250:     public static final String ROOT_VISIBLE_PROPERTY = "rootVisible";
 251:     public static final String ROW_HEIGHT_PROPERTY = "rowHeight";
 252:     public static final String SCROLLS_ON_EXPAND_PROPERTY = "scrollsOnExpand";
 253:     public static final String SELECTION_MODEL_PROPERTY = "selectionModel";
 254:     public static final String SHOWS_ROOT_HANDLES_PROPERTY = "showsRootHandles";
 255:     public static final String TOGGLE_CLICK_COUNT_PROPERTY = "toggleClickCount";
 256:     public static final String TREE_MODEL_PROPERTY = "model";
 257:     public static final String VISIBLE_ROW_COUNT_PROPERTY = "visibleRowCount";
 258: 
 259:     /** @since 1.3 */
 260:     public static final String ANCHOR_SELECTION_PATH_PROPERTY = "anchorSelectionPath";
 261: 
 262:     /** @since 1.3 */
 263:     public static final String LEAD_SELECTION_PATH_PROPERTY = "leadSelectionPath";
 264: 
 265:     /** @since 1.3 */
 266:     public static final String EXPANDS_SELECTED_PATHS_PROPERTY = "expandsSelectedPaths";
 267:     private static final Object EXPANDED = new Object();
 268:     private static final Object COLLAPSED = new Object();
 269:     private boolean dragEnabled;
 270:     private boolean expandsSelectedPaths;
 271:     private TreePath anchorSelectionPath;
 272:     private TreePath leadSelectionPath;
 273: 
 274:     /*
 275:      * This contains the state of all nodes in the tree. Al/ entries map the
 276:      * TreePath of a note to to its state. Valid states are EXPANDED and
 277:      * COLLAPSED. Nodes not in this Hashtable are assumed state COLLAPSED.
 278:      */
 279:     private Hashtable nodeStates = new Hashtable();
 280:     protected transient TreeCellEditor cellEditor;
 281:     protected transient TreeCellRenderer cellRenderer;
 282:     protected boolean editable;
 283:     protected boolean invokesStopCellEditing;
 284:     protected boolean largeModel;
 285:     protected boolean rootVisible;
 286:     protected int rowHeight;
 287:     protected boolean scrollsOnExpand;
 288:     protected transient TreeSelectionModel selectionModel;
 289:     protected boolean showsRootHandles;
 290:     protected int toggleClickCount;
 291:     protected transient TreeModel treeModel;
 292:     protected int visibleRowCount;
 293: 
 294:     /**
 295:      * Handles TreeModelEvents to update the expandedState.
 296:      */
 297:     protected transient TreeModelListener treeModelListener;
 298: 
 299:     /**
 300:      * Redirects TreeSelectionEvents so that the source is this JTree.
 301:      */
 302:     protected TreeSelectionRedirector selectionRedirector = 
 303:                                         new TreeSelectionRedirector();
 304: 
 305:     /**
 306:      * Creates a new <code>JTree</code> object.
 307:      */
 308:     public JTree()
 309:     {
 310:         this(createTreeModel(null));
 311:     }
 312: 
 313:     /**
 314:      * Creates a new <code>JTree</code> object.
 315:      * 
 316:      * @param value the initial nodes in the tree
 317:      */
 318:     public JTree(Hashtable value)
 319:     {
 320:         this(createTreeModel(value));
 321:     }
 322: 
 323:     /**
 324:      * Creates a new <code>JTree</code> object.
 325:      * 
 326:      * @param value the initial nodes in the tree
 327:      */
 328:     public JTree(Object[] value)
 329:     {
 330:         this(createTreeModel(value));
 331:     }
 332: 
 333:     /**
 334:      * Creates a new <code>JTree</code> object.
 335:      * 
 336:      * @param model the model to use
 337:      */
 338:     public JTree(TreeModel model)
 339:     {
 340:         setModel(model);
 341:         setSelectionModel(EmptySelectionModel.sharedInstance());
 342:         selectionModel.addTreeSelectionListener(selectionRedirector);
 343:         setCellRenderer(new DefaultTreeCellRenderer());
 344:         updateUI();
 345:     }
 346: 
 347:     /**
 348:      * Creates a new <code>JTree</code> object.
 349:      * 
 350:      * @param root the root node
 351:      */
 352:     public JTree(TreeNode root)
 353:     {
 354:         this(root, false);
 355:     }
 356: 
 357:     /**
 358:      * Creates a new <code>JTree</code> object.
 359:      * 
 360:      * @param root the root node
 361:      * @param asksAllowChildren if false, all nodes without children are leaf
 362:      *        nodes. If true, only nodes that do not allow children are leaf
 363:      *        nodes.
 364:      */
 365:     public JTree(TreeNode root, boolean asksAllowChildren)
 366:     {
 367:         this(new DefaultTreeModel(root, asksAllowChildren));
 368:     }
 369: 
 370:     /**
 371:      * Creates a new <code>JTree</code> object.
 372:      * 
 373:      * @param value the initial nodes in the tree
 374:      */
 375:     public JTree(Vector value)
 376:     {
 377:         this(createTreeModel(value));
 378:     }
 379: 
 380:     public static class DynamicUtilTreeNode
 381:             extends
 382:                 DefaultMutableTreeNode
 383:     {
 384:         protected Object childValue;
 385:         protected boolean loadedChildren;
 386: 
 387:         /**
 388:          * Currently not set or used by this class. It might be set and used in
 389:          * later versions of this class.
 390:          */
 391:         protected boolean hasChildren;
 392: 
 393:         public DynamicUtilTreeNode(Object value, Object children)
 394:         {
 395:             super(value);
 396:             childValue = children;
 397:             loadedChildren = false;
 398:         }
 399: 
 400:         public int getChildCount()
 401:         {
 402:             loadChildren();
 403:             return super.getChildCount();
 404:         }
 405: 
 406:         protected void loadChildren()
 407:         {
 408:             if (!loadedChildren)
 409:             {
 410:                 createChildren(this, childValue);
 411:                 loadedChildren = true;
 412:             }
 413:         }
 414: 
 415:         public Enumeration children()
 416:         {
 417:             loadChildren();
 418:             return super.children();
 419:         }
 420: 
 421:         /**
 422:          * Returns the child node at position <code>pos</code>. Subclassed
 423:          * here to load the children if necessary.
 424:          * 
 425:          * @param pos the position of the child node to fetch
 426:          * 
 427:          * @return the childnode at the specified position
 428:          */
 429:         public TreeNode getChildAt(int pos)
 430:         {
 431:             loadChildren();
 432:             return super.getChildAt(pos);
 433:         }
 434: 
 435:         public boolean isLeaf()
 436:         {
 437:             return (childValue == null || !(childValue instanceof Hashtable
 438:                     || childValue instanceof Vector || childValue.getClass()
 439:                     .isArray()));
 440:         }
 441: 
 442:         public static void createChildren(DefaultMutableTreeNode parent,
 443:                 Object children)
 444:         {
 445:             if (children instanceof Hashtable)
 446:             {
 447:                 Hashtable tab = (Hashtable) children;
 448:                 Enumeration e = tab.keys();
 449:                 while (e.hasMoreElements())
 450:                 {
 451:                     Object key = e.nextElement();
 452:                     Object val = tab.get(key);
 453:                     parent.add(new DynamicUtilTreeNode(key, val));
 454:                 }
 455:             } else if (children instanceof Vector)
 456:             {
 457:                 Iterator i = ((Vector) children).iterator();
 458:                 while (i.hasNext())
 459:                 {
 460:                     Object n = i.next();
 461:                     parent.add(new DynamicUtilTreeNode(n, n));
 462:                 }
 463:             } else if (children != null && children.getClass().isArray())
 464:             {
 465:                 Object[] arr = (Object[]) children;
 466:                 for (int i = 0; i < arr.length; ++i)
 467:                     parent.add(new DynamicUtilTreeNode(arr[i], arr[i]));
 468:             }
 469:         }
 470:     }
 471: 
 472:     public int getRowForPath(TreePath path)
 473:     {
 474:         TreeUI ui = getUI();
 475: 
 476:         if (ui != null)
 477:             return ui.getRowForPath(this, path);
 478: 
 479:         return -1;
 480:     }
 481: 
 482:     public TreePath getPathForRow(int row)
 483:     {
 484:         TreeUI ui = getUI();
 485:         return ui != null ? ui.getPathForRow(this, row) : null;
 486:     }
 487: 
 488:     protected TreePath[] getPathBetweenRows(int index0, int index1)
 489:     {
 490:         TreeUI ui = getUI();
 491: 
 492:         if (ui == null)
 493:             return null;
 494: 
 495:         int minIndex = Math.min(index0, index1);
 496:         int maxIndex = Math.max(index0, index1);
 497:         TreePath[] paths = new TreePath[maxIndex - minIndex + 1];
 498: 
 499:         for (int i = minIndex; i <= maxIndex; ++i)
 500:             paths[i - minIndex] = ui.getPathForRow(this, i);
 501: 
 502:         return paths;
 503:     }
 504: 
 505:     /**
 506:      * Creates a new <code>TreeModel</code> object.
 507:      * 
 508:      * @param value the values stored in the model
 509:      */
 510:     protected static TreeModel createTreeModel(Object value)
 511:     {
 512:         return new DefaultTreeModel(new DynamicUtilTreeNode(value, value));
 513:     }
 514: 
 515:     /**
 516:      * Return the UI associated with this <code>JTree</code> object.
 517:      * 
 518:      * @return the associated <code>TreeUI</code> object
 519:      */
 520:     public TreeUI getUI()
 521:     {
 522:         return (TreeUI) ui;
 523:     }
 524: 
 525:     /**
 526:      * Sets the UI associated with this <code>JTree</code> object.
 527:      * 
 528:      * @param ui the <code>TreeUI</code> to associate
 529:      */
 530:     public void setUI(TreeUI ui)
 531:     {
 532:         super.setUI(ui);
 533:     }
 534: 
 535:     /**
 536:      * This method resets the UI used to the Look and Feel defaults..
 537:      */
 538:     public void updateUI()
 539:     {
 540:         setUI((TreeUI) UIManager.getUI(this));
 541:         revalidate();
 542:         repaint();
 543:     }
 544: 
 545:     /**
 546:      * This method returns the String ID of the UI class of Separator.
 547:      * 
 548:      * @return The UI class' String ID.
 549:      */
 550:     public String getUIClassID()
 551:     {
 552:         return "TreeUI";
 553:     }
 554: 
 555:     /**
 556:      * Gets the AccessibleContext associated with this
 557:      * <code>JToggleButton</code>.
 558:      * 
 559:      * @return the associated context
 560:      */
 561:     public AccessibleContext getAccessibleContext()
 562:     {
 563:         return null;
 564:     }
 565: 
 566:     /**
 567:      * Returns the preferred viewport size..
 568:      * 
 569:      * @return the preferred size
 570:      */
 571:     public Dimension getPreferredScrollableViewportSize()
 572:     {
 573:         return null;
 574:     }
 575: 
 576:     public int getScrollableUnitIncrement(Rectangle visibleRect,
 577:             int orientation, int direction)
 578:     {
 579:         return 1;
 580:     }
 581: 
 582:     public int getScrollableBlockIncrement(Rectangle visibleRect,
 583:             int orientation, int direction)
 584:     {
 585:         return 1;
 586:     }
 587: 
 588:     public boolean getScrollableTracksViewportWidth()
 589:     {
 590:         return false;
 591:     }
 592: 
 593:     public boolean getScrollableTracksViewportHeight()
 594:     {
 595:         return false;
 596:     }
 597: 
 598:     /**
 599:      * Adds a <code>TreeExpansionListener</code> object to the tree.
 600:      * 
 601:      * @param listener the listener to add
 602:      */
 603:     public void addTreeExpansionListener(TreeExpansionListener listener)
 604:     {
 605:         listenerList.add(TreeExpansionListener.class, listener);
 606:     }
 607: 
 608:     /**
 609:      * Removes a <code>TreeExpansionListener</code> object from the tree.
 610:      * 
 611:      * @param listener the listener to remove
 612:      */
 613:     public void removeTreeExpansionListener(TreeExpansionListener listener)
 614:     {
 615:         listenerList.remove(TreeExpansionListener.class, listener);
 616:     }
 617: 
 618:     /**
 619:      * Returns all added <code>TreeExpansionListener</code> objects.
 620:      * 
 621:      * @return an array of listeners
 622:      */
 623:     public TreeExpansionListener[] getTreeExpansionListeners()
 624:     {
 625:         return (TreeExpansionListener[]) getListeners(TreeExpansionListener.class);
 626:     }
 627: 
 628:     /**
 629:      * Notifies all listeners that the tree was collapsed.
 630:      * 
 631:      * @param path the path to the node that was collapsed
 632:      */
 633:     public void fireTreeCollapsed(TreePath path)
 634:     {
 635:         TreeExpansionEvent event = new TreeExpansionEvent(this, path);
 636:         TreeExpansionListener[] listeners = getTreeExpansionListeners();
 637: 
 638:         for (int index = 0; index < listeners.length; ++index)
 639:             listeners[index].treeCollapsed(event);
 640:     }
 641: 
 642:     /**
 643:      * Notifies all listeners that the tree was expanded.
 644:      * 
 645:      * @param path the path to the node that was expanded
 646:      */
 647:     public void fireTreeExpanded(TreePath path)
 648:     {
 649:         TreeExpansionEvent event = new TreeExpansionEvent(this, path);
 650:         TreeExpansionListener[] listeners = getTreeExpansionListeners();
 651: 
 652:         for (int index = 0; index < listeners.length; ++index)
 653:             listeners[index].treeExpanded(event);
 654:     }
 655: 
 656:     /**
 657:      * Adds a <code>TreeSelctionListener</code> object to the tree.
 658:      * 
 659:      * @param listener the listener to add
 660:      */
 661:     public void addTreeSelectionListener(TreeSelectionListener listener)
 662:     {
 663:         listenerList.add(TreeSelectionListener.class, listener);
 664:     }
 665: 
 666:     /**
 667:      * Removes a <code>TreeSelectionListener</code> object from the tree.
 668:      * 
 669:      * @param listener the listener to remove
 670:      */
 671:     public void removeTreeSelectionListener(TreeSelectionListener listener)
 672:     {
 673:         listenerList.remove(TreeSelectionListener.class, listener);
 674:     }
 675: 
 676:     /**
 677:      * Returns all added <code>TreeSelectionListener</code> objects.
 678:      * 
 679:      * @return an array of listeners
 680:      */
 681:     public TreeSelectionListener[] getTreeSelectionListeners()
 682:     {
 683:         return (TreeSelectionListener[]) 
 684:                     getListeners(TreeSelectionListener.class);
 685:     }
 686: 
 687:     /**
 688:      * Notifies all listeners when the selection of the tree changed.
 689:      * 
 690:      * @param event the event to send
 691:      */
 692:     protected void fireValueChanged(TreeSelectionEvent event)
 693:     {
 694:         TreeSelectionListener[] listeners = getTreeSelectionListeners();
 695: 
 696:         for (int index = 0; index < listeners.length; ++index)
 697:             listeners[index].valueChanged(event);
 698:     }
 699: 
 700:     /**
 701:      * Adds a <code>TreeWillExpandListener</code> object to the tree.
 702:      * 
 703:      * @param listener the listener to add
 704:      */
 705:     public void addTreeWillExpandListener(TreeWillExpandListener listener)
 706:     {
 707:         listenerList.add(TreeWillExpandListener.class, listener);
 708:     }
 709: 
 710:     /**
 711:      * Removes a <code>TreeWillExpandListener</code> object from the tree.
 712:      * 
 713:      * @param listener the listener to remove
 714:      */
 715:     public void removeTreeWillExpandListener(TreeWillExpandListener listener)
 716:     {
 717:         listenerList.remove(TreeWillExpandListener.class, listener);
 718:     }
 719: 
 720:     /**
 721:      * Returns all added <code>TreeWillExpandListener</code> objects.
 722:      * 
 723:      * @return an array of listeners
 724:      */
 725:     public TreeWillExpandListener[] getTreeWillExpandListeners()
 726:     {
 727:         return (TreeWillExpandListener[]) 
 728:                     getListeners(TreeWillExpandListener.class);
 729:     }
 730: 
 731:     /**
 732:      * Notifies all listeners that the tree will collapse.
 733:      * 
 734:      * @param path the path to the node that will collapse
 735:      */
 736:     public void fireTreeWillCollapse(TreePath path) throws ExpandVetoException
 737:     {
 738:         TreeExpansionEvent event = new TreeExpansionEvent(this, path);
 739:         TreeWillExpandListener[] listeners = getTreeWillExpandListeners();
 740: 
 741:         for (int index = 0; index < listeners.length; ++index)
 742:             listeners[index].treeWillCollapse(event);
 743:     }
 744: 
 745:     /**
 746:      * Notifies all listeners that the tree will expand.
 747:      * 
 748:      * @param path the path to the node that will expand
 749:      */
 750:     public void fireTreeWillExpand(TreePath path) throws ExpandVetoException
 751:     {
 752:         TreeExpansionEvent event = new TreeExpansionEvent(this, path);
 753:         TreeWillExpandListener[] listeners = getTreeWillExpandListeners();
 754: 
 755:         for (int index = 0; index < listeners.length; ++index)
 756:             listeners[index].treeWillExpand(event);
 757:     }
 758: 
 759:     /**
 760:      * Returns the model of this <code>JTree</code> object.
 761:      * 
 762:      * @return the associated <code>TreeModel</code>
 763:      */
 764:     public TreeModel getModel()
 765:     {
 766:         return treeModel;
 767:     }
 768: 
 769:     /**
 770:      * Sets the model to use in <code>JTree</code>.
 771:      * 
 772:      * @param model the <code>TreeModel</code> to use
 773:      */
 774:     public void setModel(TreeModel model)
 775:     {
 776:         if (treeModel == model)
 777:             return;
 778: 
 779:         TreeModel oldValue = treeModel;
 780:         treeModel = model;
 781: 
 782:         firePropertyChange(TREE_MODEL_PROPERTY, oldValue, model);
 783: 
 784:         // add treeModelListener to the new model
 785:         if (treeModelListener == null)
 786:             treeModelListener = createTreeModelListener();
 787:         model.addTreeModelListener(treeModelListener);
 788:     }
 789: 
 790:     /**
 791:      * Checks if this <code>JTree</code> object is editable.
 792:      * 
 793:      * @return <code>true</code> if this tree object is editable,
 794:      *         <code>false</code> otherwise
 795:      */
 796:     public boolean isEditable()
 797:     {
 798:         return editable;
 799:     }
 800: 
 801:     /**
 802:      * Sets the <code>editable</code> property.
 803:      * 
 804:      * @param flag <code>true</code> to make this tree object editable,
 805:      *        <code>false</code> otherwise
 806:      */
 807:     public void setEditable(boolean flag)
 808:     {
 809:         if (editable == flag)
 810:             return;
 811: 
 812:         boolean oldValue = editable;
 813:         editable = flag;
 814:         firePropertyChange(EDITABLE_PROPERTY, oldValue, editable);
 815:     }
 816: 
 817:     /**
 818:      * Checks if the root element is visible.
 819:      * 
 820:      * @return <code>true</code> if the root element is visible,
 821:      *         <code>false</code> otherwise
 822:      */
 823:     public boolean isRootVisible()
 824:     {
 825:         return rootVisible;
 826:     }
 827: 
 828:     public void setRootVisible(boolean flag)
 829:     {
 830:         if (rootVisible == flag)
 831:             return;
 832: 
 833:         boolean oldValue = rootVisible;
 834:         rootVisible = flag;
 835:         firePropertyChange(ROOT_VISIBLE_PROPERTY, oldValue, flag);
 836:     }
 837: 
 838:     public boolean getShowsRootHandles()
 839:     {
 840:         return showsRootHandles;
 841:     }
 842: 
 843:     public void setShowsRootHandles(boolean flag)
 844:     {
 845:         if (showsRootHandles == flag)
 846:             return;
 847: 
 848:         boolean oldValue = showsRootHandles;
 849:         showsRootHandles = flag;
 850:         firePropertyChange(SHOWS_ROOT_HANDLES_PROPERTY, oldValue, flag);
 851:     }
 852: 
 853:     public TreeCellEditor getCellEditor()
 854:     {
 855: 
 856:         return cellEditor;
 857:     }
 858: 
 859:     public void setCellEditor(TreeCellEditor editor)
 860:     {
 861:         if (cellEditor == editor)
 862:             return;
 863: 
 864:         TreeCellEditor oldValue = cellEditor;
 865:         cellEditor = editor;
 866:         firePropertyChange(CELL_EDITOR_PROPERTY, oldValue, editor);
 867:     }
 868: 
 869:     public TreeCellRenderer getCellRenderer()
 870:     {
 871:         return cellRenderer;
 872:     }
 873: 
 874:     public void setCellRenderer(TreeCellRenderer newRenderer)
 875:     {
 876:         if (cellRenderer == newRenderer)
 877:             return;
 878: 
 879:         TreeCellRenderer oldValue = cellRenderer;
 880:         cellRenderer = newRenderer;
 881:         firePropertyChange(CELL_RENDERER_PROPERTY, oldValue, newRenderer);
 882:     }
 883: 
 884:     public TreeSelectionModel getSelectionModel()
 885:     {
 886:         return selectionModel;
 887:     }
 888: 
 889:     public void setSelectionModel(TreeSelectionModel model)
 890:     {
 891:         if (selectionModel == model)
 892:             return;
 893: 
 894:         if (selectionModel != null)
 895:             selectionModel.removeTreeSelectionListener(selectionRedirector);
 896: 
 897:         TreeSelectionModel oldValue = selectionModel;
 898:         selectionModel = model;
 899: 
 900:         if (selectionModel != null)
 901:             selectionModel.addTreeSelectionListener(selectionRedirector);
 902: 
 903:         firePropertyChange(SELECTION_MODEL_PROPERTY, oldValue, model);
 904:         revalidate();
 905:         repaint();
 906:     }
 907: 
 908:     public int getVisibleRowCount()
 909:     {
 910:         return visibleRowCount;
 911:     }
 912: 
 913:     public void setVisibleRowCount(int rows)
 914:     {
 915:         if (visibleRowCount == rows)
 916:             return;
 917: 
 918:         int oldValue = visibleRowCount;
 919:         visibleRowCount = rows;
 920:         firePropertyChange(VISIBLE_ROW_COUNT_PROPERTY, oldValue, rows);
 921:     }
 922: 
 923:     public boolean isLargeModel()
 924:     {
 925:         return largeModel;
 926:     }
 927: 
 928:     public void setLargeModel(boolean large)
 929:     {
 930:         if (largeModel == large)
 931:             return;
 932: 
 933:         boolean oldValue = largeModel;
 934:         largeModel = large;
 935:         firePropertyChange(LARGE_MODEL_PROPERTY, oldValue, large);
 936:     }
 937: 
 938:     public int getRowHeight()
 939:     {
 940: 
 941:         return rowHeight;
 942:     }
 943: 
 944:     public void setRowHeight(int height)
 945:     {
 946:         if (rowHeight == height)
 947:             return;
 948: 
 949:         int oldValue = rowHeight;
 950:         rowHeight = height;
 951:         firePropertyChange(ROW_HEIGHT_PROPERTY, oldValue, height);
 952:     }
 953: 
 954:     public boolean isFixedRowHeight()
 955:     {
 956:         return rowHeight > 0;
 957:     }
 958: 
 959:     public boolean getInvokesStopCellEditing()
 960:     {
 961:         return invokesStopCellEditing;
 962:     }
 963: 
 964:     public void setInvokesStopCellEditing(boolean invoke)
 965:     {
 966:         if (invokesStopCellEditing == invoke)
 967:             return;
 968: 
 969:         boolean oldValue = invokesStopCellEditing;
 970:         invokesStopCellEditing = invoke;
 971:         firePropertyChange(INVOKES_STOP_CELL_EDITING_PROPERTY, 
 972:                                                 oldValue, invoke);
 973:     }
 974: 
 975:     /**
 976:      * @since 1.3
 977:      */
 978:     public int getToggleClickCount()
 979:     {
 980:         return toggleClickCount;
 981:     }
 982: 
 983:     /**
 984:      * @since 1.3
 985:      */
 986:     public void setToggleClickCount(int count)
 987:     {
 988:         if (toggleClickCount == count)
 989:             return;
 990: 
 991:         int oldValue = toggleClickCount;
 992:         toggleClickCount = count;
 993:         firePropertyChange(TOGGLE_CLICK_COUNT_PROPERTY, oldValue, count);
 994:     }
 995: 
 996:     public void scrollPathToVisible(TreePath path)
 997:     {
 998:         if (path == null)
 999:             return;
1000: 
1001:         Rectangle rect = getPathBounds(path);
1002: 
1003:         if (rect == null)
1004:             return;
1005: 
1006:         scrollRectToVisible(rect);
1007:     }
1008: 
1009:     public void scrollRowToVisible(int row)
1010:     {
1011:         scrollPathToVisible(getPathForRow(row));
1012:     }
1013: 
1014:     public boolean getScrollsOnExpand()
1015:     {
1016:         return scrollsOnExpand;
1017:     }
1018: 
1019:     public void setScrollsOnExpand(boolean scroll)
1020:     {
1021:         if (scrollsOnExpand == scroll)
1022:             return;
1023: 
1024:         boolean oldValue = scrollsOnExpand;
1025:         scrollsOnExpand = scroll;
1026:         firePropertyChange(SCROLLS_ON_EXPAND_PROPERTY, oldValue, scroll);
1027:     }
1028: 
1029:     public void setSelectionPath(TreePath path)
1030:     {
1031:         selectionModel.setSelectionPath(path);
1032:     }
1033: 
1034:     public void setSelectionPaths(TreePath[] paths)
1035:     {
1036:         selectionModel.setSelectionPaths(paths);
1037:     }
1038: 
1039:     public void setSelectionRow(int row)
1040:     {
1041:         TreePath path = getPathForRow(row);
1042: 
1043:         if (path != null)
1044:             selectionModel.setSelectionPath(path);
1045:     }
1046: 
1047:     public void setSelectionRows(int[] rows)
1048:     {
1049:         // Make sure we have an UI so getPathForRow() does not return null.
1050:         if (rows == null || getUI() == null)
1051:             return;
1052: 
1053:         TreePath[] paths = new TreePath[rows.length];
1054: 
1055:         for (int i = rows.length - 1; i >= 0; --i)
1056:             paths[i] = getPathForRow(rows[i]);
1057: 
1058:         setSelectionPaths(paths);
1059:     }
1060: 
1061:     public void setSelectionInterval(int index0, int index1)
1062:     {
1063:         TreePath[] paths = getPathBetweenRows(index0, index1);
1064: 
1065:         if (paths != null)
1066:             setSelectionPaths(paths);
1067:     }
1068: 
1069:     public void addSelectionPath(TreePath path)
1070:     {
1071:         selectionModel.addSelectionPath(path);
1072:     }
1073: 
1074:     public void addSelectionPaths(TreePath[] paths)
1075:     {
1076:         selectionModel.addSelectionPaths(paths);
1077:     }
1078: 
1079:     public void addSelectionRow(int row)
1080:     {
1081:         TreePath path = getPathForRow(row);
1082: 
1083:         if (path != null)
1084:             selectionModel.addSelectionPath(path);
1085:     }
1086: 
1087:     public void addSelectionRows(int[] rows)
1088:     {
1089:         // Make sure we have an UI so getPathForRow() does not return null.
1090:         if (rows == null || getUI() == null)
1091:             return;
1092: 
1093:         TreePath[] paths = new TreePath[rows.length];
1094: 
1095:         for (int i = rows.length - 1; i >= 0; --i)
1096:             paths[i] = getPathForRow(rows[i]);
1097: 
1098:         addSelectionPaths(paths);
1099:     }
1100: 
1101:     public void addSelectionInterval(int index0, int index1)
1102:     {
1103:         TreePath[] paths = getPathBetweenRows(index0, index1);
1104: 
1105:         if (paths != null)
1106:             addSelectionPaths(paths);
1107:     }
1108: 
1109:     public void removeSelectionPath(TreePath path)
1110:     {
1111:         selectionModel.removeSelectionPath(path);
1112:     }
1113: 
1114:     public void removeSelectionPaths(TreePath[] paths)
1115:     {
1116:         selectionModel.removeSelectionPaths(paths);
1117:     }
1118: 
1119:     public void removeSelectionRow(int row)
1120:     {
1121:         TreePath path = getPathForRow(row);
1122: 
1123:         if (path != null)
1124:             selectionModel.removeSelectionPath(path);
1125:     }
1126: 
1127:     public void removeSelectionRows(int[] rows)
1128:     {
1129:         if (rows == null || getUI() == null)
1130:             return;
1131: 
1132:         TreePath[] paths = new TreePath[rows.length];
1133: 
1134:         for (int i = rows.length - 1; i >= 0; --i)
1135:             paths[i] = getPathForRow(rows[i]);
1136: 
1137:         removeSelectionPaths(paths);
1138:     }
1139: 
1140:     public void removeSelectionInterval(int index0, int index1)
1141:     {
1142:         TreePath[] paths = getPathBetweenRows(index0, index1);
1143: 
1144:         if (paths != null)
1145:             removeSelectionPaths(paths);
1146:     }
1147: 
1148:     public void clearSelection()
1149:     {
1150:         selectionModel.clearSelection();
1151:       setLeadSelectionPath(null);
1152:     }
1153: 
1154:     public TreePath getLeadSelectionPath()
1155:     {
1156:         return leadSelectionPath;
1157:     }
1158: 
1159:     /**
1160:      * @since 1.3
1161:      */
1162:     public void setLeadSelectionPath(TreePath path)
1163:     {
1164:         if (leadSelectionPath == path)
1165:             return;
1166: 
1167:         TreePath oldValue = leadSelectionPath;
1168:         leadSelectionPath = path;
1169:         firePropertyChange(LEAD_SELECTION_PATH_PROPERTY, oldValue, path);
1170:     }
1171: 
1172:     /**
1173:      * @since 1.3
1174:      */
1175:     public TreePath getAnchorSelectionPath()
1176:     {
1177:         return anchorSelectionPath;
1178:     }
1179: 
1180:     /**
1181:      * @since 1.3
1182:      */
1183:     public void setAnchorSelectionPath(TreePath path)
1184:     {
1185:         if (anchorSelectionPath == path)
1186:             return;
1187: 
1188:         TreePath oldValue = anchorSelectionPath;
1189:         anchorSelectionPath = path;
1190:         firePropertyChange(ANCHOR_SELECTION_PATH_PROPERTY, oldValue, path);
1191:     }
1192: 
1193:     public int getLeadSelectionRow()
1194:     {
1195:         return selectionModel.getLeadSelectionRow();
1196:     }
1197: 
1198:     public int getMaxSelectionRow()
1199:     {
1200:         return selectionModel.getMaxSelectionRow();
1201:     }
1202: 
1203:     public int getMinSelectionRow()
1204:     {
1205:         return selectionModel.getMinSelectionRow();
1206:     }
1207: 
1208:     public int getSelectionCount()
1209:     {
1210:         return selectionModel.getSelectionCount();
1211:     }
1212: 
1213:     public TreePath getSelectionPath()
1214:     {
1215:         return selectionModel.getSelectionPath();
1216:     }
1217: 
1218:     public TreePath[] getSelectionPaths()
1219:     {
1220:         return selectionModel.getSelectionPaths();
1221:     }
1222: 
1223:     public int[] getSelectionRows()
1224:     {
1225:         return selectionModel.getSelectionRows();
1226:     }
1227: 
1228:     public boolean isPathSelected(TreePath path)
1229:     {
1230:         return selectionModel.isPathSelected(path);
1231:     }
1232: 
1233:     public boolean isRowSelected(int row)
1234:     {
1235:         return selectionModel.isPathSelected(getPathForRow(row));
1236:     }
1237: 
1238:     public boolean isSelectionEmpty()
1239:     {
1240:         return selectionModel.isSelectionEmpty();
1241:     }
1242: 
1243:     /**
1244:      * Return the value of the <code>dragEnabled</code> property.
1245:      * 
1246:      * @return the value
1247:      * 
1248:      * @since 1.4
1249:      */
1250:     public boolean getDragEnabled()
1251:     {
1252:         return dragEnabled;
1253:     }
1254: 
1255:     /**
1256:      * Set the <code>dragEnabled</code> property.
1257:      * 
1258:      * @param enabled new value
1259:      * 
1260:      * @since 1.4
1261:      */
1262:     public void setDragEnabled(boolean enabled)
1263:     {
1264: 
1265:         dragEnabled = enabled;
1266:     }
1267: 
1268:     public int getRowCount()
1269:     {
1270:         TreeUI ui = getUI();
1271: 
1272:         if (ui != null)
1273:             return ui.getRowCount(this);
1274: 
1275:         return 0;
1276:     }
1277: 
1278:     public void collapsePath(TreePath path)
1279:     {
1280:         setExpandedState(path, false);
1281:     }
1282: 
1283:     public void collapseRow(int row)
1284:     {
1285:         if (row < 0 || row >= getRowCount())
1286:             return;
1287: 
1288:         TreePath path = getPathForRow(row);
1289: 
1290:         if (path != null)
1291:             collapsePath(path);
1292:     }
1293: 
1294:     public void expandPath(TreePath path)
1295:     {
1296:         // Don't expand if last path component is a leaf node.
1297:         if ((path == null) || (treeModel.isLeaf(path.getLastPathComponent())))
1298:             return;
1299:         
1300:         setExpandedState(path, true);
1301:     }
1302: 
1303:     public void expandRow(int row)
1304:     {
1305:         if (row < 0 || row >= getRowCount())
1306:             return;
1307: 
1308:         TreePath path = getPathForRow(row);
1309: 
1310:         if (path != null)
1311:             expandPath(path);
1312:     }
1313: 
1314:     public boolean isCollapsed(TreePath path)
1315:     {
1316:         return !isExpanded(path);
1317:     }
1318: 
1319:     public boolean isCollapsed(int row)
1320:     {
1321:         if (row < 0 || row >= getRowCount())
1322:             return false;
1323: 
1324:         TreePath path = getPathForRow(row);
1325: 
1326:         if (path != null)
1327:             return isCollapsed(path);
1328: 
1329:         return false;
1330:     }
1331: 
1332:     public boolean isExpanded(TreePath path)
1333:     {
1334:         if (path == null)
1335:             return false;
1336: 
1337:         Object state = nodeStates.get(path);
1338: 
1339:         if ((state == null) || (state != EXPANDED))
1340:             return false;
1341: 
1342:         TreePath parent = path.getParentPath();
1343: 
1344:         if (parent != null)
1345:             return isExpanded(parent);
1346: 
1347:         return true;
1348:     }
1349: 
1350:     public boolean isExpanded(int row)
1351:     {
1352:         if (row < 0 || row >= getRowCount())
1353:             return false;
1354: 
1355:         TreePath path = getPathForRow(row);
1356: 
1357:         if (path != null)
1358:             return isExpanded(path);
1359: 
1360:         return false;
1361:     }
1362: 
1363:     /**
1364:      * @since 1.3
1365:      */
1366:     public boolean getExpandsSelectedPaths()
1367:     {
1368:         return expandsSelectedPaths;
1369:     }
1370: 
1371:     /**
1372:      * @since 1.3
1373:      */
1374:     public void setExpandsSelectedPaths(boolean flag)
1375:     {
1376:         if (expandsSelectedPaths == flag)
1377:             return;
1378: 
1379:         boolean oldValue = expandsSelectedPaths;
1380:         expandsSelectedPaths = flag;
1381:         firePropertyChange(EXPANDS_SELECTED_PATHS_PROPERTY, oldValue, flag);
1382:     }
1383: 
1384:     public Rectangle getPathBounds(TreePath path)
1385:     {
1386:         TreeUI ui = getUI();
1387: 
1388:         if (ui == null)
1389:             return null;
1390: 
1391:         return ui.getPathBounds(this, path);
1392:     }
1393: 
1394:     public Rectangle getRowBounds(int row)
1395:     {
1396:         TreePath path = getPathForRow(row);
1397: 
1398:         if (path != null)
1399:             return getPathBounds(path);
1400: 
1401:         return null;
1402:     }
1403: 
1404:     public boolean isEditing()
1405:     {
1406:         TreeUI ui = getUI();
1407: 
1408:         if (ui != null)
1409:             return ui.isEditing(this);
1410: 
1411:         return false;
1412:     }
1413: 
1414:     public boolean stopEditing()
1415:     {
1416:         TreeUI ui = getUI();
1417: 
1418:         if (ui != null)
1419:             return ui.stopEditing(this);
1420: 
1421:         return false;
1422:     }
1423: 
1424:     public void cancelEditing()
1425:     {
1426:         TreeUI ui = getUI();
1427: 
1428:         if (ui != null)
1429:             ui.cancelEditing(this);
1430:     }
1431: 
1432:     public void startEditingAtPath(TreePath path)
1433:     {
1434:         TreeUI ui = getUI();
1435: 
1436:         if (ui != null)
1437:             ui.startEditingAtPath(this, path);
1438:     }
1439: 
1440:     public TreePath getEditingPath()
1441:     {
1442:         TreeUI ui = getUI();
1443: 
1444:         if (ui != null)
1445:             return ui.getEditingPath(this);
1446: 
1447:         return null;
1448:     }
1449: 
1450:     public TreePath getPathForLocation(int x, int y)
1451:     {
1452:         TreePath path = getClosestPathForLocation(x, y);
1453: 
1454:         if (path != null)
1455:         {
1456:             Rectangle rect = getPathBounds(path);
1457: 
1458:             if ((rect != null) && rect.contains(x, y))
1459:                 return path;
1460:         }
1461: 
1462:         return null;
1463:     }
1464: 
1465:     public int getRowForLocation(int x, int y)
1466:     {
1467:         TreePath path = getPathForLocation(x, y);
1468: 
1469:         if (path != null)
1470:             return getRowForPath(path);
1471: 
1472:         return -1;
1473:     }
1474: 
1475:     public TreePath getClosestPathForLocation(int x, int y)
1476:     {
1477:         TreeUI ui = getUI();
1478: 
1479:         if (ui != null)
1480:             return ui.getClosestPathForLocation(this, x, y);
1481: 
1482:         return null;
1483:     }
1484: 
1485:     public int getClosestRowForLocation(int x, int y)
1486:     {
1487:         TreePath path = getClosestPathForLocation(x, y);
1488: 
1489:         if (path != null)
1490:             return getRowForPath(path);
1491: 
1492:         return -1;
1493:     }
1494: 
1495:     public Object getLastSelectedPathComponent()
1496:     {
1497:         TreePath path = getSelectionPath();
1498: 
1499:         if (path != null)
1500:             return path.getLastPathComponent();
1501: 
1502:         return null;
1503:     }
1504: 
1505:     private void checkExpandParents(TreePath path) throws ExpandVetoException
1506:     {
1507: 
1508:         TreePath parent = path.getParentPath();
1509: 
1510:         if (parent != null)
1511:             checkExpandParents(parent);
1512: 
1513:         fireTreeWillExpand(path);
1514:     }
1515: 
1516:     private void doExpandParents(TreePath path, boolean state)
1517:     {
1518:         TreePath parent = path.getParentPath();
1519:         
1520:         if (isExpanded(parent))
1521:         {
1522:             nodeStates.put(path, state ? EXPANDED : COLLAPSED);
1523:             return;
1524:         }
1525: 
1526:         if (parent != null)
1527:             doExpandParents(parent, false);
1528: 
1529:         nodeStates.put(path, state ? EXPANDED : COLLAPSED);
1530:     }
1531: 
1532:     protected void setExpandedState(TreePath path, boolean state)
1533:     {
1534:         if (path == null)
1535:             return;
1536: 
1537:         TreePath parent = path.getParentPath();
1538: 
1539:         try
1540:         {
1541:             if (parent != null)
1542:                 checkExpandParents(parent);
1543:         } 
1544:         catch (ExpandVetoException e)
1545:         {
1546:             // Expansion vetoed.
1547:             return;
1548:         }
1549: 
1550:         doExpandParents(path, state);
1551:     }
1552: 
1553:     protected void clearToggledPaths()
1554:     {
1555:         nodeStates.clear();
1556:     }
1557: 
1558:     protected Enumeration getDescendantToggledPaths(TreePath parent)
1559:     {
1560:         if (parent == null)
1561:             return null;
1562: 
1563:         Enumeration nodes = nodeStates.keys();
1564:         Vector result = new Vector();
1565: 
1566:         while (nodes.hasMoreElements())
1567:         {
1568:             TreePath path = (TreePath) nodes.nextElement();
1569: 
1570:             if (path.isDescendant(parent))
1571:                 result.addElement(path);
1572:         }
1573: 
1574:         return result.elements();
1575:     }
1576: 
1577:     public boolean hasBeenExpanded(TreePath path)
1578:     {
1579:         if (path == null)
1580:             return false;
1581: 
1582:         return nodeStates.get(path) != null;
1583:     }
1584: 
1585:     public boolean isVisible(TreePath path)
1586:     {
1587:         if (path == null)
1588:             return false;
1589: 
1590:         TreePath parent = path.getParentPath();
1591: 
1592:         if (parent == null)
1593:             return true; // Is root node.
1594: 
1595:         return isExpanded(parent);
1596:     }
1597: 
1598:     public void makeVisible(TreePath path)
1599:     {
1600:         if (path == null)
1601:             return;
1602: 
1603:         expandPath(path.getParentPath());
1604:     }
1605: 
1606:     public boolean isPathEditable(TreePath path)
1607:     {
1608:         return isEditable();
1609:     }
1610: 
1611:     /**
1612:      * Creates and returns an instance of {@link TreeModelHandler}.
1613:      * 
1614:      * @returns an instance of {@link TreeModelHandler}
1615:      */
1616:     protected TreeModelListener createTreeModelListener()
1617:     {
1618:         return new TreeModelHandler();
1619:     }
1620: 
1621:     /**
1622:      * Returns a sample TreeModel that can be used in a JTree. This can be used
1623:      * in Bean- or GUI-Builders to show something interesting.
1624:      * 
1625:      * @return a sample TreeModel that can be used in a JTree
1626:      */
1627:     protected static TreeModel getDefaultTreeModel()
1628:     {
1629:         DefaultMutableTreeNode root = new DefaultMutableTreeNode("Root node");
1630:         DefaultMutableTreeNode child1 = new DefaultMutableTreeNode(
1631:                 "Child node 1");
1632:         DefaultMutableTreeNode child11 = new DefaultMutableTreeNode(
1633:                 "Child node 1.1");
1634:         DefaultMutableTreeNode child12 = new DefaultMutableTreeNode(
1635:                 "Child node 1.2");
1636:         DefaultMutableTreeNode child13 = new DefaultMutableTreeNode(
1637:                 "Child node 1.3");
1638:         DefaultMutableTreeNode child2 = new DefaultMutableTreeNode(
1639:                 "Child node 2");
1640:         DefaultMutableTreeNode child21 = new DefaultMutableTreeNode(
1641:                 "Child node 2.1");
1642:         DefaultMutableTreeNode child22 = new DefaultMutableTreeNode(
1643:                 "Child node 2.2");
1644:         DefaultMutableTreeNode child23 = new DefaultMutableTreeNode(
1645:                 "Child node 2.3");
1646:         DefaultMutableTreeNode child24 = new DefaultMutableTreeNode(
1647:                 "Child node 2.4");
1648: 
1649:         DefaultMutableTreeNode child3 = new DefaultMutableTreeNode(
1650:                 "Child node 3");
1651:         root.add(child1);
1652:         root.add(child2);
1653:         root.add(child3);
1654:         child1.add(child11);
1655:         child1.add(child12);
1656:         child1.add(child13);
1657:         child2.add(child21);
1658:         child2.add(child22);
1659:         child2.add(child23);
1660:         child2.add(child24);
1661:         return new DefaultTreeModel(root);
1662:     }
1663: 
1664:     /**
1665:      * Converts the specified value to a String. This is used by the renderers
1666:      * of this JTree and its nodes.
1667:      * 
1668:      * This implementation simply returns <code>value.toString()</code> and
1669:      * ignores all other parameters. Subclass this method to control the
1670:      * conversion.
1671:      * 
1672:      * @param value the value that is converted to a String
1673:      * @param selected indicates if that value is selected or not
1674:      * @param expanded indicates if that value is expanded or not
1675:      * @param leaf indicates if that value is a leaf node or not
1676:      * @param row the row of the node
1677:      * @param hasFocus indicates if that node has focus or not
1678:      */
1679:     public String convertValueToText(Object value, boolean selected,
1680:             boolean expanded, boolean leaf, int row, boolean hasFocus)
1681:     {
1682:         return value.toString();
1683:     }
1684: 
1685:     /**
1686:      * A String representation of this JTree. This is intended to be used for
1687:      * debugging. The returned string may be empty but may not be
1688:      * <code>null</code>.
1689:      * 
1690:      * @return a String representation of this JTree
1691:      */
1692:     public String paramString()
1693:     {
1694:         // TODO: this is completely legal, but it would possibly be nice
1695:         // to return some more content, like the tree structure, some properties
1696:         // etc ...
1697:         return "";
1698:     }
1699: 
1700:     /**
1701:      * Returns all TreePath objects which are a descendants of the given path
1702:      * and are exapanded at the moment of the execution of this method. If the
1703:      * state of any node is beeing toggled while this method is executing this
1704:      * change may be left unaccounted.
1705:      * 
1706:      * @param path The parent of this request
1707:      * @return An Enumeration containing TreePath objects
1708:      */
1709:     public Enumeration getExpandedDescendants(TreePath path)
1710:     {
1711:         Enumeration paths = nodeStates.keys();
1712:         Vector relevantPaths = new Vector();
1713:         while (paths.hasMoreElements())
1714:         {
1715:             TreePath nextPath = (TreePath) paths.nextElement();
1716:             if (nodeStates.get(nextPath) == EXPANDED
1717:                     && path.isDescendant(nextPath))
1718:             {
1719:                 relevantPaths.add(nextPath);
1720:             }
1721:         }
1722:         return relevantPaths.elements();
1723:     }
1724: 
1725:     /**
1726:      * Returns the next table element (beginning from the row
1727:      * <code>startingRow</code> that starts with <code>prefix</code>.
1728:      * Searching is done in the direction specified by <code>bias</code>.
1729:      * 
1730:      * @param prefix the prefix to search for in the cell values
1731:      * @param startingRow the index of the row where to start searching from
1732:      * @param bias the search direction, either {@link Position.Bias.Forward} or
1733:      *        {@link Position.Bias.Backward}
1734:      * 
1735:      * @return the path to the found element or -1 if no such element has been
1736:      *         found
1737:      * 
1738:      * @throws IllegalArgumentException if prefix is <code>null</code> or
1739:      *         startingRow is not valid
1740:      * 
1741:      * @since 1.4
1742:      */
1743:     public TreePath getNextMatch(String prefix, int startingRow,
1744:             Position.Bias bias)
1745:     {
1746:         if (prefix == null)
1747:             throw new IllegalArgumentException(
1748:                     "The argument 'prefix' must not be" + " null.");
1749:         if (startingRow < 0)
1750:             throw new IllegalArgumentException(
1751:                     "The argument 'startingRow' must not"
1752:                             + " be less than zero.");
1753: 
1754:         int size = getRowCount();
1755:         if (startingRow > size)
1756:             throw new IllegalArgumentException(
1757:                     "The argument 'startingRow' must not"
1758:                             + " be greater than the number of"
1759:                             + " elements in the TreeModel.");
1760: 
1761:         TreePath foundPath = null;
1762:         if (bias == Position.Bias.Forward)
1763:         {
1764:             for (int i = startingRow; i < size; i++)
1765:             {
1766:                 TreePath path = getPathForRow(i);
1767:                 Object o = path.getLastPathComponent();
1768:                 // FIXME: in the following call to convertValueToText the
1769:                 // last argument (hasFocus) should be done right.
1770:                 String item = convertValueToText(o, isRowSelected(i),
1771:                         isExpanded(i), treeModel.isLeaf(o), i, false);
1772:                 if (item.startsWith(prefix))
1773:                 {
1774:                     foundPath = path;
1775:                     break;
1776:                 }
1777:             }
1778:         } else
1779:         {
1780:             for (int i = startingRow; i >= 0; i--)
1781:             {
1782:                 TreePath path = getPathForRow(i);
1783:                 Object o = path.getLastPathComponent();
1784:                 // FIXME: in the following call to convertValueToText the
1785:                 // last argument (hasFocus) should be done right.
1786:                 String item = convertValueToText(o, isRowSelected(i),
1787:                         isExpanded(i), treeModel.isLeaf(o), i, false);
1788:                 if (item.startsWith(prefix))
1789:                 {
1790:                     foundPath = path;
1791:                     break;
1792:                 }
1793:             }
1794:         }
1795:         return foundPath;
1796:     }
1797: 
1798:     /**
1799:      * Removes any paths in the current set of selected paths that are
1800:      * descendants of <code>path</code>. If <code>includePath</code> is set
1801:      * to <code>true</code> and <code>path</code> itself is selected, then
1802:      * it will be removed too.
1803:      * 
1804:      * @param path the path from which selected descendants are to be removed
1805:      * @param includePath if <code>true</code> then <code>path</code> itself
1806:      *        will also be remove if it's selected
1807:      * 
1808:      * @return <code>true</code> if something has been removed,
1809:      *         <code>false</code> otherwise
1810:      * 
1811:      * @since 1.3
1812:      */
1813:     protected boolean removeDescendantSelectedPaths(TreePath path,
1814:             boolean includeSelected)
1815:     {
1816:         boolean removedSomething = false;
1817:         TreePath[] selected = getSelectionPaths();
1818:         for (int index = 0; index < selected.length; index++)
1819:         {
1820:             if ((selected[index] == path && includeSelected)
1821:                     || (selected[index].isDescendant(path)))
1822:             {
1823:                 removeSelectionPath(selected[index]);
1824:                 removedSomething = true;
1825:             }
1826:         }
1827:         return removedSomething;
1828:     }
1829: }