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